r4722: Start to add 'net join' to Samba4.
authorAndrew Bartlett <abartlet@samba.org>
Thu, 13 Jan 2005 07:50:09 +0000 (07:50 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 18:08:48 +0000 (13:08 -0500)
Andrew Bartlett
(This used to be commit a9b960609142e15ba5950eb1b22944eb6df18d9c)

source4/libnet/config.mk
source4/libnet/libnet.h
source4/libnet/libnet_join.c [new file with mode: 0644]
source4/libnet/libnet_join.h [new file with mode: 0644]
source4/libnet/libnet_passwd.c
source4/utils/net/config.mk
source4/utils/net/net.c
source4/utils/net/net_join.c [new file with mode: 0644]

index 969a2bbd3412cd583faea0db26caeed337fa49a8..8d95cdd8e284cfb4d85a77d751107cfc398e76b4 100644 (file)
@@ -6,7 +6,8 @@ INIT_OBJ_FILES = \
 ADD_OBJ_FILES = \
                libnet/libnet_passwd.o \
                libnet/libnet_time.o \
-               libnet/libnet_rpc.o
+               libnet/libnet_rpc.o \
+               libnet/libnet_join.o
 REQUIRED_SUBSYSTEMS = RPC_NDR_SAMR RPC_NDR_SRVSVC
 # End SUBSYSTEM LIBNET
 #################################
index 55681137475e123bd2a80c4e08dbbed49e519903..0939c20a9f31d37fb6ddf946639d93a5a5da29b1 100644 (file)
@@ -35,3 +35,4 @@ struct libnet_context {
 #include "libnet/libnet_passwd.h"
 #include "libnet/libnet_time.h"
 #include "libnet/libnet_rpc.h"
+#include "libnet/libnet_join.h"
diff --git a/source4/libnet/libnet_join.c b/source4/libnet/libnet_join.c
new file mode 100644 (file)
index 0000000..871e5c5
--- /dev/null
@@ -0,0 +1,279 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Stefan Metzmacher     2004
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+#include "lib/crypto/crypto.h"
+
+/*
+ * do a domain join using DCERPC/SAMR calls
+ * 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation)
+ *    is it correct to contact the the pdc of the domain of the user who's password should be set?
+ * 2. do a samr_Connect to get a policy handle
+ * 3. do a samr_LookupDomain to get the domain sid
+ * 4. do a samr_OpenDomain to get a domain handle
+ * 5. do a samr_CreateAccount to try and get a new account 
+ * 
+ * If that fails, do:
+ * 5.1. do a samr_LookupNames to get the users rid
+ * 5.2. do a samr_OpenUser to get a user handle
+ * 
+ * 6. call libnet_SetPassword_samr_handle to set the password
+ *
+ * 7. do a samrSetUserInfo to set the account flags
+ */
+static NTSTATUS libnet_JoinDomain_samr(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
+{
+       NTSTATUS status;
+       union libnet_rpc_connect c;
+       struct samr_Connect sc;
+       struct policy_handle p_handle;
+       struct samr_LookupDomain ld;
+       struct samr_String d_name;
+       struct samr_OpenDomain od;
+       struct policy_handle d_handle;
+       struct samr_LookupNames ln;
+       struct samr_OpenUser ou;
+       struct samr_CreateUser2 cu;
+       struct policy_handle u_handle;
+       struct samr_SetUserInfo sui;
+       union samr_UserInfo u_info;
+       union libnet_SetPassword r2;
+       struct samr_GetUserPwInfo pwp;
+       struct samr_String samr_account_name;
+
+       uint32 rid, access_granted;
+       int policy_min_pw_len = 0;
+
+       /* prepare connect to the SAMR pipe of users domain PDC */
+       c.pdc.level                     = LIBNET_RPC_CONNECT_PDC;
+       c.pdc.in.domain_name            = r->samr.in.domain_name;
+       c.pdc.in.dcerpc_iface_name      = DCERPC_SAMR_NAME;
+       c.pdc.in.dcerpc_iface_uuid      = DCERPC_SAMR_UUID;
+       c.pdc.in.dcerpc_iface_version   = DCERPC_SAMR_VERSION;
+
+       /* 1. connect to the SAMR pipe of users domain PDC (maybe a standalone server or workstation) */
+       status = libnet_rpc_connect(ctx, mem_ctx, &c);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "Connection to SAMR pipe of PDC of domain '%s' failed: %s\n",
+                                               r->samr.in.domain_name, nt_errstr(status));
+               return status;
+       }
+
+       /* prepare samr_Connect */
+       ZERO_STRUCT(p_handle);
+       sc.in.system_name = NULL;
+       sc.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       sc.out.connect_handle = &p_handle;
+
+       /* 2. do a samr_Connect to get a policy handle */
+       status = dcerpc_samr_Connect(c.pdc.out.dcerpc_pipe, mem_ctx, &sc);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_Connect failed: %s\n",
+                                               nt_errstr(status));
+               goto disconnect;
+       }
+
+       /* check result of samr_Connect */
+       if (!NT_STATUS_IS_OK(sc.out.result)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_Connect failed: %s\n", 
+                                               nt_errstr(sc.out.result));
+               status = sc.out.result;
+               goto disconnect;
+       }
+
+       /* prepare samr_LookupDomain */
+       d_name.string = r->samr.in.domain_name;
+       ld.in.connect_handle = &p_handle;
+       ld.in.domain = &d_name;
+
+       /* 3. do a samr_LookupDomain to get the domain sid */
+       status = dcerpc_samr_LookupDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &ld);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_LookupDomain for [%s] failed: %s\n",
+                                               r->samr.in.domain_name, nt_errstr(status));
+               goto disconnect;
+       }
+
+       /* check result of samr_LookupDomain */
+       if (!NT_STATUS_IS_OK(ld.out.result)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_LookupDomain for [%s] failed: %s\n",
+                                               r->samr.in.domain_name, nt_errstr(ld.out.result));
+               status = ld.out.result;
+               goto disconnect;
+       }
+
+       /* prepare samr_OpenDomain */
+       ZERO_STRUCT(d_handle);
+       od.in.connect_handle = &p_handle;
+       od.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+       od.in.sid = ld.out.sid;
+       od.out.domain_handle = &d_handle;
+
+       /* 4. do a samr_OpenDomain to get a domain handle */
+       status = dcerpc_samr_OpenDomain(c.pdc.out.dcerpc_pipe, mem_ctx, &od);
+       if (!NT_STATUS_IS_OK(status)) {
+               r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                               "samr_OpenDomain for [%s] failed: %s\n",
+                                               r->samr.in.domain_name, nt_errstr(status));
+               goto disconnect;
+       }
+
+       /* prepare samr_CreateUser2 */
+       ZERO_STRUCT(u_handle);
+       cu.in.domain_handle  = &d_handle;
+       cu.in.access_mask     = SEC_FLAG_MAXIMUM_ALLOWED;
+       samr_account_name.string = r->samr.in.account_name;
+       cu.in.account_name    = &samr_account_name;
+       cu.in.acct_flags      = r->samr.in.acct_type;
+       cu.out.user_handle    = &u_handle;
+       cu.out.rid            = &rid;
+       cu.out.access_granted = &access_granted;
+
+       /* 4. do a samr_CreateUser2 to get an account handle, or an error */
+       status = dcerpc_samr_CreateUser2(c.pdc.out.dcerpc_pipe, mem_ctx, &cu);
+       if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                                                  "samr_CreateUser2 for [%s] failed: %s\n",
+                                                                  r->samr.in.domain_name, nt_errstr(status));
+                       goto disconnect;
+
+       } else if (NT_STATUS_EQUAL(status, NT_STATUS_USER_EXISTS)) {
+               /* prepare samr_LookupNames */
+               ln.in.domain_handle = &d_handle;
+               ln.in.num_names = 1;
+               ln.in.names = talloc_array_p(mem_ctx, struct samr_String, 1);
+               if (!ln.in.names) {
+                       r->samr.out.error_string = "Out of Memory";
+                       return NT_STATUS_NO_MEMORY;
+               }
+               ln.in.names[0].string = r->samr.in.account_name;
+               
+               /* 5. do a samr_LookupNames to get the users rid */
+               status = dcerpc_samr_LookupNames(c.pdc.out.dcerpc_pipe, mem_ctx, &ln);
+               if (!NT_STATUS_IS_OK(status)) {
+                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                                                  "samr_LookupNames for [%s] failed: %s\n",
+                                               r->samr.in.account_name, nt_errstr(status));
+                       goto disconnect;
+               }
+               
+               
+               /* check if we got one RID for the user */
+               if (ln.out.rids.count != 1) {
+                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                                                  "samr_LookupNames for [%s] returns %d RIDs\n",
+                                                                  r->samr.in.account_name, ln.out.rids.count);
+                       status = NT_STATUS_INVALID_PARAMETER;
+                       goto disconnect;        
+               }
+               
+               /* prepare samr_OpenUser */
+               ZERO_STRUCT(u_handle);
+               ou.in.domain_handle = &d_handle;
+               ou.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
+               ou.in.rid = ln.out.rids.ids[0];
+               ou.out.user_handle = &u_handle;
+               
+               /* 6. do a samr_OpenUser to get a user handle */
+               status = dcerpc_samr_OpenUser(c.pdc.out.dcerpc_pipe, mem_ctx, &ou);
+               if (!NT_STATUS_IS_OK(status)) {
+                       r->samr.out.error_string = talloc_asprintf(mem_ctx,
+                                                                  "samr_OpenUser for [%s] failed: %s\n",
+                                                                  r->samr.in.account_name, nt_errstr(status));
+                       goto disconnect;
+               }
+       }
+
+       pwp.in.user_handle = &u_handle;
+
+       status = dcerpc_samr_GetUserPwInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &pwp);
+       if (NT_STATUS_IS_OK(status)) {
+               policy_min_pw_len = pwp.out.info.min_password_length;
+       }
+
+       r->samr.out.join_password = generate_random_str(mem_ctx, MAX(8, policy_min_pw_len));
+
+       r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
+       r2.samr_handle.in.account_name  = r->samr.in.account_name;
+       r2.samr_handle.in.newpassword   = r->samr.out.join_password;
+       r2.samr_handle.in.user_handle   = &u_handle;
+       r2.samr_handle.in.dcerpc_pipe   = c.pdc.out.dcerpc_pipe;
+
+       status = libnet_SetPassword(ctx, mem_ctx, &r2);
+
+       r->samr.out.error_string = r2.samr_handle.out.error_string;
+
+       if (!NT_STATUS_IS_OK(status)) {
+               goto disconnect;
+       }
+
+       /* prepare samr_SetUserInfo level 23 */
+       ZERO_STRUCT(u_info);
+       u_info.info16.acct_flags = r->samr.in.acct_type;
+
+       sui.in.user_handle = &u_handle;
+       sui.in.info = &u_info;
+       sui.in.level = 16;
+       
+       dcerpc_samr_SetUserInfo(c.pdc.out.dcerpc_pipe, mem_ctx, &sui);
+
+disconnect:
+       /* close connection */
+       dcerpc_pipe_close(c.pdc.out.dcerpc_pipe);
+
+       return status;
+}
+
+static NTSTATUS libnet_JoinDomain_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
+{
+       NTSTATUS status;
+       union libnet_JoinDomain r2;
+
+       r2.samr.level           = LIBNET_JOIN_DOMAIN_SAMR;
+       r2.samr.in.account_name = r->generic.in.account_name;
+       r2.samr.in.domain_name  = r->generic.in.domain_name;
+       r2.samr.in.acct_type    = r->generic.in.acct_type;
+
+       status = libnet_JoinDomain(ctx, mem_ctx, &r2);
+
+       r->generic.out.error_string = r2.samr.out.error_string;
+
+       return status;
+}
+
+NTSTATUS libnet_JoinDomain(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_JoinDomain *r)
+{
+       switch (r->generic.level) {
+               case LIBNET_JOIN_DOMAIN_GENERIC:
+                       return libnet_JoinDomain_generic(ctx, mem_ctx, r);
+               case LIBNET_JOIN_DOMAIN_SAMR:
+                       return libnet_JoinDomain_samr(ctx, mem_ctx, r);
+       }
+
+       return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/libnet/libnet_join.h b/source4/libnet/libnet_join.h
new file mode 100644 (file)
index 0000000..8788016
--- /dev/null
@@ -0,0 +1,51 @@
+/* 
+   Unix SMB/CIFS implementation.
+   
+   Copyright (C) Stefan Metzmacher     2004
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
+   
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* struct and enum for doing a remote domain join */
+enum libnet_JoinDomain_level {
+       LIBNET_JOIN_DOMAIN_GENERIC,
+       LIBNET_JOIN_DOMAIN_SAMR,
+};
+
+union libnet_JoinDomain {
+       struct {
+               enum libnet_JoinDomain_level level;
+
+               struct _libnet_JoinDomain_in {
+                       const char *domain_name;
+                       const char *account_name;
+                       uint32      acct_type;
+               } in;
+
+               struct _libnet_JoinDomain_out {
+                       const char *error_string;
+                       const char *join_password;
+               } out;
+       } generic;
+
+       struct {
+               enum libnet_JoinDomain_level level;
+               struct _libnet_JoinDomain_in in;
+               struct _libnet_JoinDomain_out out;
+       } samr;
+
+};
+
index c36c478733eb424a11ceb92c5ec333831e32880c..20be3a9dce65e078410e4b1272241c8450819d66 100644 (file)
@@ -2,6 +2,7 @@
    Unix SMB/CIFS implementation.
    
    Copyright (C) Stefan Metzmacher     2004
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2005
    
    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
@@ -544,15 +545,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
                goto disconnect;
        }
 
-       /* check result of samr_Connect */
-       if (!NT_STATUS_IS_OK(sc.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_Connect failed: %s\n", 
-                                               nt_errstr(sc.out.result));
-               status = sc.out.result;
-               goto disconnect;
-       }
-
        /* prepare samr_LookupDomain */
        d_name.string = r->samr.in.domain_name;
        ld.in.connect_handle = &p_handle;
@@ -567,15 +559,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
                goto disconnect;
        }
 
-       /* check result of samr_LookupDomain */
-       if (!NT_STATUS_IS_OK(ld.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_LookupDomain for [%s] failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(ld.out.result));
-               status = ld.out.result;
-               goto disconnect;
-       }
-
        /* prepare samr_OpenDomain */
        ZERO_STRUCT(d_handle);
        od.in.connect_handle = &p_handle;
@@ -592,15 +575,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
                goto disconnect;
        }
 
-       /* check result of samr_OpenDomain */
-       if (!NT_STATUS_IS_OK(od.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_OpenDomain for [%s] failed: %s\n",
-                                               r->samr.in.domain_name, nt_errstr(od.out.result));
-               status = od.out.result;
-               goto disconnect;
-       }
-
        /* prepare samr_LookupNames */
        ln.in.domain_handle = &d_handle;
        ln.in.num_names = 1;
@@ -620,15 +594,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
                goto disconnect;
        }
 
-       /* check result of samr_LookupNames */
-       if (!NT_STATUS_IS_OK(ln.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_LookupNames for [%s] failed: %s\n",
-                                               r->samr.in.account_name, nt_errstr(ln.out.result));
-               status = ln.out.result;
-               goto disconnect;
-}
-
        /* check if we got one RID for the user */
        if (ln.out.rids.count != 1) {
                r->samr.out.error_string = talloc_asprintf(mem_ctx,
@@ -654,15 +619,6 @@ static NTSTATUS libnet_SetPassword_samr(struct libnet_context *ctx, TALLOC_CTX *
                goto disconnect;
        }
 
-       /* check result of samr_OpenUser */
-       if (!NT_STATUS_IS_OK(ou.out.result)) {
-               r->samr.out.error_string = talloc_asprintf(mem_ctx,
-                                               "samr_OpenUser for [%s] failed: %s\n",
-                                               r->samr.in.account_name, nt_errstr(ou.out.result));
-               status = ou.out.result;
-               goto disconnect;
-       }
-
        r2.samr_handle.level            = LIBNET_SET_PASSWORD_SAMR_HANDLE;
        r2.samr_handle.in.account_name  = r->samr.in.account_name;
        r2.samr_handle.in.newpassword   = r->samr.in.newpassword;
index f1f6646e9c304ae488ffe512b894283b0d8e73e5..e1bc813becfb3dec8e7393ea8885f173a31f75ba 100644 (file)
@@ -6,7 +6,8 @@
 OBJ_FILES = \
                utils/net/net.o \
                utils/net/net_password.o \
-               utils/net/net_time.o
+               utils/net/net_time.o \
+               utils/net/net_join.o
 REQUIRED_SUBSYSTEMS = \
                CONFIG \
                LIBCMDLINE \
index 022bb9225ec2279061f1c5edd43cf18d550266d0..350ec251d9e9b8d90eb9b3d25cd6d3f60af51d15 100644 (file)
@@ -139,7 +139,7 @@ static int net_help_usage(struct net_context *ctx, int argc, const char **argv)
 static const struct net_functable net_functable[] = {
        {"password", net_password, net_password_usage, net_password_help},
        {"time", net_time, net_time_usage, net_time_help},
-
+       {"join", net_join, net_join_usage, net_join_help},
        {"help", net_help, net_help_usage, net_help},
        {NULL, NULL}
 };
diff --git a/source4/utils/net/net_join.c b/source4/utils/net/net_join.c
new file mode 100644 (file)
index 0000000..81e795a
--- /dev/null
@@ -0,0 +1,108 @@
+/* 
+   Samba Unix/Linux SMB client library 
+   Distributed SMB/CIFS Server Management Utility 
+
+   Copyright (C) 2004 Stefan Metzmacher (metze@samba.org)
+
+   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 2 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, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include "includes.h"
+#include "utils/net/net.h"
+#include "libnet/libnet.h"
+#include "librpc/gen_ndr/ndr_samr.h"
+
+static int net_join_domain(struct net_context *ctx, int argc, const char **argv)
+{
+       NTSTATUS status;
+       struct libnet_context *libnetctx;
+       union libnet_JoinDomain r;
+       char *tmp;
+       const char *domain_name;
+
+       switch (argc) {
+               case 0: /* no args -> fail */
+                       DEBUG(0,("net_join_domain: no args\n"));
+                       return -1;
+               case 1: /* only DOMAIN */
+                       tmp = talloc_strdup(ctx->mem_ctx, argv[0]);
+                       break;
+               default: /* too mayn args -> fail */
+                       DEBUG(0,("net_join_domain: too many args [%d]\n",argc));
+                       return -1;
+       }
+
+       domain_name = tmp;
+
+       libnetctx = libnet_context_init();
+       if (!libnetctx) {
+               return -1;      
+       }
+       libnetctx->user.account_name    = ctx->user.account_name;
+       libnetctx->user.domain_name     = ctx->user.domain_name;
+       libnetctx->user.password        = ctx->user.password;
+
+       /* prepare password change */
+       r.generic.level                 = LIBNET_JOIN_DOMAIN_GENERIC;
+       r.generic.in.domain_name        = domain_name;
+       r.generic.in.account_name       = talloc_asprintf(ctx->mem_ctx, "%s$", lp_netbios_name());
+       r.generic.in.acct_type          = ACB_SVRTRUST;
+
+       /* do the domain join */
+       status = libnet_JoinDomain(libnetctx, ctx->mem_ctx, &r);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,("net_join_domain: %s\n",r.generic.out.error_string));
+               return -1;
+       }
+
+       libnet_context_destroy(&libnetctx);
+
+       return 0;
+}
+
+static int net_join_domain_usage(struct net_context *ctx, int argc, const char **argv)
+{
+       d_printf("net_join_domain_usage: TODO\n");
+       return 0;       
+}
+
+static int net_join_domain_help(struct net_context *ctx, int argc, const char **argv)
+{
+       d_printf("net_join_domain_help: TODO\n");
+       return 0;       
+}
+
+static const struct net_functable net_password_functable[] = {
+       {"domain", net_join_domain, net_join_domain_usage,  net_join_domain_help},
+       {NULL, NULL}
+};
+
+int net_join(struct net_context *ctx, int argc, const char **argv) 
+{
+       
+       return net_run_function(ctx, argc, argv, net_password_functable, net_password_usage);
+}
+
+int net_join_usage(struct net_context *ctx, int argc, const char **argv)
+{
+       d_printf("net_password_usage: TODO\n");
+       return 0;       
+}
+
+int net_join_help(struct net_context *ctx, int argc, const char **argv)
+{
+       d_printf("net_password_help: TODO\n");
+       return 0;       
+}