summaryrefslogtreecommitdiffstats
path: root/source3/utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/utils
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source3/utils')
-rw-r--r--source3/utils/async-tracker.c315
-rw-r--r--source3/utils/clirap2.c2552
-rw-r--r--source3/utils/clirap2.h80
-rw-r--r--source3/utils/conn_tdb.c173
-rw-r--r--source3/utils/conn_tdb.h45
-rw-r--r--source3/utils/dbwrap_tool.c593
-rw-r--r--source3/utils/dbwrap_torture.c366
-rw-r--r--source3/utils/destroy_netlogon_creds_cli.c136
-rw-r--r--source3/utils/eventlogadm.c506
-rw-r--r--source3/utils/interact.c135
-rw-r--r--source3/utils/interact.h36
-rw-r--r--source3/utils/log2pcaphex.c408
-rw-r--r--source3/utils/mdsearch.c260
-rw-r--r--source3/utils/mvxattr.c227
-rw-r--r--source3/utils/net.c1450
-rw-r--r--source3/utils/net.h208
-rw-r--r--source3/utils/net_ads.c4184
-rw-r--r--source3/utils/net_ads_gpo.c428
-rw-r--r--source3/utils/net_ads_join_dns.c342
-rw-r--r--source3/utils/net_afs.c127
-rw-r--r--source3/utils/net_afs.h29
-rw-r--r--source3/utils/net_cache.c652
-rw-r--r--source3/utils/net_conf.c1305
-rw-r--r--source3/utils/net_conf_util.c69
-rw-r--r--source3/utils/net_conf_util.h33
-rw-r--r--source3/utils/net_dns.c224
-rw-r--r--source3/utils/net_dns.h44
-rw-r--r--source3/utils/net_dom.c385
-rw-r--r--source3/utils/net_eventlog.c275
-rw-r--r--source3/utils/net_file.c57
-rw-r--r--source3/utils/net_g_lock.c267
-rw-r--r--source3/utils/net_group.c68
-rw-r--r--source3/utils/net_groupmap.c1023
-rw-r--r--source3/utils/net_help.c69
-rw-r--r--source3/utils/net_help_common.c95
-rw-r--r--source3/utils/net_help_common.h49
-rw-r--r--source3/utils/net_idmap.c1413
-rw-r--r--source3/utils/net_idmap_check.c974
-rw-r--r--source3/utils/net_idmap_check.h48
-rw-r--r--source3/utils/net_join.c55
-rw-r--r--source3/utils/net_lookup.c542
-rw-r--r--source3/utils/net_notify.c199
-rw-r--r--source3/utils/net_offlinejoin.c600
-rw-r--r--source3/utils/net_printing.c592
-rw-r--r--source3/utils/net_proto.h488
-rw-r--r--source3/utils/net_rap.c1386
-rw-r--r--source3/utils/net_registry.c1732
-rw-r--r--source3/utils/net_registry_check.c1324
-rw-r--r--source3/utils/net_registry_check.h52
-rw-r--r--source3/utils/net_registry_util.c177
-rw-r--r--source3/utils/net_registry_util.h41
-rw-r--r--source3/utils/net_rpc.c8408
-rw-r--r--source3/utils/net_rpc_audit.c540
-rw-r--r--source3/utils/net_rpc_conf.c2483
-rw-r--r--source3/utils/net_rpc_printer.c2624
-rw-r--r--source3/utils/net_rpc_registry.c2126
-rw-r--r--source3/utils/net_rpc_rights.c798
-rw-r--r--source3/utils/net_rpc_samsync.c257
-rw-r--r--source3/utils/net_rpc_service.c1138
-rw-r--r--source3/utils/net_rpc_sh_acct.c489
-rw-r--r--source3/utils/net_rpc_shell.c306
-rw-r--r--source3/utils/net_rpc_trust.c735
-rw-r--r--source3/utils/net_sam.c2308
-rw-r--r--source3/utils/net_serverid.c703
-rw-r--r--source3/utils/net_share.c75
-rw-r--r--source3/utils/net_status.c246
-rw-r--r--source3/utils/net_tdb.c105
-rw-r--r--source3/utils/net_time.c262
-rw-r--r--source3/utils/net_user.c67
-rw-r--r--source3/utils/net_usershare.c1172
-rw-r--r--source3/utils/net_util.c614
-rw-r--r--source3/utils/net_vfs.c469
-rw-r--r--source3/utils/net_witness.c2361
-rw-r--r--source3/utils/netlookup.c218
-rw-r--r--source3/utils/nmblookup.c468
-rw-r--r--source3/utils/ntlm_auth.c2856
-rw-r--r--source3/utils/ntlm_auth.h26
-rw-r--r--source3/utils/ntlm_auth_diagnostics.c724
-rw-r--r--source3/utils/ntlm_auth_proto.h51
-rw-r--r--source3/utils/passwd_proto.h31
-rw-r--r--source3/utils/passwd_util.c80
-rw-r--r--source3/utils/pdbedit.c1414
-rw-r--r--source3/utils/profiles.c365
-rw-r--r--source3/utils/py_net.c369
-rw-r--r--source3/utils/py_net.h26
-rw-r--r--source3/utils/regedit.c835
-rw-r--r--source3/utils/regedit.h77
-rw-r--r--source3/utils/regedit_dialog.c2328
-rw-r--r--source3/utils/regedit_dialog.h240
-rw-r--r--source3/utils/regedit_hexedit.c563
-rw-r--r--source3/utils/regedit_hexedit.h49
-rw-r--r--source3/utils/regedit_list.c591
-rw-r--r--source3/utils/regedit_list.h82
-rw-r--r--source3/utils/regedit_samba3.c244
-rw-r--r--source3/utils/regedit_treeview.c705
-rw-r--r--source3/utils/regedit_treeview.h89
-rw-r--r--source3/utils/regedit_valuelist.c496
-rw-r--r--source3/utils/regedit_valuelist.h72
-rw-r--r--source3/utils/regedit_wrap.c143
-rw-r--r--source3/utils/sharesec.c613
-rw-r--r--source3/utils/smbcacls.c2614
-rw-r--r--source3/utils/smbcontrol.c1870
-rw-r--r--source3/utils/smbcquotas.c832
-rw-r--r--source3/utils/smbfilter.c356
-rw-r--r--source3/utils/smbget.c1073
-rw-r--r--source3/utils/smbpasswd.c678
-rw-r--r--source3/utils/smbtree.c295
-rw-r--r--source3/utils/status.c1241
-rw-r--r--source3/utils/status.h44
-rw-r--r--source3/utils/status_json.c1386
-rw-r--r--source3/utils/status_json.h77
-rw-r--r--source3/utils/status_json_dummy.c101
-rw-r--r--source3/utils/status_profile.c381
-rw-r--r--source3/utils/status_profile.h30
-rw-r--r--source3/utils/status_profile_dummy.c35
-rw-r--r--source3/utils/testparm.c1060
-rw-r--r--source3/utils/wscript_build364
-rw-r--r--source3/utils/wspsearch.c842
118 files changed, 82158 insertions, 0 deletions
diff --git a/source3/utils/async-tracker.c b/source3/utils/async-tracker.c
new file mode 100644
index 0000000..7b6c2c0
--- /dev/null
+++ b/source3/utils/async-tracker.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2011, Nokia <ivan.frade@nokia.com>
+ * Copyright (C) 2015, Noel Power <nopower@suse.com>
+ * Copyright (C) 2016, Ralph Boehme <slow@samba.org.>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "lib/cmdline/cmdline.h"
+#include "param.h"
+/*
+ * glib uses TRUE and FALSE which was redefined by "includes.h" to be
+ * unusable, undefine so glib can establish its own working
+ * replacement.
+ */
+#undef TRUE
+#undef FALSE
+#include <glib.h>
+#include <libtracker-sparql/tracker-sparql.h>
+#include "lib/tevent_glib_glue.h"
+
+enum loop_type {TEVENT_LOOP, GLIB_LOOP};
+
+struct test_state {
+ enum loop_type loop_type;
+ TrackerSparqlConnection *connection;
+ GCancellable *cancellable;
+ GTimer *timer;
+ GMainLoop *loop;
+ struct tevent_context *ev;
+ struct tevent_glib_glue *glue;
+};
+
+static void cleanup(struct test_state *state)
+{
+ g_cancellable_cancel(state->cancellable);
+ g_object_unref(state->cancellable);
+ g_timer_destroy(state->timer);
+ if (state->connection != NULL) {
+ g_object_unref(state->connection);
+ state->connection = NULL;
+ }
+ if (state->loop_type == GLIB_LOOP) {
+ g_main_loop_quit(state->loop);
+ } else {
+ samba_tevent_glib_glue_quit(state->glue);
+ }
+}
+
+static void cursor_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct test_state *state = talloc_get_type_abort(
+ user_data, struct test_state);
+ TrackerSparqlCursor *cursor = NULL;
+ GError *error = NULL;
+ gboolean more_results;
+ static gint i = 0;
+
+ cursor = TRACKER_SPARQL_CURSOR(object);
+ more_results = tracker_sparql_cursor_next_finish(cursor,
+ res,
+ &error);
+ if (error) {
+ g_critical("Could not run cursor next: %s", error->message);
+
+ if (cursor != NULL) {
+ g_object_unref(cursor);
+ }
+
+ g_error_free(error);
+ cleanup(state);
+ return;
+ }
+
+ if (!more_results) {
+ g_print("\n");
+ g_print("\nAsync cursor next took: %.6f (for all %d results)\n",
+ g_timer_elapsed (state->timer, NULL), i);
+
+ g_object_unref(cursor);
+ cleanup(state);
+ return;
+ }
+
+ if (i++ < 5) {
+ int num_cols = tracker_sparql_cursor_get_n_columns(cursor);
+ int col;
+
+ if (i == 1) {
+ g_print("Printing first 5 results:\n");
+ }
+ for (col = 0; col < num_cols; col++) {
+ g_print(" %s ", tracker_sparql_cursor_get_string(
+ cursor, col, NULL));
+ if (col == num_cols -1 ) {
+ g_print("\n");
+ }
+ }
+
+ if (i == 5) {
+ g_print(" ...\n");
+ g_print(" Printing nothing for remaining results\n");
+ }
+ }
+
+ tracker_sparql_cursor_next_async(cursor,
+ state->cancellable,
+ cursor_cb,
+ state);
+}
+
+static void query_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct test_state *state = talloc_get_type_abort(
+ user_data, struct test_state);
+ TrackerSparqlCursor *cursor = NULL;
+ GError *error = NULL;
+
+ g_print("Async query took: %.6f\n", g_timer_elapsed(state->timer, NULL));
+
+ cursor = tracker_sparql_connection_query_finish(
+ TRACKER_SPARQL_CONNECTION(object),
+ res,
+ &error);
+ if (error) {
+ g_critical("Could not run query: %s", error->message);
+
+ if (cursor) {
+ g_object_unref(cursor);
+ }
+
+ g_error_free(error);
+ cleanup(state);
+ return;
+ }
+
+ g_timer_start(state->timer);
+
+ tracker_sparql_cursor_next_async(cursor,
+ state->cancellable,
+ cursor_cb,
+ state);
+}
+
+static void connection_cb(GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ struct test_state *state = talloc_get_type_abort(
+ user_data, struct test_state);
+ GError *error = NULL;
+
+ g_print("Async connection took: %.6f\n",
+ g_timer_elapsed(state->timer, NULL));
+
+ state->connection = tracker_sparql_connection_get_finish(res, &error);
+ if (error) {
+ g_critical("Could not connect: %s", error->message);
+ g_error_free(error);
+ cleanup(state);
+ return;
+ }
+
+ g_timer_start(state->timer);
+
+ tracker_sparql_connection_query_async(
+ state->connection,
+ "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) "
+ "WHERE { {?s nie:url ?name}}",
+ state->cancellable,
+ query_cb,
+ state);
+}
+
+static void debug_fn(void *private_data,
+ enum tevent_debug_level level,
+ const char *fmt,
+ va_list ap)
+{
+ dbgtext_va(fmt, ap);
+}
+
+int main(int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ struct test_state *state = NULL;
+ int c;
+ poptContext pc;
+ bool ok;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "tevent",
+ .shortName = 't',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'v',
+ .descrip = "Use tevent loop",
+ },
+ {
+ .longName = "glib",
+ .shortName = 'g',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'g',
+ .descrip = "Use glib loop",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ exit(1);
+ }
+
+ state = talloc_zero(mem_ctx, struct test_state);
+ if (state == NULL) {
+ exit(1);
+ }
+
+ state->loop_type = TEVENT_LOOP;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ true /* require_smbconf */);
+ if (!ok) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 'g':
+ state->loop_type = GLIB_LOOP;
+ break;
+ case 't':
+ state->loop_type = TEVENT_LOOP;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(c));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (state->loop_type == GLIB_LOOP) {
+ state->loop = g_main_loop_new(NULL, false);
+ } else {
+ state->ev = tevent_context_init(mem_ctx);
+ if (CHECK_DEBUGLVL(10)) {
+ tevent_set_debug(state->ev, debug_fn, NULL);
+ }
+ state->glue = samba_tevent_glib_glue_create(
+ mem_ctx, state->ev, g_main_context_default());
+ if (state->glue == NULL) {
+ printf("tevent_glib_glue_create failed\n");
+ exit(1);
+ }
+ }
+
+ state->timer = g_timer_new();
+ state->cancellable = g_cancellable_new();
+
+ tracker_sparql_connection_get_async(state->cancellable,
+ connection_cb,
+ state);
+
+ if (state->loop_type == GLIB_LOOP) {
+ printf("entering g_main_loop_run\n");
+ g_main_loop_run(state->loop);
+ } else {
+ printf("entering tevent_loop_wait\n");
+ tevent_loop_wait(state->ev);
+
+ TALLOC_FREE(state->glue);
+ TALLOC_FREE(state->ev);
+ }
+
+ TALLOC_FREE(mem_ctx);
+ poptFreeContext(pc);
+
+ return 0;
+}
diff --git a/source3/utils/clirap2.c b/source3/utils/clirap2.c
new file mode 100644
index 0000000..b2e5187
--- /dev/null
+++ b/source3/utils/clirap2.c
@@ -0,0 +1,2552 @@
+/*
+ Samba Unix/Linux SMB client library
+ More client RAP (SMB Remote Procedure Calls) functions
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2007 Jeremy Allison. jra@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*****************************************************/
+/* */
+/* Additional RAP functionality */
+/* */
+/* RAP is the original SMB RPC, documented */
+/* by Microsoft and X/Open in the 1990s and */
+/* supported by most SMB/CIFS servers although */
+/* it is unlikely that any one implementation */
+/* supports all RAP command codes since some */
+/* are quite obsolete and a few are specific */
+/* to a particular network operating system */
+/* */
+/* Although it has largely been replaced */
+/* for complex remote administration and management */
+/* (of servers) by the relatively newer */
+/* DCE/RPC based remote API (which better handles */
+/* large >64K data structures), there are many */
+/* important administrative and resource location */
+/* tasks and user tasks (e.g. password change) */
+/* that are performed via RAP. */
+/* */
+/* Although a few of the RAP calls are implemented */
+/* in the Samba client library already (clirap.c) */
+/* the new ones are in clirap2.c for easy patching */
+/* and integration and a corresponding header */
+/* file, rap.h, has been created. */
+/* */
+/* This is based on data from the CIFS spec */
+/* and the LAN Server and LAN Manager */
+/* Programming Reference books and published */
+/* RAP document and CIFS forum postings and */
+/* lots of trial and error */
+/* */
+/* Function names changed from API_ (as they are */
+/* in the CIFS specification) to RAP_ in order */
+/* to avoid confusion with other API calls */
+/* sent via DCE RPC */
+/* */
+/*****************************************************/
+
+/*****************************************************/
+/* */
+/* cifsrap.c already includes support for: */
+/* */
+/* WshareEnum ( API number 0, level 1) */
+/* NetServerEnum2 (API num 104, level 1) */
+/* WWkstaUserLogon (132) */
+/* SamOEMchgPasswordUser2_P (214) */
+/* */
+/* cifsprint.c already includes support for: */
+/* */
+/* WPrintJobEnum (API num 76, level 2) */
+/* WPrintJobDel (API num 81) */
+/* */
+/*****************************************************/
+
+#include "includes.h"
+#include "libsmb/libsmb.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "../librpc/gen_ndr/svcctl.h"
+#include "clirap2.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+#define WORDSIZE 2
+#define DWORDSIZE 4
+
+#define PUTBYTE(p,b) do {SCVAL(p,0,b); p++;} while(0)
+
+#define GETBYTE(p,b,endp) \
+ do {\
+ if (p+1 < endp) {\
+ b = CVAL(p,0);\
+ }\
+ p++;\
+ } while(0)
+
+#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0)
+
+#define GETWORD(p,w,endp) \
+ do {\
+ if (p+WORDSIZE < endp) {\
+ w = SVAL(p,0);\
+ }\
+ p += WORDSIZE;\
+ } while(0)
+
+#define PUTDWORD(p,d) do {SIVAL(p,0,d); p += DWORDSIZE;} while(0)
+
+#define GETDWORD(p,d,endp) \
+ do {\
+ if (p+DWORDSIZE < endp) {\
+ d = IVAL(p,0);\
+ }\
+ p += DWORDSIZE;\
+ } while(0)
+
+#define GETRES(p,endp) ((p && p+2 < endp) ? SVAL(p,0) : -1)
+
+/**
+ * Skip past some strings in a buffer - old version - no checks.
+ * **/
+
+static char *push_skip_string(char *buf)
+{
+ buf += strlen(buf) + 1;
+ return(buf);
+}
+
+/* put string s at p with max len n and increment p past string */
+#define PUTSTRING(p,s,n) \
+ do {\
+ push_ascii(p,s?s:"",n?n:256,STR_TERMINATE);\
+ p = push_skip_string(p);\
+ } while(0)
+
+/* put string s and p, using fixed len l, and increment p by l */
+#define PUTSTRINGF(p,s,l) \
+ do {\
+ push_ascii(p,s?s:"",l,STR_TERMINATE);\
+ p += l;\
+ } while (0)
+
+/* put string pointer at p, supplying offset o from rdata r, store */
+/* dword offset at p, increment p by 4 and o by length of s. This */
+/* means on the first call, you must calc the offset yourself! */
+
+#define PUTSTRINGP(p,s,r,o) \
+ do {\
+ if (s) {\
+ push_ascii(r+o,s,strlen(s)+1,STR_TERMINATE);\
+ PUTDWORD(p,o);\
+ o += strlen(s) + 1;\
+ } else {\
+ PUTDWORD(p,0);\
+ }\
+ }while(0);
+
+/* get asciiz string dest from src, return increment past string */
+
+static size_t rap_getstring(TALLOC_CTX *ctx, char *src, char **dest, const char *endp)
+{
+ char *p1;
+ size_t len;
+
+ *dest = NULL;
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++)
+ p1++;
+ if (!*p1) {
+ len++;
+ }
+ pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII);
+ return len;
+}
+
+/* get fixed length l string dest from src, return increment for src */
+
+static size_t rap_getstringf(char *src, char *dest, size_t l, size_t dlen, char *endp)
+{
+ char *p1;
+ size_t len;
+
+ if (dlen) {
+ dest[0] = '\0';
+ }
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++) {
+ p1++;
+ }
+ if (!*p1) {
+ len++;
+ }
+ if (len > l) {
+ len = l;
+ }
+ if (len) {
+ pull_ascii(dest,src,len,len,STR_ASCII);
+ }
+ return l;
+}
+
+/* get string dest from offset (obtained at p) from rdata r - converter c */
+static size_t rap_getstringp(TALLOC_CTX *ctx, char *p, char **dest, char *r, uint16_t c, char *endp)
+{
+ uint32_t off = 0;
+ const char *src;
+ size_t len=0;
+
+ *dest = NULL;
+ if (p+4 < endp) {
+ GETDWORD(p,off,endp);
+ off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */
+ off -= c;
+ }
+ if (r+off > endp || r+off < r) {
+ src="";
+ len=1;
+ } else {
+ const char *p1;
+ src=r+off;
+ for (p1 = src, len = 0; *p1 && p1 < endp; len++) {
+ p1++;
+ }
+ if (!*p1) {
+ len++;
+ }
+ }
+ pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII);
+ return 4;
+}
+
+static char *make_header(char *param, uint16_t apinum, const char *reqfmt, const char *datafmt)
+{
+ PUTWORD(param,apinum);
+ if (reqfmt)
+ PUTSTRING(param,reqfmt,0);
+ else
+ *param++ = (char) 0;
+
+ if (datafmt)
+ PUTSTRING(param,datafmt,0);
+ else
+ *param++ = (char) 0;
+
+ return param;
+}
+
+/****************************************************************************
+ call a NetGroupDelete - delete user group from remote server
+****************************************************************************/
+
+int cli_NetGroupDelete(struct cli_state *cli, const char *group_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api GroupDel */
+ p = make_header(param, RAP_WGroupDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, group_name, RAP_GROUPNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ } else if (res == 2220) {
+ DEBUG (1, ("Group does not exist\n"));
+ } else {
+ DEBUG(4,("NetGroupDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ call a NetGroupAdd - add user group to remote server
+****************************************************************************/
+
+int cli_NetGroupAdd(struct cli_state *cli, struct rap_group_info_1 *grinfo)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAdd_REQ) /* req string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset = RAP_GROUPNAME_LEN + 1 + DWORDSIZE;
+ char *data;
+ size_t data_size;
+
+ /* Allocate data. */
+ data_size = MAX(soffset + strlen(grinfo->comment) + 1, 1024);
+
+ data = SMB_MALLOC_ARRAY(char, data_size);
+ if (!data) {
+ DEBUG (1, ("Malloc fail\n"));
+ return -1;
+ }
+
+ /* now send a SMBtrans command with api WGroupAdd */
+
+ p = make_header(param, RAP_WGroupAdd,
+ RAP_NetGroupAdd_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, (const char *)grinfo->group_name, RAP_GROUPNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGP(p, grinfo->comment, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, data_size, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ } else if (res == 2223) {
+ DEBUG (1, ("Group already exists\n"));
+ } else {
+ DEBUG(4,("NetGroupAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAdd failed\n"));
+ }
+
+ SAFE_FREE(data);
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetGroupEnum - try and list user groups on a different host.
+****************************************************************************/
+
+int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupEnum_REQ) /* parm string */
+ +sizeof(RAP_GROUP_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WGroupEnum,
+ RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */ /* add level 0 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rdrcnt;
+
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if(cli->rap_error == 234) {
+ DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n"));
+ } else if (cli->rap_error != 0) {
+ DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp;i++) {
+ char *comment = NULL;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+
+ if (!comment || !groupname[0]) {
+ break;
+ }
+
+ fn(groupname, comment, cli);
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_RNetGroupEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupEnum_REQ) /* parm string */
+ +sizeof(RAP_GROUP_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WGroupEnum,
+ RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L0);
+ PUTWORD(p,0); /* Info level 0 */ /* Hmmm. I *very* much suspect this
+ is the resume count, at least
+ that's what smbd believes... */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam+rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if(cli->rap_error == 234) {
+ DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n"));
+ } else if (cli->rap_error != 0) {
+ DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetGroupEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp;i++) {
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ if (groupname[0]) {
+ fn(groupname, cli);
+ }
+ }
+ } else {
+ DEBUG(4,("NetGroupEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDelUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to del */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupDelUser, RAP_NetGroupDelUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ case 2237:
+ DEBUG(1, ("User is not in group\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupDelUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupDelUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupAddUser_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +RAP_USERNAME_LEN]; /* user to add */
+
+ /* now send a SMBtrans command with api GroupMemberAdd */
+ p = make_header(param, RAP_WGroupAddUser, RAP_NetGroupAddUser_REQ, NULL);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ switch(res) {
+ case 0:
+ break;
+ case 5:
+ case 65:
+ DEBUG(1, ("Access Denied\n"));
+ break;
+ case 50:
+ DEBUG(1, ("Not supported by server\n"));
+ break;
+ case 2220:
+ DEBUG(1, ("Group does not exist\n"));
+ break;
+ case 2221:
+ DEBUG(1, ("User does not exist\n"));
+ break;
+ default:
+ DEBUG(4,("NetGroupAddUser res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetGroupAddUser failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+
+int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupGetUsers_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_GROUPNAME_LEN /* group name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WGroupGetUsers,
+ RAP_NetGroupGetUsers_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,group_name,RAP_GROUPNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetGroupGetUsers gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetGroupGetUsers no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+ char username[RAP_USERNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp; i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ if (username[0]) {
+ fn(username, state);
+ }
+ }
+ } else {
+ DEBUG(4,("NetGroupGetUsers res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserGetGroups_REQ)/* parm string */
+ +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */
+ +RAP_USERNAME_LEN /* user name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api GroupGetUsers */
+ p = make_header(param, RAP_WUserGetGroups,
+ RAP_NetUserGetGroups_REQ, RAP_GROUP_USERS_INFO_0);
+ PUTSTRING(p,user_name,RAP_USERNAME_LEN-1);
+ PUTWORD(p,0); /* info level 0 */
+ PUTWORD(p,0xFFE0); /* return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetUserGetGroups gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserGetGroups no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+ char groupname[RAP_GROUPNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata; i<count && p < endp; i++) {
+ p += rap_getstringf(p,
+ groupname,
+ RAP_GROUPNAME_LEN,
+ RAP_GROUPNAME_LEN,
+ endp);
+ if (groupname[0]) {
+ fn(groupname, state);
+ }
+ }
+ } else {
+ DEBUG(4,("NetUserGetGroups res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
+
+/****************************************************************************
+ Call a NetUserDelete - delete user from remote server.
+****************************************************************************/
+
+int cli_NetUserDelete(struct cli_state *cli, const char * user_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetGroupDel_REQ) /* parm string */
+ +1 /* no ret string */
+ +RAP_USERNAME_LEN /* user to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api UserDel */
+ p = make_header(param, RAP_WUserDel, RAP_NetGroupDel_REQ, NULL);
+ PUTSTRING(p, user_name, RAP_USERNAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ } else if (res == 2221) {
+ DEBUG (1, ("User does not exist\n"));
+ } else {
+ DEBUG(4,("NetUserDelete res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetUserAdd - add user to remote server.
+****************************************************************************/
+
+int cli_NetUserAdd(struct cli_state *cli, struct rap_user_info_1 * userinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserAdd2_REQ) /* req string */
+ +sizeof(RAP_USER_INFO_L1) /* data string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer length */
+ +WORDSIZE]; /* reserved */
+
+ char data[1024];
+ /* offset into data of free format strings. Will be updated */
+ /* by PUTSTRINGP macro and end up with total data length. */
+ int soffset=RAP_USERNAME_LEN+1 /* user name + pad */
+ + RAP_UPASSWD_LEN /* password */
+ + DWORDSIZE /* password age */
+ + WORDSIZE /* privilege */
+ + DWORDSIZE /* home dir ptr */
+ + DWORDSIZE /* comment ptr */
+ + WORDSIZE /* flags */
+ + DWORDSIZE; /* login script ptr*/
+
+ /* now send a SMBtrans command with api NetUserAdd */
+ p = make_header(param, RAP_WUserAdd2,
+ RAP_NetUserAdd2_REQ, RAP_USER_INFO_L1);
+
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, 0); /* pwencrypt */
+ PUTWORD(p, MIN(strlen((const char *)userinfo->passwrd),
+ RAP_UPASSWD_LEN));
+
+ p = data;
+ memset(data, '\0', soffset);
+
+ PUTSTRINGF(p, (const char *)userinfo->user_name, RAP_USERNAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+ PUTSTRINGF(p, (const char *)userinfo->passwrd, RAP_UPASSWD_LEN);
+ PUTDWORD(p, 0); /* pw age - n.a. on user add */
+ PUTWORD(p, userinfo->priv);
+ PUTSTRINGP(p, userinfo->home_dir, data, soffset);
+ PUTSTRINGP(p, userinfo->comment, data, soffset);
+ PUTWORD(p, userinfo->userflags);
+ PUTSTRINGP(p, userinfo->logon_script, data, soffset);
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if ((res == 5) || (res == 65)) {
+ DEBUG(1, ("Access Denied\n"));
+ } else if (res == 2224) {
+ DEBUG (1, ("User already exists\n"));
+ } else {
+ DEBUG(4,("NetUserAdd res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetUserAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+call a NetUserEnum - try and list users on a different host
+****************************************************************************/
+
+int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserEnum_REQ) /* parm string */
+ +sizeof(RAP_USER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WUserEnum,
+ RAP_NetUserEnum_REQ, RAP_USER_INFO_L1);
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFF00); /* Return buffer size */
+
+ /* BB Fix handling of large numbers of users to be returned */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, converter = 0, count = 0;
+ char username[RAP_USERNAME_LEN];
+ char userpw[RAP_UPASSWD_LEN];
+ char *endp = rparam + rprcnt;
+ char *comment, *homedir, *logonscript;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ userpw,
+ RAP_UPASSWD_LEN,
+ RAP_UPASSWD_LEN,
+ endp);
+ p += DWORDSIZE; /* skip password age */
+ p += WORDSIZE; /* skip priv: 0=guest, 1=user, 2=admin */
+ p += rap_getstringp(frame,
+ p,
+ &homedir,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ p += WORDSIZE; /* skip flags */
+ p += rap_getstringp(frame,
+ p,
+ &logonscript,
+ rdata,
+ converter,
+ endp);
+ if (username[0] && comment &&
+ homedir && logonscript) {
+ fn(username,
+ comment,
+ homedir,
+ logonscript,
+ cli);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_RNetUserEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetUserEnum_REQ) /* parm string */
+ +sizeof(RAP_USER_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WUserEnum,
+ RAP_NetUserEnum_REQ, RAP_USER_INFO_L0);
+ PUTWORD(p,0); /* Info level 1 */
+ PUTWORD(p,0xFF00); /* Return buffer size */
+
+ /* BB Fix handling of large numbers of users to be returned */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetUserEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, count = 0;
+ char *endp = rparam + rprcnt;
+ char username[RAP_USERNAME_LEN];
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ p += rap_getstringf(p,
+ username,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ if (username[0]) {
+ fn(username, cli);
+ }
+ }
+ } else {
+ DEBUG(4,("NetUserEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetFileClose2 - close open file on another session to server.
+****************************************************************************/
+
+int cli_NetFileClose(struct cli_state *cli, uint32_t file_id )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileClose2_REQ) /* req string */
+ +1 /* no ret string */
+ +DWORDSIZE]; /* file ID */
+ int res = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileClose2, RAP_WFileClose2_REQ, NULL);
+ PUTDWORD(p, file_id);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else if (res == 2314){
+ DEBUG(1, ("NetFileClose2 - attempt to close non-existent file open instance\n"));
+ } else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetFileGetInfo - get information about server file opened from other
+ workstation.
+****************************************************************************/
+
+int cli_NetFileGetInfo(struct cli_state *cli, uint32_t file_id, void (*fn)(const char *, const char *, uint16_t, uint16_t, uint32_t))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileGetInfo2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +DWORDSIZE /* file ID */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileGetInfo2,
+ RAP_WFileGetInfo2_REQ, RAP_FILE_INFO_L3);
+ PUTDWORD(p, file_id);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0x1000); /* buffer size */
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0x1000, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0,id = 0, perms = 0, locks = 0;
+ char *fpath, *fuser;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+
+ GETDWORD(p, id, endp);
+ GETWORD(p, perms, endp);
+ GETWORD(p, locks, endp);
+
+ p += rap_getstringp(frame,
+ p,
+ &fpath,
+ rdata,
+ converter,
+ endp);
+ rap_getstringp(frame,
+ p,
+ &fuser,
+ rdata,
+ converter,
+ endp);
+
+ if (fpath && fuser) {
+ fn(fpath, fuser, perms, locks, id);
+ }
+
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetFileGetInfo2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileGetInfo2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+* Call a NetFileEnum2 - list open files on an SMB server
+*
+* PURPOSE: Remotes a NetFileEnum API call to the current server or target
+* server listing the files open via the network (and their
+* corresponding open instance ids)
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* user - if present, return only files opened by this remote user
+* base_path - if present, return only files opened below this
+* base path
+* fn - display function to invoke for each entry in the result
+*
+*
+* Returns:
+* True - success
+* False - failure
+*
+****************************************************************************/
+
+int cli_NetFileEnum(struct cli_state *cli, const char * user,
+ const char * base_path,
+ void (*fn)(const char *, const char *, uint16_t, uint16_t,
+ uint32_t))
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WFileEnum2_REQ) /* req string */
+ +sizeof(RAP_FILE_INFO_L3) /* return string */
+ +1024 /* base path (opt) */
+ +RAP_USERNAME_LEN /* user name (opt) */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* resume key ? */
+ +DWORDSIZE]; /* resume key ? */
+ int count = -1;
+ int res = -1;
+
+ /* now send a SMBtrans command with api RNetShareEnum */
+ p = make_header(param, RAP_WFileEnum2,
+ RAP_WFileEnum2_REQ, RAP_FILE_INFO_L3);
+
+ PUTSTRING(p, base_path, 1024);
+ PUTSTRING(p, user, RAP_USERNAME_LEN);
+ PUTWORD(p, 3); /* info level */
+ PUTWORD(p, 0xFF00); /* buffer size */
+ PUTDWORD(p, 0); /* zero out the resume key */
+ PUTDWORD(p, 0); /* or is this one the resume key? */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 0xFF00, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0, i;
+
+ p = rparam + WORDSIZE; /* skip result */
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i=0; i<count && p < endp; i++) {
+ int id = 0, perms = 0, locks = 0;
+ char *fpath, *fuser;
+
+ GETDWORD(p, id, endp);
+ GETWORD(p, perms, endp);
+ GETWORD(p, locks, endp);
+ p += rap_getstringp(frame,
+ p,
+ &fpath,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &fuser,
+ rdata,
+ converter,
+ endp);
+
+ if (fpath && fuser) {
+ fn(fpath, fuser, perms, locks, id);
+ }
+ } /* BB fix ERRmoredata case to send resume request */
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetFileEnum2 res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetFileEnum2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return count;
+}
+
+/****************************************************************************
+ Call a NetShareAdd - share/export directory on remote server.
+****************************************************************************/
+
+int cli_NetShareAdd(struct cli_state *cli, struct rap_share_info_2 * sinfo )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareAdd_REQ) /* req string */
+ +sizeof(RAP_SHARE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* reserved word */
+ char data[1024];
+ /* offset to free format string section following fixed length data. */
+ /* will be updated by PUTSTRINGP macro and will end up with total len */
+ int soffset = RAP_SHARENAME_LEN + 1 /* share name + pad */
+ + WORDSIZE /* share type */
+ + DWORDSIZE /* comment pointer */
+ + WORDSIZE /* permissions */
+ + WORDSIZE /* max users */
+ + WORDSIZE /* active users */
+ + DWORDSIZE /* share path */
+ + RAP_SPASSWD_LEN + 1; /* share password + pad */
+
+ memset(param,'\0',sizeof(param));
+ /* now send a SMBtrans command with api RNetShareAdd */
+ p = make_header(param, RAP_WshareAdd,
+ RAP_WShareAdd_REQ, RAP_SHARE_INFO_L2);
+ PUTWORD(p, 2); /* info level */
+ PUTWORD(p, 0); /* reserved word 0 */
+
+ p = data;
+ PUTSTRINGF(p, (const char *)sinfo->share_name, RAP_SHARENAME_LEN);
+ PUTBYTE(p, 0); /* pad byte 0 */
+
+ PUTWORD(p, sinfo->share_type);
+ PUTSTRINGP(p, sinfo->comment, data, soffset);
+ PUTWORD(p, sinfo->perms);
+ PUTWORD(p, sinfo->maximum_users);
+ PUTWORD(p, sinfo->active_users);
+ PUTSTRINGP(p, sinfo->path, data, soffset);
+ PUTSTRINGF(p, (const char *)sinfo->password, RAP_SPASSWD_LEN);
+ SCVAL(p,-1,0x0A); /* required 0x0A at end of password */
+
+ if (cli_api(cli,
+ param, sizeof(param), 1024, /* Param, length, maxlen */
+ data, soffset, sizeof(data), /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else {
+ DEBUG(4,("NetShareAdd res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetShareAdd failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetShareDelete - unshare exported directory on remote server.
+****************************************************************************/
+
+int cli_NetShareDelete(struct cli_state *cli, const char * share_name )
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ int res = -1;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WShareDel_REQ) /* req string */
+ +1 /* no ret string */
+ +RAP_SHARENAME_LEN /* share to del */
+ +WORDSIZE]; /* reserved word */
+
+ /* now send a SMBtrans command with api RNetShareDelete */
+ p = make_header(param, RAP_WshareDel, RAP_WShareDel_REQ, NULL);
+ PUTSTRING(p,share_name,RAP_SHARENAME_LEN);
+ PUTWORD(p,0); /* reserved word MBZ on input */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0) {
+ /* nothing to do */
+ } else {
+ DEBUG(4,("NetShareDelete res=%d\n", res));
+ }
+ } else {
+ DEBUG(4,("NetShareDelete failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/*************************************************************************
+*
+* Function Name: cli_get_pdc_name
+*
+* PURPOSE: Remotes a NetServerEnum API call to the current server
+* requesting the name of a server matching the server
+* type of SV_TYPE_DOMAIN_CTRL (PDC).
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing name of domain
+* pdc_name - pointer to string that will contain PDC name
+* on successful return
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+
+bool cli_get_pdc_name(struct cli_state *cli, const char *workgroup, char **pdc_name)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ int count = -1;
+ int res = -1;
+
+ *pdc_name = NULL;
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, SV_TYPE_DOMAIN_CTRL);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+
+ /*
+ * We only really care to copy a name if the
+ * API succeeded and we got back a name.
+ */
+ if (cli->rap_error == 0) {
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count, endp);
+ p = rdata;
+ endp = rdata + rdrcnt;
+
+ if (count > 0) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *dcname;
+ rap_getstring(frame,
+ p,
+ &dcname,
+ endp);
+ if (dcname) {
+ *pdc_name = SMB_STRDUP(dcname);
+ }
+ TALLOC_FREE(frame);
+ }
+ } else {
+ DEBUG(4, ("cli_get_pdc_name: machine %s failed the "
+ "NetServerEnum call. Error was : %s.\n",
+ smbXcli_conn_remote_name(cli->conn),
+ win_errstr(W_ERROR(cli->rap_error))));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return(count > 0);
+}
+
+bool cli_get_server_name(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ char **servername)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_WserverGetInfo_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L1) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ bool res = false;
+ char *endp;
+ fstring tmp;
+
+ /* send a SMBtrans command with api NetServerGetInfo */
+ p = make_header(param, RAP_WserverGetInfo,
+ RAP_WserverGetInfo_REQ, RAP_SERVER_INFO_L1);
+ PUTWORD(p, 1); /* info level */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (!cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ goto failed;
+ }
+
+ endp = rparam + rprcnt;
+ if (GETRES(rparam, endp) != 0) {
+ goto failed;
+ }
+
+ if (rdrcnt < 16) {
+ DEBUG(10, ("invalid data count %d, expected >= 16\n", rdrcnt));
+ goto failed;
+ }
+
+ if (pull_ascii(tmp, rdata, sizeof(tmp)-1, 16, STR_TERMINATE) == -1) {
+ DEBUG(10, ("pull_ascii failed\n"));
+ goto failed;
+ }
+
+ if (!(*servername = talloc_strdup(mem_ctx, tmp))) {
+ DEBUG(1, ("talloc_strdup failed\n"));
+ goto failed;
+ }
+
+ res = true;
+
+ failed:
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return res;
+}
+
+/*************************************************************************
+*
+* Function Name: cli_ns_check_server_type
+*
+* PURPOSE: Remotes a NetServerEnum2 API call to the current server
+* requesting server_info_0 level information of machines
+* matching the given server type. If the returned server
+* list contains the machine name contained in smbXcli_conn_remote_name(->conn)
+* then we conclude the server type checks out. This routine
+* is useful to retrieve list of server's of a certain
+* type when all you have is a null session connection and
+* can't remote API calls such as NetWkstaGetInfo or
+* NetServerGetInfo.
+*
+* Dependencies: none
+*
+* Parameters:
+* cli - pointer to cli_state structure
+* workgroup - pointer to string containing domain
+* stype - server type
+*
+* Returns:
+* True - success
+* False - failure
+*
+************************************************************************/
+
+bool cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32_t stype)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rdrcnt,rprcnt;
+ char *p;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServerEnum2_REQ) /* req string */
+ +sizeof(RAP_SERVER_INFO_L0) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +DWORDSIZE /* server type */
+ +RAP_MACHNAME_LEN]; /* workgroup */
+ bool found_server = false;
+ int res = -1;
+ const char *remote_name = smbXcli_conn_remote_name(cli->conn);
+
+ /* send a SMBtrans command with api NetServerEnum */
+ p = make_header(param, RAP_NetServerEnum2,
+ RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L0);
+ PUTWORD(p, 0); /* info level 0 */
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTDWORD(p, stype);
+ PUTSTRING(p, workgroup, RAP_MACHNAME_LEN);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 8, /* params, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+
+ if (res == 0 || res == ERRmoredata) {
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE;
+ GETWORD(p, count,endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i = 0;i < count && p < endp;i++, p += 16) {
+ char ret_server[RAP_MACHNAME_LEN];
+
+ p += rap_getstringf(p,
+ ret_server,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ if (strequal(ret_server, remote_name)) {
+ found_server = true;
+ break;
+ }
+ }
+ } else {
+ DEBUG(4, ("cli_ns_check_server_type: machine %s "
+ "failed the NetServerEnum call. Error was : "
+ "%s.\n", remote_name,
+ win_errstr(W_ERROR(cli->rap_error))));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return found_server;
+}
+
+/****************************************************************************
+ Perform a NetWkstaUserLogoff.
+****************************************************************************/
+
+bool cli_NetWkstaUserLogoff(struct cli_state *cli, const char *user, const char *workstation)
+{
+ char *rparam = NULL;
+ char *rdata = NULL;
+ char *p;
+ unsigned int rdrcnt,rprcnt;
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetWkstaUserLogoff_REQ) /* req string */
+ +sizeof(RAP_USER_LOGOFF_INFO_L1) /* return string */
+ +RAP_USERNAME_LEN+1 /* user name+pad */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* buffer size */
+ +WORDSIZE]; /* buffer size? */
+ char upperbuf[MAX(RAP_USERNAME_LEN,RAP_MACHNAME_LEN)];
+ int res = -1;
+ char *tmp = NULL;
+
+ memset(param, 0, sizeof(param));
+
+ /* send a SMBtrans command with api NetWkstaUserLogoff */
+ p = make_header(param, RAP_WWkstaUserLogoff,
+ RAP_NetWkstaUserLogoff_REQ, RAP_USER_LOGOFF_INFO_L1);
+ PUTDWORD(p, 0); /* Null pointer */
+ PUTDWORD(p, 0); /* Null pointer */
+ strlcpy(upperbuf, user, sizeof(upperbuf));
+ if (!strupper_m(upperbuf)) {
+ return false;
+ }
+ tmp = upperbuf;
+ PUTSTRINGF(p, tmp, RAP_USERNAME_LEN);
+ p++; /* strange format, but ok */
+ strlcpy(upperbuf, workstation, sizeof(upperbuf));
+ if (!strupper_m(upperbuf)) {
+ return false;
+ }
+ tmp = upperbuf;
+ PUTSTRINGF(p, tmp, RAP_MACHNAME_LEN);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+ PUTWORD(p, CLI_BUFFER_SIZE);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024, /* param, length, max */
+ NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */
+ &rparam, &rprcnt, /* return params, return size */
+ &rdata, &rdrcnt /* return data, return size */
+ )) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam,endp);
+ cli->rap_error = res;
+
+ if (cli->rap_error != 0) {
+ DEBUG(4,("NetwkstaUserLogoff gave error %d\n", cli->rap_error));
+ }
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+ return (cli->rap_error == 0);
+}
+
+int cli_NetPrintQEnum(struct cli_state *cli,
+ void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t),
+ void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQEnum_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQEnum,
+ RAP_NetPrintQEnum_REQ, RAP_PRINTQ_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetPrintQEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ for (i=0;i<count && p < endp;i++) {
+ char qname[RAP_SHARENAME_LEN];
+ char *sep_file, *print_proc, *dest, *parms, *comment;
+ uint16_t jobcount = 0, priority = 0;
+ uint16_t start_time = 0, until_time = 0, status = 0;
+
+ p += rap_getstringf(p,
+ qname,
+ RAP_SHARENAME_LEN,
+ RAP_SHARENAME_LEN,
+ endp);
+ p++; /* pad */
+ GETWORD(p, priority, endp);
+ GETWORD(p, start_time, endp);
+ GETWORD(p, until_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &sep_file,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &print_proc,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &dest,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &parms,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, status, endp);
+ GETWORD(p, jobcount, endp);
+
+ if (sep_file && print_proc && dest && parms &&
+ comment) {
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+ }
+
+ if (jobcount) {
+ int j;
+ for (j=0;j<jobcount;j++) {
+ uint16_t jid = 0, pos = 0, fsstatus = 0;
+ char ownername[RAP_USERNAME_LEN];
+ char notifyname[RAP_MACHNAME_LEN];
+ char datatype[RAP_DATATYPE_LEN];
+ char *jparms, *jstatus, *jcomment;
+ unsigned int submitted = 0, jsize = 0;
+
+ GETWORD(p, jid, endp);
+ p += rap_getstringf(p,
+ ownername,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ notifyname,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ p += rap_getstringf(p,
+ datatype,
+ RAP_DATATYPE_LEN,
+ RAP_DATATYPE_LEN,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &jparms,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, pos, endp);
+ GETWORD(p, fsstatus, endp);
+ p += rap_getstringp(frame,
+ p,
+ &jstatus,
+ rdata,
+ converter,
+ endp);
+ GETDWORD(p, submitted, endp);
+ GETDWORD(p, jsize, endp);
+ p += rap_getstringp(frame,
+ p,
+ &jcomment,
+ rdata,
+ converter,
+ endp);
+
+ if (jparms && jstatus && jcomment) {
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetPrintQEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer,
+ void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t),
+ void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetPrintQGetInfo_REQ) /* req string */
+ +sizeof(RAP_PRINTQ_INFO_L2) /* return string */
+ +RAP_SHARENAME_LEN /* printer name */
+ +WORDSIZE /* info level */
+ +WORDSIZE /* buffer size */
+ +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0',sizeof(param));
+ p = make_header(param, RAP_WPrintQGetInfo,
+ RAP_NetPrintQGetInfo_REQ, RAP_PRINTQ_INFO_L2);
+ PUTSTRING(p, printer, RAP_SHARENAME_LEN-1);
+ PUTWORD(p, 2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+ PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0);
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),1024,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetPrintQGetInfo gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetPrintQGetInfo no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int rsize = 0, converter = 0;
+ char qname[RAP_SHARENAME_LEN];
+ char *sep_file, *print_proc, *dest, *parms, *comment;
+ uint16_t jobcount = 0, priority = 0;
+ uint16_t start_time = 0, until_time = 0, status = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, rsize, endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ p += rap_getstringf(p,
+ qname,
+ RAP_SHARENAME_LEN,
+ RAP_SHARENAME_LEN,
+ endp);
+ p++; /* pad */
+ GETWORD(p, priority, endp);
+ GETWORD(p, start_time, endp);
+ GETWORD(p, until_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &sep_file,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &print_proc,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &dest,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &parms,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &comment,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, status, endp);
+ GETWORD(p, jobcount, endp);
+
+ if (sep_file && print_proc && dest &&
+ parms && comment) {
+ qfn(qname, priority, start_time, until_time, sep_file, print_proc,
+ dest, parms, comment, status, jobcount);
+ }
+ if (jobcount) {
+ int j;
+ for (j=0;(j<jobcount)&&(PTR_DIFF(p,rdata)< rsize)&&
+ p<endp;j++) {
+ uint16_t jid = 0, pos = 0, fsstatus = 0;
+ char ownername[RAP_USERNAME_LEN];
+ char notifyname[RAP_MACHNAME_LEN];
+ char datatype[RAP_DATATYPE_LEN];
+ char *jparms, *jstatus, *jcomment;
+ unsigned int submitted = 0, jsize = 0;
+
+ GETWORD(p, jid, endp);
+ p += rap_getstringf(p,
+ ownername,
+ RAP_USERNAME_LEN,
+ RAP_USERNAME_LEN,
+ endp);
+ p++; /* pad byte */
+ p += rap_getstringf(p,
+ notifyname,
+ RAP_MACHNAME_LEN,
+ RAP_MACHNAME_LEN,
+ endp);
+ p += rap_getstringf(p,
+ datatype,
+ RAP_DATATYPE_LEN,
+ RAP_DATATYPE_LEN,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &jparms,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, pos,endp);
+ GETWORD(p, fsstatus,endp);
+ p += rap_getstringp(frame,
+ p,
+ &jstatus,
+ rdata,
+ converter,
+ endp);
+ GETDWORD(p, submitted,endp);
+ GETDWORD(p, jsize,endp);
+ p += rap_getstringp(frame,
+ p,
+ &jcomment,
+ rdata,
+ converter,
+ endp);
+
+ if (jparms && jstatus && jcomment) {
+ jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus,
+ jstatus, submitted, jsize, jcomment);
+ }
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetPrintQGetInfo res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetServiceEnum - list running services on a different host.
+****************************************************************************/
+
+int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetServiceEnum_REQ) /* parm string */
+ +sizeof(RAP_SERVICE_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WServiceEnum,
+ RAP_NetServiceEnum_REQ, RAP_SERVICE_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, 0xFFE0 /* data area size */,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if(cli->rap_error == 234) {
+ DEBUG(1,("Not all service names were returned (such as those longer than 15 characters)\n"));
+ } else if (cli->rap_error != 0) {
+ DEBUG(1,("NetServiceEnum gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetServiceEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ char *endp = rparam + rprcnt;
+ int i, count = 0;
+
+ p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */
+ GETWORD(p, count,endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char comment[RAP_SRVCCMNT_LEN];
+ char servicename[RAP_SRVCNAME_LEN];
+
+ p += rap_getstringf(p,
+ servicename,
+ RAP_SRVCNAME_LEN,
+ RAP_SRVCNAME_LEN,
+ endp);
+ p+=8; /* pass status words */
+ p += rap_getstringf(p,
+ comment,
+ RAP_SRVCCMNT_LEN,
+ RAP_SRVCCMNT_LEN,
+ endp);
+
+ if (servicename[0]) {
+ fn(servicename, comment, cli); /* BB add status too */
+ }
+ }
+ } else {
+ DEBUG(4,("NetServiceEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetSessionEnum - list workstations with sessions to an SMB server.
+****************************************************************************/
+
+int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionEnum_REQ) /* parm string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionEnum,
+ RAP_NetSessionEnum_REQ, RAP_SESSION_INFO_L2);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),8,
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetSessionEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetSessionEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char *wsname, *username, *clitype_name;
+ uint16_t num_conns = 0, num_opens = 0, num_users = 0;
+ unsigned int sess_time = 0, idle_time = 0, user_flags = 0;
+
+ p += rap_getstringp(frame,
+ p,
+ &wsname,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, num_conns, endp);
+ GETWORD(p, num_opens, endp);
+ GETWORD(p, num_users, endp);
+ GETDWORD(p, sess_time, endp);
+ GETDWORD(p, idle_time, endp);
+ GETDWORD(p, user_flags, endp);
+ p += rap_getstringp(frame,
+ p,
+ &clitype_name,
+ rdata,
+ converter,
+ endp);
+
+ if (wsname && username && clitype_name) {
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetSessionEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetSessionGetInfo - get information about other session to an SMB server.
+****************************************************************************/
+
+int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation,
+ void (*fn)(const char *, const char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, const char *))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionGetInfo_REQ) /* req string */
+ +sizeof(RAP_SESSION_INFO_L2) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ char *endp;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionGetInfo,
+ RAP_NetSessionGetInfo_REQ, RAP_SESSION_INFO_L2);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,2); /* Info level 2 */
+ PUTWORD(p,0xFF); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (cli->rap_error != 0) {
+ DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetSessionGetInfo no data returned\n"));
+ goto out;
+ }
+
+ endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ int converter = 0;
+ char *wsname, *username, *clitype_name;
+ uint16_t num_conns = 0, num_opens = 0, num_users = 0;
+ unsigned int sess_time = 0, idle_time = 0, user_flags = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter,endp);
+
+ p = rdata;
+ endp = rdata + rdrcnt;
+ p += rap_getstringp(frame,
+ p,
+ &wsname,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ GETWORD(p, num_conns, endp);
+ GETWORD(p, num_opens, endp);
+ GETWORD(p, num_users, endp);
+ GETDWORD(p, sess_time, endp);
+ GETDWORD(p, idle_time, endp);
+ GETDWORD(p, user_flags, endp);
+ rap_getstringp(frame,
+ p,
+ &clitype_name,
+ rdata,
+ converter,
+ endp);
+
+ if (wsname && username && clitype_name) {
+ fn(wsname, username, num_conns, num_opens, num_users, sess_time,
+ idle_time, user_flags, clitype_name);
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetSessionGetInfo res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+/****************************************************************************
+ Call a NetSessionDel - close a session to an SMB server.
+****************************************************************************/
+
+int cli_NetSessionDel(struct cli_state *cli, const char *workstation)
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetSessionDel_REQ) /* req string */
+ +1 /* no return string */
+ +RAP_MACHNAME_LEN /* workstation name */
+ +WORDSIZE]; /* reserved (0) */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WsessionDel, RAP_NetSessionDel_REQ, NULL);
+ PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1);
+ PUTWORD(p,0); /* reserved word of 0 */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */
+ NULL, 0, 200, /* data, length, maxlen */
+ &rparam, &rprcnt, /* return params, length */
+ &rdata, &rdrcnt)) /* return data, length */
+ {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+
+ if (res == 0) {
+ /* nothing to do */
+ } else {
+ DEBUG(4,("NetFileClose2 res=%d\n", res));
+ }
+ } else {
+ res = -1;
+ DEBUG(4,("NetFileClose2 failed\n"));
+ }
+
+ SAFE_FREE(rparam);
+ SAFE_FREE(rdata);
+
+ return res;
+}
+
+int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier,
+ void (*fn)(uint16_t conid, uint16_t contype,
+ uint16_t numopens, uint16_t numusers,
+ uint32_t contime, const char *username,
+ const char *netname))
+{
+ char param[WORDSIZE /* api number */
+ +sizeof(RAP_NetConnectionEnum_REQ) /* req string */
+ +sizeof(RAP_CONNECTION_INFO_L1) /* return string */
+ +RAP_MACHNAME_LEN /* wksta name */
+ +WORDSIZE /* info level */
+ +WORDSIZE]; /* buffer size */
+ char *p;
+ char *rparam = NULL;
+ char *rdata = NULL;
+ unsigned int rprcnt, rdrcnt;
+ int res = -1;
+
+ memset(param, '\0', sizeof(param));
+ p = make_header(param, RAP_WconnectionEnum,
+ RAP_NetConnectionEnum_REQ, RAP_CONNECTION_INFO_L1);
+ PUTSTRING(p, qualifier, RAP_MACHNAME_LEN-1);/* Workstation name */
+ PUTWORD(p,1); /* Info level 1 */
+ PUTWORD(p,0xFFE0); /* Return buffer size */
+
+ if (cli_api(cli,
+ param, PTR_DIFF(p,param),PTR_DIFF(p,param),
+ NULL, 0, CLI_BUFFER_SIZE,
+ &rparam, &rprcnt,
+ &rdata, &rdrcnt)) {
+ char *endp = rparam + rprcnt;
+ res = GETRES(rparam, endp);
+ cli->rap_error = res;
+ if (res != 0) {
+ DEBUG(1,("NetConnectionEnum gave error %d\n", res));
+ }
+ }
+
+ if (!rdata) {
+ DEBUG(4,("NetConnectionEnum no data returned\n"));
+ goto out;
+ }
+
+ if (res == 0 || res == ERRmoredata) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *endp = rparam + rprcnt;
+ int i, converter = 0, count = 0;
+
+ p = rparam + WORDSIZE;
+ GETWORD(p, converter, endp);
+ GETWORD(p, count, endp);
+
+ endp = rdata + rdrcnt;
+ for (i=0,p=rdata;i<count && p < endp;i++) {
+ char *netname, *username;
+ uint16_t conn_id = 0, conn_type = 0, num_opens = 0, num_users = 0;
+ unsigned int conn_time = 0;
+
+ GETWORD(p,conn_id, endp);
+ GETWORD(p,conn_type, endp);
+ GETWORD(p,num_opens, endp);
+ GETWORD(p,num_users, endp);
+ GETDWORD(p,conn_time, endp);
+ p += rap_getstringp(frame,
+ p,
+ &username,
+ rdata,
+ converter,
+ endp);
+ p += rap_getstringp(frame,
+ p,
+ &netname,
+ rdata,
+ converter,
+ endp);
+
+ if (username && netname) {
+ fn(conn_id, conn_type, num_opens, num_users, conn_time,
+ username, netname);
+ }
+ }
+ TALLOC_FREE(frame);
+ } else {
+ DEBUG(4,("NetConnectionEnum res=%d\n", res));
+ }
+
+ out:
+
+ SAFE_FREE(rdata);
+ SAFE_FREE(rparam);
+ return res;
+}
diff --git a/source3/utils/clirap2.h b/source3/utils/clirap2.h
new file mode 100644
index 0000000..275d491
--- /dev/null
+++ b/source3/utils/clirap2.h
@@ -0,0 +1,80 @@
+/*
+ Samba Unix/Linux SMB client library
+ More client RAP (SMB Remote Procedure Calls) functions
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2007 Jeremy Allison. jra@samba.org
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) James Peach 2007
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __UTILS_CLIRAP2_H__
+#define __UTILS_CLIRAP2_H__
+
+#include "../libsmb/clirap.h"
+
+struct rap_group_info_1;
+struct rap_user_info_1;
+struct rap_share_info_2;
+
+int cli_NetGroupDelete(struct cli_state *cli, const char *group_name);
+int cli_NetGroupAdd(struct cli_state *cli, struct rap_group_info_1 *grinfo);
+int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state);
+int cli_RNetGroupEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state);
+int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name);
+int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name);
+int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state );
+int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state );
+int cli_NetUserDelete(struct cli_state *cli, const char * user_name );
+int cli_NetUserAdd(struct cli_state *cli, struct rap_user_info_1 * userinfo );
+int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state);
+int cli_RNetUserEnum0(struct cli_state *cli,
+ void (*fn)(const char *, void *),
+ void *state);
+int cli_NetFileClose(struct cli_state *cli, uint32_t file_id );
+int cli_NetFileGetInfo(struct cli_state *cli, uint32_t file_id, void (*fn)(const char *, const char *, uint16_t, uint16_t, uint32_t));
+int cli_NetFileEnum(struct cli_state *cli, const char * user,
+ const char * base_path,
+ void (*fn)(const char *, const char *, uint16_t, uint16_t,
+ uint32_t));
+int cli_NetShareAdd(struct cli_state *cli, struct rap_share_info_2 * sinfo );
+int cli_NetShareDelete(struct cli_state *cli, const char * share_name );
+bool cli_get_pdc_name(struct cli_state *cli, const char *workgroup, char **pdc_name);
+bool cli_get_server_name(TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ char **servername);
+bool cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32_t stype);
+bool cli_NetWkstaUserLogoff(struct cli_state *cli, const char *user, const char *workstation);
+int cli_NetPrintQEnum(struct cli_state *cli,
+ void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t),
+ void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*));
+int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer,
+ void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t),
+ void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*));
+int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state);
+int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, char *));
+int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation,
+ void (*fn)(const char *, const char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, const char *));
+int cli_NetSessionDel(struct cli_state *cli, const char *workstation);
+int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier,
+ void (*fn)(uint16_t conid, uint16_t contype,
+ uint16_t numopens, uint16_t numusers,
+ uint32_t contime, const char *username,
+ const char *netname));
+
+#endif /* __UTILS_CLIRAP2_H__ */
diff --git a/source3/utils/conn_tdb.c b/source3/utils/conn_tdb.c
new file mode 100644
index 0000000..3724bd4
--- /dev/null
+++ b/source3/utils/conn_tdb.c
@@ -0,0 +1,173 @@
+/*
+ Unix SMB/CIFS implementation.
+ Low-level connections.tdb access functions
+ Copyright (C) Volker Lendecke 2007
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "smbd/globals.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "messages.h"
+#include "conn_tdb.h"
+#include "util_tdb.h"
+#include "lib/util/string_wrappers.h"
+
+struct connections_forall_state {
+ struct db_context *session_by_pid;
+ int (*fn)(const struct connections_data *data,
+ void *private_data);
+ void *private_data;
+ int count;
+};
+
+struct connections_forall_session {
+ uid_t uid;
+ gid_t gid;
+ fstring machine;
+ fstring addr;
+ uint16_t cipher;
+ uint16_t dialect;
+ uint16_t signing;
+ uint8_t signing_flags;
+};
+
+static int collect_sessions_fn(struct smbXsrv_session_global0 *global,
+ void *connections_forall_state)
+{
+ NTSTATUS status;
+ struct connections_forall_state *state =
+ (struct connections_forall_state*)connections_forall_state;
+
+ uint32_t id = global->session_global_id;
+ struct connections_forall_session sess;
+
+ if (global->auth_session_info == NULL) {
+ sess.uid = -1;
+ sess.gid = -1;
+ } else {
+ sess.uid = global->auth_session_info->unix_token->uid;
+ sess.gid = global->auth_session_info->unix_token->gid;
+ }
+ fstrcpy(sess.machine, global->channels[0].remote_name);
+ fstrcpy(sess.addr, global->channels[0].remote_address);
+ sess.cipher = global->channels[0].encryption_cipher;
+ sess.signing = global->channels[0].signing_algo;
+ sess.dialect = global->connection_dialect;
+ sess.signing_flags = global->signing_flags;
+
+ status = dbwrap_store(state->session_by_pid,
+ make_tdb_data((void*)&id, sizeof(id)),
+ make_tdb_data((void*)&sess, sizeof(sess)),
+ TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to store record: %s\n", nt_errstr(status)));
+ }
+ return 0;
+}
+
+static int traverse_tcon_fn(struct smbXsrv_tcon_global0 *global,
+ void *connections_forall_state)
+{
+ NTSTATUS status;
+ struct connections_forall_state *state =
+ (struct connections_forall_state*)connections_forall_state;
+
+ struct connections_data data;
+
+ uint32_t sess_id = global->session_global_id;
+ struct connections_forall_session sess = {
+ .uid = -1,
+ .gid = -1,
+ };
+
+ TDB_DATA val = tdb_null;
+
+ /*
+ * Note: that share_name is defined as array without a pointer.
+ * that's why it's always a valid pointer here.
+ */
+ if (strlen(global->share_name) == 0) {
+ /*
+ * when a smbXsrv_tcon is created it's created
+ * with empty share_name first in order to allocate
+ * an id, before filling in the details.
+ */
+ return 0;
+ }
+
+ status = dbwrap_fetch(state->session_by_pid, state,
+ make_tdb_data((void*)&sess_id, sizeof(sess_id)),
+ &val);
+ if (NT_STATUS_IS_OK(status)) {
+ memcpy((uint8_t *)&sess, val.dptr, val.dsize);
+ }
+
+ ZERO_STRUCT(data);
+
+ data.pid = global->server_id;
+ data.cnum = global->tcon_global_id;
+ data.sess_id = sess_id;
+ fstrcpy(data.servicename, global->share_name);
+ data.uid = sess.uid;
+ data.gid = sess.gid;
+ fstrcpy(data.addr, sess.addr);
+ fstrcpy(data.machine, sess.machine);
+ data.start = global->creation_time;
+ data.encryption_flags = global->encryption_flags;
+ data.cipher = sess.cipher;
+ data.dialect = sess.dialect;
+ data.signing = sess.signing;
+ data.signing_flags = global->signing_flags;
+
+ state->count++;
+
+ return state->fn(&data, state->private_data);
+}
+
+int connections_forall_read(int (*fn)(const struct connections_data *data,
+ void *private_data),
+ void *private_data)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct connections_forall_state *state =
+ talloc_zero(talloc_tos(), struct connections_forall_state);
+ NTSTATUS status;
+ int ret = -1;
+
+ state->session_by_pid = db_open_rbt(state);
+ state->fn = fn;
+ state->private_data = private_data;
+ status = smbXsrv_session_global_traverse(collect_sessions_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse sessions: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ status = smbXsrv_tcon_global_traverse(traverse_tcon_fn, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse tree connects: %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ ret = state->count;
+done:
+ talloc_free(frame);
+ return ret;
+}
diff --git a/source3/utils/conn_tdb.h b/source3/utils/conn_tdb.h
new file mode 100644
index 0000000..2a6e04e
--- /dev/null
+++ b/source3/utils/conn_tdb.h
@@ -0,0 +1,45 @@
+/*
+ Unix SMB/CIFS implementation.
+ Low-level connections.tdb access functions
+ Copyright (C) Volker Lendecke 2007
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* key and data in the connections database - used in smbstatus and smbd */
+
+#include "source3/include/includes.h"
+
+struct connections_data {
+ struct server_id pid;
+ int cnum;
+ uint32_t sess_id;
+ uid_t uid;
+ gid_t gid;
+ fstring servicename;
+ fstring addr;
+ fstring machine;
+ NTTIME start;
+ uint8_t encryption_flags;
+ uint16_t cipher;
+ uint16_t dialect;
+ uint8_t signing_flags;
+ uint16_t signing;
+};
+
+/* The following definitions come from lib/conn_tdb.c */
+
+int connections_forall_read(int (*fn)(const struct connections_data *data,
+ void *private_data),
+ void *private_data);
diff --git a/source3/utils/dbwrap_tool.c b/source3/utils/dbwrap_tool.c
new file mode 100644
index 0000000..eb97d64
--- /dev/null
+++ b/source3/utils/dbwrap_tool.c
@@ -0,0 +1,593 @@
+/*
+ Samba Unix/Linux CIFS implementation
+
+ low level TDB/CTDB tool using the dbwrap interface
+
+ Copyright (C) 2009 Michael Adam <obnox@samba.org>
+ Copyright (C) 2011 Bjoern Baumbach <bb@sernet.de>
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "messages.h"
+#include "util_tdb.h"
+#include "cmdline_contexts.h"
+#include "lib/param/param.h"
+
+enum dbwrap_op { OP_FETCH, OP_STORE, OP_DELETE, OP_ERASE, OP_LISTKEYS,
+ OP_EXISTS };
+
+enum dbwrap_type { TYPE_INT32, TYPE_UINT32, TYPE_STRING, TYPE_HEX, TYPE_NONE };
+
+static int dbwrap_tool_fetch_int32(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ int32_t value;
+ NTSTATUS status;
+
+ status = dbwrap_fetch_int32_bystring(db, keyname, &value);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Error fetching int32 from key '%s': %s\n",
+ keyname, nt_errstr(status));
+ return -1;
+ }
+ d_printf("%d\n", value);
+
+ return 0;
+}
+
+static int dbwrap_tool_fetch_uint32(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ uint32_t value;
+ NTSTATUS ret;
+
+ ret = dbwrap_fetch_uint32_bystring(db, keyname, &value);
+ if (NT_STATUS_IS_OK(ret)) {
+ d_printf("%u\n", value);
+ return 0;
+ } else {
+ d_fprintf(stderr, "ERROR: could not fetch uint32 key '%s': "
+ "%s\n", nt_errstr(ret), keyname);
+ return -1;
+ }
+}
+
+static int dbwrap_tool_fetch_string(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ TDB_DATA tdbdata;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ int ret;
+
+ status = dbwrap_fetch_bystring(db, tmp_ctx, keyname, &tdbdata);
+ if (NT_STATUS_IS_OK(status)) {
+ d_printf("%-*.*s\n", (int)tdbdata.dsize, (int)tdbdata.dsize,
+ tdbdata.dptr);
+ ret = 0;
+ } else {
+ d_fprintf(stderr, "ERROR: could not fetch string key '%s': "
+ "%s\n", nt_errstr(status), keyname);
+ ret = -1;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int dbwrap_tool_fetch_hex(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ TDB_DATA tdbdata;
+ DATA_BLOB datablob;
+ NTSTATUS status;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ char *hex_string;
+ int ret;
+
+ status = dbwrap_fetch_bystring(db, tmp_ctx, keyname, &tdbdata);
+ if (NT_STATUS_IS_OK(status)) {
+ datablob.data = tdbdata.dptr;
+ datablob.length = tdbdata.dsize;
+
+ hex_string = data_blob_hex_string_upper(tmp_ctx, &datablob);
+ if (hex_string == NULL) {
+ d_fprintf(stderr, "ERROR: could not get hex string "
+ "from data blob\n");
+ ret = -1;
+ } else {
+ d_printf("%s\n", hex_string);
+ ret = 0;
+ }
+ } else {
+ d_fprintf(stderr, "ERROR: could not fetch hex key '%s': "
+ "%s\n", nt_errstr(status), keyname);
+ ret = -1;
+ }
+
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+static int dbwrap_tool_store_int32(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+ int32_t value = (int32_t)strtol(data, NULL, 10);
+
+ if (dbwrap_is_persistent(db)) {
+ status = dbwrap_trans_store_int32_bystring(db, keyname, value);
+ } else {
+ status = dbwrap_store_int32_bystring(db, keyname, value);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "ERROR: could not store int32 key '%s': %s\n",
+ keyname, nt_errstr(status));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dbwrap_tool_store_uint32(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+ uint32_t value = (uint32_t)strtol(data, NULL, 10);
+
+ if (dbwrap_is_persistent(db)) {
+ status = dbwrap_trans_store_uint32_bystring(db, keyname, value);
+ } else {
+ status = dbwrap_store_uint32_bystring(db, keyname, value);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "ERROR: could not store uint32 key '%s': %s\n",
+ keyname, nt_errstr(status));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dbwrap_tool_store_string(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+ TDB_DATA tdbdata;
+
+ tdbdata = string_term_tdb_data(data);
+
+ if (dbwrap_is_persistent(db)) {
+ status = dbwrap_trans_store_bystring(db, keyname,
+ tdbdata,
+ TDB_REPLACE);
+ } else {
+ status = dbwrap_store_bystring(db, keyname,
+ tdbdata,
+ TDB_REPLACE);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "ERROR: could not store string key '%s': %s\n",
+ keyname, nt_errstr(status));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dbwrap_tool_store_hex(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+ DATA_BLOB datablob;
+ TDB_DATA tdbdata;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ datablob = strhex_to_data_blob(tmp_ctx, data);
+ if(strlen(data) > 0 && datablob.length == 0) {
+ d_fprintf(stderr,
+ "ERROR: could not convert hex string to data blob\n"
+ " Not a valid hex string?\n");
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ tdbdata.dptr = (unsigned char *)datablob.data;
+ tdbdata.dsize = datablob.length;
+
+ if (dbwrap_is_persistent(db)) {
+ status = dbwrap_trans_store_bystring(db, keyname,
+ tdbdata,
+ TDB_REPLACE);
+ } else {
+ status = dbwrap_store_bystring(db, keyname,
+ tdbdata,
+ TDB_REPLACE);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "ERROR: could not store string key '%s': %s\n",
+ keyname, nt_errstr(status));
+ talloc_free(tmp_ctx);
+ return -1;
+ }
+
+ talloc_free(tmp_ctx);
+ return 0;
+}
+
+static int dbwrap_tool_delete(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+
+ if (dbwrap_is_persistent(db)) {
+ status = dbwrap_trans_delete_bystring(db, keyname);
+ } else {
+ status = dbwrap_delete_bystring(db, keyname);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "ERROR deleting record %s : %s\n",
+ keyname, nt_errstr(status));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int dbwrap_tool_exists(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ bool result;
+
+ result = dbwrap_exists(db, string_term_tdb_data(keyname));
+
+ if (result) {
+ d_fprintf(stdout, "Key %s exists\n", keyname);
+ } else {
+ d_fprintf(stdout, "Key %s does not exist\n", keyname);
+ }
+
+ return (result)?0:1;
+}
+
+/**
+ * dbwrap_tool_erase: erase the whole data base
+ * the keyname argument is not used.
+ */
+static int dbwrap_tool_erase(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ int ret;
+
+ ret = dbwrap_wipe(db);
+
+ if (ret != 0) {
+ d_fprintf(stderr, "ERROR erasing the database\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int listkey_fn(struct db_record *rec, void *private_data)
+{
+ TDB_DATA key = dbwrap_record_get_key(rec);
+ size_t length = key.dsize;
+ unsigned char *p = (unsigned char *)key.dptr;
+
+ while (length--) {
+ if (isprint(*p) && !strchr("\"\\", *p)) {
+ d_printf("%c", *p);
+ } else {
+ d_printf("\\%02X", *p);
+ }
+ p++;
+ }
+
+ d_printf("\n");
+
+ return 0;
+}
+
+static int dbwrap_tool_listkeys(struct db_context *db,
+ const char *keyname,
+ const char *data)
+{
+ NTSTATUS status;
+
+ status = dbwrap_traverse_read(db, listkey_fn, NULL, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "ERROR listing db keys\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+struct dbwrap_op_dispatch_table {
+ enum dbwrap_op op;
+ enum dbwrap_type type;
+ int (*cmd)(struct db_context *db,
+ const char *keyname,
+ const char *data);
+};
+
+struct dbwrap_op_dispatch_table dispatch_table[] = {
+ { OP_FETCH, TYPE_INT32, dbwrap_tool_fetch_int32 },
+ { OP_FETCH, TYPE_UINT32, dbwrap_tool_fetch_uint32 },
+ { OP_FETCH, TYPE_STRING, dbwrap_tool_fetch_string },
+ { OP_FETCH, TYPE_HEX, dbwrap_tool_fetch_hex },
+ { OP_STORE, TYPE_INT32, dbwrap_tool_store_int32 },
+ { OP_STORE, TYPE_UINT32, dbwrap_tool_store_uint32 },
+ { OP_STORE, TYPE_STRING, dbwrap_tool_store_string },
+ { OP_STORE, TYPE_HEX, dbwrap_tool_store_hex },
+ { OP_DELETE, TYPE_INT32, dbwrap_tool_delete },
+ { OP_ERASE, TYPE_INT32, dbwrap_tool_erase },
+ { OP_LISTKEYS, TYPE_INT32, dbwrap_tool_listkeys },
+ { OP_EXISTS, TYPE_STRING, dbwrap_tool_exists },
+ { 0, 0, NULL },
+};
+
+int main(int argc, const char **argv)
+{
+ struct tevent_context *evt_ctx;
+ struct messaging_context *msg_ctx;
+ struct db_context *db;
+
+ uint16_t count;
+
+ const char *dbname;
+ const char *opname;
+ enum dbwrap_op op;
+ const char *keyname = "";
+ const char *keytype = "int32";
+ enum dbwrap_type type;
+ const char *valuestr = "0";
+ int persistent = 0;
+ int non_persistent = 0;
+ int tdb_flags = TDB_DEFAULT;
+
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+
+ int ret = 1;
+ bool ok;
+
+ struct poptOption popt_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ { "non-persistent", 0, POPT_ARG_NONE, &non_persistent, 0,
+ "treat the database as non-persistent "
+ "(CAVEAT: This mode might wipe your database!)",
+ NULL },
+ { "persistent", 0, POPT_ARG_NONE, &persistent, 0,
+ "treat the database as persistent",
+ NULL },
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ int opt;
+ const char **extra_argv;
+ int extra_argc = 0;
+ poptContext pc;
+
+ smb_init_locale();
+
+ setup_logging(argv[0], DEBUG_DEFAULT_STDERR);
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ popt_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (!ok) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ fprintf(stderr, "Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ goto done;
+ }
+ }
+
+ /* setup the remaining options for the main program to use */
+ extra_argv = poptGetArgs(pc);
+ if (extra_argv) {
+ extra_argv++;
+ while (extra_argv[extra_argc]) extra_argc++;
+ }
+
+ if ((extra_argc < 2) || (extra_argc > 5)) {
+ d_fprintf(stderr,
+ "USAGE: %s [options] <database> <op> [<key> [<type> "
+ "[<value>]]]\n"
+ " ops: fetch, store, delete, exists, "
+ "erase, listkeys\n"
+ " types: int32, uint32, string, hex\n",
+ argv[0]);
+ goto done;
+ }
+
+ if ((persistent + non_persistent) != 1) {
+ d_fprintf(stderr, "ERROR: you must specify exactly one "
+ "of --persistent and --non-persistent\n");
+ goto done;
+ }
+ if (non_persistent == 1) {
+ tdb_flags |= TDB_CLEAR_IF_FIRST;
+ }
+
+ dbname = extra_argv[0];
+ opname = extra_argv[1];
+
+ if (strcmp(opname, "store") == 0) {
+ if (extra_argc != 5) {
+ d_fprintf(stderr, "ERROR: operation 'store' requires "
+ "value argument\n");
+ goto done;
+ }
+ valuestr = extra_argv[4];
+ keytype = extra_argv[3];
+ keyname = extra_argv[2];
+ op = OP_STORE;
+ } else if (strcmp(opname, "fetch") == 0) {
+ if (extra_argc != 4) {
+ d_fprintf(stderr, "ERROR: operation 'fetch' requires "
+ "type but not value argument\n");
+ goto done;
+ }
+ op = OP_FETCH;
+ keytype = extra_argv[3];
+ keyname = extra_argv[2];
+ } else if (strcmp(opname, "delete") == 0) {
+ if (extra_argc != 3) {
+ d_fprintf(stderr, "ERROR: operation 'delete' does "
+ "not allow type nor value argument\n");
+ goto done;
+ }
+ keyname = extra_argv[2];
+ op = OP_DELETE;
+ } else if (strcmp(opname, "erase") == 0) {
+ if (extra_argc != 2) {
+ d_fprintf(stderr, "ERROR: operation 'erase' does "
+ "not take a key argument\n");
+ goto done;
+ }
+ op = OP_ERASE;
+ } else if (strcmp(opname, "listkeys") == 0) {
+ if (extra_argc != 2) {
+ d_fprintf(stderr, "ERROR: operation 'listkeys' does "
+ "not take a key argument\n");
+ goto done;
+ }
+ op = OP_LISTKEYS;
+ } else if (strcmp(opname, "exists") == 0) {
+ if (extra_argc != 3) {
+ d_fprintf(stderr, "ERROR: operation 'exists' does "
+ "not allow type nor value argument\n");
+ goto done;
+ }
+ keyname = extra_argv[2];
+ op = OP_EXISTS;
+ keytype = "string";
+ } else {
+ d_fprintf(stderr,
+ "ERROR: invalid op '%s' specified\n"
+ " supported ops: fetch, store, delete, exists, "
+ "erase, listkeys\n",
+ opname);
+ goto done;
+ }
+
+ if (strcmp(keytype, "int32") == 0) {
+ type = TYPE_INT32;
+ } else if (strcmp(keytype, "uint32") == 0) {
+ type = TYPE_UINT32;
+ } else if (strcmp(keytype, "string") == 0) {
+ type = TYPE_STRING;
+ } else if (strcmp(keytype, "hex") == 0) {
+ type = TYPE_HEX;
+ } else if (strcmp(keytype, "none") == 0) {
+ type = TYPE_NONE;
+ } else {
+ d_fprintf(stderr, "ERROR: invalid type '%s' specified.\n"
+ " supported types: int32, uint32, "
+ "string, hex, none\n",
+ keytype);
+ goto done;
+ }
+
+ evt_ctx = samba_tevent_context_init(mem_ctx);
+ if (evt_ctx == NULL) {
+ d_fprintf(stderr, "ERROR: could not init event context\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(mem_ctx, evt_ctx);
+ if (msg_ctx == NULL) {
+ d_fprintf(stderr, "ERROR: could not init messaging context\n");
+ goto done;
+ }
+
+ switch (op) {
+ case OP_FETCH:
+ case OP_STORE:
+ case OP_DELETE:
+ case OP_ERASE:
+ case OP_LISTKEYS:
+ case OP_EXISTS:
+ db = db_open(mem_ctx, dbname, 0, tdb_flags, O_RDWR | O_CREAT,
+ 0644, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ d_fprintf(stderr, "ERROR: could not open dbname\n");
+ goto done;
+ }
+ break;
+ default:
+ db = NULL;
+ break;
+ }
+
+ for (count = 0; dispatch_table[count].cmd != NULL; count++) {
+ if ((op == dispatch_table[count].op) &&
+ (type == dispatch_table[count].type))
+ {
+ ret = dispatch_table[count].cmd(db, keyname, valuestr);
+ break;
+ }
+ }
+
+done:
+ poptFreeContext(pc);
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
diff --git a/source3/utils/dbwrap_torture.c b/source3/utils/dbwrap_torture.c
new file mode 100644
index 0000000..ec33853
--- /dev/null
+++ b/source3/utils/dbwrap_torture.c
@@ -0,0 +1,366 @@
+/*
+ Samba Linux/Unix CIFS implementation
+
+ simple tool to test persistent databases
+
+ Copyright (C) Michael Adam 2009
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "messages.h"
+#include "lib/util/util_tdb.h"
+#include "lib/param/param.h"
+
+#if 0
+#include "lib/events/events.h"
+#include "system/filesys.h"
+#include "popt.h"
+#include "cmdline.h"
+
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#define DEFAULT_DB_NAME "transaction.tdb"
+
+static int timelimit = 10;
+static int torture_delay = 0;
+static int verbose = 0;
+static int no_trans = 0;
+static const char *db_name = DEFAULT_DB_NAME;
+
+
+static unsigned int pnn;
+
+static TDB_DATA old_data;
+
+static int success = true;
+
+static void print_counters(void)
+{
+ int i;
+ uint32_t *old_counters;
+
+ printf("[%4u] Counters: ", (unsigned int)getpid());
+ old_counters = (uint32_t *)old_data.dptr;
+ for (i=0; i < old_data.dsize/sizeof(uint32_t); i++) {
+ printf("%6u ", old_counters[i]);
+ }
+ printf("\n");
+}
+
+static void each_second(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t,
+ void *private_data)
+{
+ struct db_context *db = talloc_get_type(private_data, struct db_context);
+
+ print_counters();
+
+ tevent_add_timer(ev, db, timeval_current_ofs(1, 0), each_second, db);
+}
+
+static bool check_counters(struct db_context *db, TDB_DATA data)
+{
+ int i;
+ uint32_t *counters, *old_counters;
+
+ counters = (uint32_t *)data.dptr;
+ old_counters = (uint32_t *)old_data.dptr;
+
+ /* check that all the counters are monotonic increasing */
+ for (i=0; i < old_data.dsize/sizeof(uint32_t); i++) {
+ if (counters[i] < old_counters[i]) {
+ printf("[%4u] ERROR: counters has decreased for node %u From %u to %u\n",
+ (unsigned int)getpid(), i, old_counters[i], counters[i]);
+ success = false;
+ return false;
+ }
+ }
+
+ if (old_data.dsize != data.dsize) {
+ old_data.dsize = data.dsize;
+ old_data.dptr = (unsigned char*)talloc_realloc_size(db, old_data.dptr, old_data.dsize);
+ }
+
+ memcpy(old_data.dptr, data.dptr, data.dsize);
+ if (verbose) print_counters();
+
+ return true;
+}
+
+
+static void do_sleep(unsigned int sec)
+{
+ unsigned int i;
+
+ if (sec == 0) {
+ return;
+ }
+
+ for (i=0; i<sec; i++) {
+ if (verbose) printf(".");
+ sleep(1);
+ }
+
+ if (verbose) printf("\n");
+}
+
+static void test_store_records(struct db_context *db, struct tevent_context *ev)
+{
+ TDB_DATA key;
+ uint32_t *counters;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ struct timeval start;
+
+ key = string_term_tdb_data("testkey");
+
+ start = timeval_current();
+ while ((timelimit == 0) || (timeval_elapsed(&start) < timelimit)) {
+ struct db_record *rec;
+ TDB_DATA data;
+ TDB_DATA value;
+ int ret;
+ NTSTATUS status;
+
+ if (!no_trans) {
+ if (verbose) DEBUG(1, ("starting transaction\n"));
+ ret = dbwrap_transaction_start(db);
+ if (ret != 0) {
+ DEBUG(0, ("Failed to start transaction on node "
+ "%d\n", pnn));
+ goto fail;
+ }
+ if (verbose) DEBUG(1, ("transaction started\n"));
+ do_sleep(torture_delay);
+ }
+
+ if (verbose) DEBUG(1, ("calling fetch_lock\n"));
+ rec = dbwrap_fetch_locked(db, tmp_ctx, key);
+ if (rec == NULL) {
+ DEBUG(0, ("Failed to fetch record\n"));
+ goto fail;
+ }
+ if (verbose) DEBUG(1, ("fetched record ok\n"));
+ do_sleep(torture_delay);
+ value = dbwrap_record_get_value(rec);
+
+ data.dsize = MAX(value.dsize, sizeof(uint32_t) * (pnn+1));
+ data.dptr = (unsigned char *)talloc_zero_size(tmp_ctx,
+ data.dsize);
+ if (data.dptr == NULL) {
+ DEBUG(0, ("Failed to allocate data\n"));
+ goto fail;
+ }
+ memcpy(data.dptr, value.dptr, value.dsize);
+
+ counters = (uint32_t *)data.dptr;
+
+ /* bump our counter */
+ counters[pnn]++;
+
+ if (verbose) DEBUG(1, ("storing data\n"));
+ status = dbwrap_record_store(rec, data, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to store record\n"));
+ if (!no_trans) {
+ ret = dbwrap_transaction_cancel(db);
+ if (ret != 0) {
+ DEBUG(0, ("Error cancelling transaction.\n"));
+ }
+ }
+ goto fail;
+ }
+ talloc_free(rec);
+ if (verbose) DEBUG(1, ("stored data ok\n"));
+ do_sleep(torture_delay);
+
+ if (!no_trans) {
+ if (verbose) DEBUG(1, ("calling transaction_commit\n"));
+ ret = dbwrap_transaction_commit(db);
+ if (ret != 0) {
+ DEBUG(0, ("Failed to commit transaction\n"));
+ goto fail;
+ }
+ if (verbose) DEBUG(1, ("transaction committed\n"));
+ }
+
+ /* store the counters and verify that they are sane */
+ if (verbose || (pnn == 0)) {
+ if (!check_counters(db, data)) {
+ goto fail;
+ }
+ }
+ talloc_free(data.dptr);
+
+ do_sleep(torture_delay);
+ }
+
+ goto done;
+
+fail:
+ success = false;
+
+done:
+ talloc_free(tmp_ctx);
+ return;
+}
+
+/*
+ main program
+*/
+int main(int argc, const char *argv[])
+{
+ TALLOC_CTX *mem_ctx;
+ struct tevent_context *ev_ctx;
+ struct messaging_context *msg_ctx;
+ struct db_context *db;
+
+ int unsafe_writes = 0;
+ struct poptOption popt_options[] = {
+ POPT_AUTOHELP
+ POPT_COMMON_SAMBA
+ { "timelimit", 't', POPT_ARG_INT, &timelimit, 0, "timelimit", "INTEGER" },
+ { "delay", 'D', POPT_ARG_INT, &torture_delay, 0, "delay (in seconds) between operations", "INTEGER" },
+ { "verbose", 'v', POPT_ARG_NONE, &verbose, 0, "switch on verbose mode", NULL },
+ { "db-name", 'N', POPT_ARG_STRING, &db_name, 0, "name of the test db", "NAME" },
+ { "no-trans", 'n', POPT_ARG_NONE, &no_trans, 0, "use fetch_lock/record store instead of transactions", NULL },
+ { "unsafe-writes", 'u', POPT_ARG_NONE, &unsafe_writes, 0, "do not use tdb transactions when writing", NULL },
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ int opt;
+ const char **extra_argv;
+ int extra_argc = 0;
+ poptContext pc;
+ int tdb_flags;
+ bool ok;
+ int ret = 1;
+ struct loadparm_context *lp_ctx = NULL;
+
+ mem_ctx = talloc_stackframe();
+
+ if (verbose) {
+ setbuf(stdout, (char *)NULL); /* don't buffer */
+ } else {
+ setlinebuf(stdout);
+ }
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ popt_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ default:
+ fprintf(stderr, "Invalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ goto done;
+ }
+ }
+
+ /* setup the remaining options for the main program to use */
+ extra_argv = poptGetArgs(pc);
+ if (extra_argv) {
+ extra_argv++;
+ while (extra_argv[extra_argc]) extra_argc++;
+ }
+
+ ev_ctx = samba_tevent_context_init(mem_ctx);
+ if (ev_ctx == NULL) {
+ d_fprintf(stderr, "ERROR: could not init event context\n");
+ goto done;
+ }
+
+ msg_ctx = messaging_init(mem_ctx, ev_ctx);
+ if (msg_ctx == NULL) {
+ d_fprintf(stderr, "ERROR: could not init messaging context\n");
+ goto done;
+ }
+
+ if (unsafe_writes == 1) {
+ tdb_flags = TDB_NOSYNC;
+ } else {
+ tdb_flags = TDB_DEFAULT;
+ }
+
+ if (no_trans) {
+ tdb_flags |= TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH;
+ }
+
+ db = db_open(mem_ctx, db_name, 0, tdb_flags, O_RDWR | O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+
+ if (db == NULL) {
+ d_fprintf(stderr, "failed to open db '%s': %s\n", db_name,
+ strerror(errno));
+ goto done;
+ }
+
+ if (get_my_vnn() == NONCLUSTER_VNN) {
+ set_my_vnn(0);
+ }
+ pnn = get_my_vnn();
+
+ printf("Starting test on node %u. running for %u seconds. "
+ "sleep delay: %u seconds.\n", pnn, timelimit, torture_delay);
+
+ if (!verbose && (pnn == 0)) {
+ tevent_add_timer(ev_ctx, db, timeval_current_ofs(1, 0), each_second, db);
+ }
+
+ test_store_records(db, ev_ctx);
+
+ if (verbose || (pnn == 0)) {
+ if (success != true) {
+ printf("The test FAILED\n");
+ ret = 2;
+ } else {
+ printf("SUCCESS!\n");
+ ret = 0;
+ }
+ }
+
+done:
+ poptFreeContext(pc);
+ talloc_free(mem_ctx);
+ return ret;
+}
diff --git a/source3/utils/destroy_netlogon_creds_cli.c b/source3/utils/destroy_netlogon_creds_cli.c
new file mode 100644
index 0000000..a2e1952
--- /dev/null
+++ b/source3/utils/destroy_netlogon_creds_cli.c
@@ -0,0 +1,136 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Garble the netlogon_creds_cli key for testing purposes
+ * Copyright (C) Volker Lendecke 2018
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <talloc.h>
+#include <tevent.h>
+#include "messages.h"
+#include "lib/util/talloc_stack.h"
+#include "lib/param/loadparm.h"
+#include "lib/param/param.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+
+int main(int argc, const char *argv[])
+{
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct tevent_context *ev;
+ struct messaging_context *msg_ctx;
+ struct loadparm_context *lp_ctx;
+ struct db_context *global_db;
+ struct netlogon_creds_cli_context *ctx;
+ struct netlogon_creds_CredentialState *creds;
+ NTSTATUS status;
+ int ret = 1;
+
+ smb_init_locale();
+
+ if (!lp_load_global(get_dyn_CONFIGFILE())) {
+ fprintf(stderr, "error opening config file %s. Error was %s\n",
+ get_dyn_CONFIGFILE(), strerror(errno));
+ goto done;
+ }
+
+ if (argc != 4) {
+ fprintf(stderr, "usage: %s cli_computer domain dc\n", argv[0]);
+ goto done;
+ }
+
+ lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ fprintf(stderr, "loadparm_init_s3 failed\n");
+ goto done;
+ }
+
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ fprintf(stderr, "samba3_tevent_context_init failed\n");
+ goto done;
+ }
+ msg_ctx = messaging_init(mem_ctx, ev);
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ goto done;
+ }
+
+ global_db = db_open(
+ mem_ctx,
+ lpcfg_private_db_path(mem_ctx, lp_ctx, "netlogon_creds_cli"),
+ 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH,
+ O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_2,
+ DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS);
+ if (global_db == NULL) {
+ fprintf(stderr, "db_open failed\n");
+ goto done;
+ }
+
+ status = netlogon_creds_cli_set_global_db(lp_ctx, &global_db);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "netlogon_creds_cli_set_global_db failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = netlogon_creds_cli_context_global(
+ lp_ctx,
+ msg_ctx,
+ talloc_asprintf(mem_ctx, "%s$", argv[1]),
+ SEC_CHAN_WKSTA,
+ argv[3],
+ argv[2],
+ "",
+ mem_ctx,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "netlogon_creds_cli_context_global failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = netlogon_creds_cli_lock(ctx,
+ mem_ctx,
+ &creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "netlogon_creds_cli_get failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ creds->session_key[0]++;
+
+ status = netlogon_creds_cli_store(ctx, creds);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "netlogon_creds_cli_store failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ TALLOC_FREE(creds);
+
+ ret = 0;
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
diff --git a/source3/utils/eventlogadm.c b/source3/utils/eventlogadm.c
new file mode 100644
index 0000000..f831927
--- /dev/null
+++ b/source3/utils/eventlogadm.c
@@ -0,0 +1,506 @@
+
+/*
+ * Samba Unix/Linux SMB client utility
+ * Write Eventlog records to a tdb, perform other eventlog related functions
+ *
+ *
+ * Copyright (C) Brian Moran 2005.
+ * Copyright (C) Guenther Deschner 2009.
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "includes.h"
+#include "lib/eventlog/eventlog.h"
+#include "registry.h"
+#include "registry/reg_api.h"
+#include "registry/reg_init_basic.h"
+#include "registry/reg_util_token.h"
+#include "registry/reg_backend_db.h"
+#include "../libcli/registry/util_reg.h"
+#include "cmdline_contexts.h"
+#include "lib/util/string_wrappers.h"
+
+extern int optind;
+extern char *optarg;
+
+int opt_debug = 0;
+
+static void usage( char *s )
+{
+ printf( "\nUsage: %s [OPTION]\n\n", s );
+ printf( " -o write <Eventlog Name> \t\t\t\t\tWrites records to eventlog from STDIN\n" );
+ printf( " -o addsource <EventlogName> <sourcename> <msgfileDLLname> \tAdds the specified source & DLL eventlog registry entry\n" );
+ printf( " -o dump <Eventlog Name> <starting_record>\t\t\t\t\tDump stored eventlog entries on STDOUT\n" );
+ printf( "\nMiscellaneous options:\n" );
+ printf( " -s <filename>\t\t\t\t\t\t\tUse configuration file <filename>.\n");
+ printf( " -d\t\t\t\t\t\t\t\tturn debug on\n" );
+ printf( " -h\t\t\t\t\t\t\t\tdisplay help\n\n" );
+}
+
+static void display_eventlog_names( void )
+{
+ const char **elogs;
+ int i;
+
+ elogs = lp_eventlog_list( );
+ printf( "Active eventlog names:\n" );
+ printf( "--------------------------------------\n" );
+ if ( elogs ) {
+ for ( i = 0; elogs[i]; i++ ) {
+ printf( "\t%s\n", elogs[i] );
+ }
+ }
+ else
+ printf( "\t<None specified>\n");
+}
+
+/*********************************************************************
+ for an eventlog, add in a source name. If the eventlog doesn't
+ exist (not in the list) do nothing. If a source for the log
+ already exists, change the information (remove, replace)
+*********************************************************************/
+static bool eventlog_add_source( const char *eventlog, const char *sourcename,
+ const char *messagefile )
+{
+ /* Find all of the eventlogs, add keys for each of them */
+ /* need to add to the value KEY_EVENTLOG/<eventlog>/Sources string (Creating if necessary)
+ need to add KEY of source to KEY_EVENTLOG/<eventlog>/<source> */
+
+ const char **elogs = lp_eventlog_list( );
+ const char **wrklist, **wp;
+ char *evtlogpath = NULL;
+ int ii = 0;
+ bool already_in;
+ int i;
+ int numsources = 0;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ WERROR werr;
+ struct registry_key *key_hive, *key_eventlog, *key_source;
+ struct security_token *token = NULL;
+ const char *hive_name, *relpath;
+ enum winreg_CreateAction action;
+ struct registry_value *value;
+ static const uint32_t ACCESS = REG_KEY_READ | REG_KEY_WRITE;
+ bool ret = false;
+
+ if (!elogs) {
+ d_printf("No Eventlogs configured\n");
+ goto done;
+ }
+
+ for ( i = 0; elogs[i]; i++ ) {
+ if ( strequal( elogs[i], eventlog ) )
+ break;
+ }
+
+ if ( !elogs[i] ) {
+ d_printf("Eventlog [%s] not found in list of valid event logs\n",
+ eventlog);
+ goto done;
+ }
+
+ /* have to assume that the evenlog key itself exists at this point */
+ /* add in a key of [sourcename] under the eventlog key */
+
+ /* todo add to Sources */
+
+ evtlogpath = talloc_asprintf(ctx, "%s\\%s", KEY_EVENTLOG, eventlog);
+ if (!evtlogpath) {
+ d_printf("Out of memory\n");
+ goto done;
+ }
+
+ relpath = evtlogpath + sizeof(KEY_EVENTLOG);
+ hive_name = talloc_strndup(ctx, evtlogpath, relpath - evtlogpath);
+ if (!hive_name) {
+ d_printf("Out of memory\n");
+ goto done;
+ }
+ relpath++;
+
+ werr = ntstatus_to_werror(registry_create_admin_token(ctx, &token));
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to create admin token: %s\n", win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_openhive(ctx, hive_name, ACCESS, token, &key_hive);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to open hive [%s]: %s\n", hive_name, win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_openkey(ctx, key_hive, relpath, ACCESS, &key_eventlog);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to open key [%s]: %s\n", evtlogpath, win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_queryvalue(ctx, key_eventlog, "Sources", &value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to get value \"Sources\" for [%s]: %s\n", evtlogpath, win_errstr(werr));
+ goto done;
+ }
+ /* perhaps this adding a new string to a multi_sz should be a fn? */
+ /* check to see if it's there already */
+
+ if ( value->type != REG_MULTI_SZ ) {
+ d_printf("Wrong type for \"Sources\", should be REG_MULTI_SZ\n");
+ goto done;
+ }
+ /* convert to a 'regulah' chars to do some comparisons */
+
+ already_in = false;
+ wrklist = NULL;
+ dump_data(1, value->data.data, value->data.length);
+
+ if (!pull_reg_multi_sz(ctx, &value->data, &wrklist)) {
+ d_printf("Failed to pull REG_MULTI_SZ from \"Sources\"\n");
+ goto done;
+ }
+
+ for (ii=0; wrklist[ii]; ii++) {
+ numsources++;
+ }
+
+ if (numsources > 0) {
+ /* see if it's in there already */
+ wp = wrklist;
+
+ while (wp && *wp ) {
+ if ( strequal( *wp, sourcename ) ) {
+ d_printf("Source name [%s] already in list for [%s] \n",
+ sourcename, eventlog);
+ already_in = true;
+ break;
+ }
+ wp++;
+ }
+ } else {
+ d_printf("Nothing in the sources list, this might be a problem\n");
+ }
+
+ if ( !already_in ) {
+ /* make a new list with an additional entry; copy values, add another */
+ wp = talloc_realloc(ctx, wrklist, const char *, numsources + 2 );
+ if ( !wp ) {
+ d_printf("Out of memory\n");
+ goto done;
+ }
+
+ wp[numsources] = sourcename;
+ wp[numsources+1] = NULL;
+ if (!push_reg_multi_sz(ctx, &value->data, wp)) {
+ d_printf("Failed to push Sources\n");
+ goto done;
+ }
+ dump_data( 1, value->data.data, value->data.length);
+ werr = reg_setvalue(key_eventlog, "Sources", value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to set value Sources: %s\n", win_errstr(werr));
+ goto done;
+ }
+ } else {
+ d_printf("Source name [%s] found in existing list of sources\n",
+ sourcename);
+ }
+
+ werr = reg_createkey(ctx, key_eventlog, sourcename, ACCESS, &key_source, &action);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to create subkey \"%s\" of \"%s\": %s\n", sourcename, evtlogpath, win_errstr(werr));
+ goto done;
+ }
+
+ if (action == REG_CREATED_NEW_KEY) {
+ d_printf(" Source name [%s] for eventlog [%s] didn't exist, adding \n",
+ sourcename, eventlog);
+ }
+
+ /* at this point KEY_EVENTLOG/<eventlog>/<sourcename> key is in there. Now need to add EventMessageFile */
+
+ /* now add the values to the KEY_EVENTLOG/Application form key */
+ d_printf("Storing EventMessageFile [%s] to eventlog path of [%s]\n",
+ messagefile, evtlogpath);
+
+ if (!push_reg_sz(ctx, &value->data, messagefile)) {
+ d_printf("Failed to push \"EventMessageFile\"\n");
+ goto done;
+ }
+ value->type = REG_SZ;
+
+ werr = reg_setvalue(key_source, "EventMessageFile", value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to set value \"EventMessageFile\": %s\n", win_errstr(werr));
+ return false;
+ }
+ ret = true;
+done:
+ talloc_free(ctx);
+ return ret;
+}
+
+static int DoAddSourceCommand( int argc, char **argv, bool debugflag, char *exename )
+{
+ WERROR werr;
+
+ if ( argc < 3 ) {
+ printf( "need more arguments:\n" );
+ printf( "-o addsource EventlogName SourceName /path/to/EventMessageFile.dll\n" );
+ return -1;
+ }
+
+ /* must open the registry before we access it */
+ werr = registry_init_common();
+ if (!W_ERROR_IS_OK(werr)) {
+ printf("Can't open the registry: %s.\n", win_errstr(werr));
+ return -1;
+ }
+ werr = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(werr)) {
+ printf("Can't start transaction on registry: %s.\n", win_errstr(werr));
+ return -1;
+ }
+
+ if ( !eventlog_add_source( argv[0], argv[1], argv[2] ) ) {
+ regdb_transaction_cancel();
+ return -2;
+ }
+ werr = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(werr)) {
+ printf("Failed to commit transaction on registry: %s.\n", win_errstr(werr));
+ return -1;
+ }
+ return 0;
+}
+
+static int DoWriteCommand( int argc, char **argv, bool debugflag, char *exename )
+{
+ FILE *f1;
+ char *argfname;
+ ELOG_TDB *etdb;
+ NTSTATUS status;
+
+ /* fixed constants are bad bad bad */
+ char linein[1024];
+ bool is_eor;
+ struct eventlog_Record_tdb ee;
+ uint32_t record_number = 0;
+ TALLOC_CTX *mem_ctx = talloc_tos();
+
+ f1 = stdin;
+ if ( !f1 ) {
+ printf( "Can't open STDIN\n" );
+ return -1;
+ }
+
+ if ( debugflag ) {
+ printf( "Starting write for eventlog [%s]\n", argv[0] );
+ display_eventlog_names( );
+ }
+
+ argfname = argv[0];
+
+ if ( !( etdb = elog_open_tdb( argfname, False, False ) ) ) {
+ printf( "can't open the eventlog TDB (%s)\n", argfname );
+ return -1;
+ }
+
+ ZERO_STRUCT( ee ); /* MUST initialize between records */
+
+ while ( !feof( f1 ) ) {
+ if (fgets( linein, sizeof( linein ) - 1, f1 ) == NULL) {
+ break;
+ }
+ if ((strlen(linein) > 0)
+ && (linein[strlen(linein)-1] == '\n')) {
+ linein[strlen(linein)-1] = 0;
+ }
+
+ if ( debugflag )
+ printf( "Read line [%s]\n", linein );
+
+ is_eor = False;
+
+
+ parse_logentry( mem_ctx, ( char * ) &linein, &ee, &is_eor );
+ /* should we do something with the return code? */
+
+ if ( is_eor ) {
+ fixup_eventlog_record_tdb( &ee );
+
+ if ( opt_debug )
+ printf( "record number [%d], tg [%d] , tw [%d]\n",
+ ee.record_number, (int)ee.time_generated, (int)ee.time_written );
+
+ if ( ee.time_generated != 0 ) {
+
+ /* printf("Writing to the event log\n"); */
+
+ status = evlog_push_record_tdb( mem_ctx, ELOG_TDB_CTX(etdb),
+ &ee, &record_number );
+ if ( !NT_STATUS_IS_OK(status) ) {
+ printf( "Can't write to the event log: %s\n",
+ nt_errstr(status) );
+ } else {
+ if ( opt_debug )
+ printf( "Wrote record %d\n",
+ record_number );
+ }
+ } else {
+ if ( opt_debug )
+ printf( "<null record>\n" );
+ }
+ ZERO_STRUCT( ee ); /* MUST initialize between records */
+ }
+ }
+
+ elog_close_tdb( etdb , False );
+
+ return 0;
+}
+
+static int DoDumpCommand(int argc, char **argv, bool debugflag, char *exename)
+{
+ ELOG_TDB *etdb;
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ uint32_t count = 1;
+
+ if (argc > 2) {
+ return -1;
+ }
+
+ if (argc > 1) {
+ count = atoi(argv[1]);
+ }
+
+ etdb = elog_open_tdb(argv[0], false, true);
+ if (!etdb) {
+ printf("can't open the eventlog TDB (%s)\n", argv[0]);
+ return -1;
+ }
+
+ while (1) {
+
+ struct eventlog_Record_tdb *r;
+ char *s;
+
+ r = evlog_pull_record_tdb(mem_ctx, etdb->tdb, count);
+ if (!r) {
+ break;
+ }
+
+ printf("displaying record: %d\n", count);
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, eventlog_Record_tdb, r);
+ if (s) {
+ printf("%s\n", s);
+ talloc_free(s);
+ }
+ count++;
+ }
+
+ elog_close_tdb(etdb, false);
+
+ return 0;
+}
+
+/* would be nice to use the popT stuff here, however doing so forces us to drag in a lot of other infrastructure */
+
+int main( int argc, char *argv[] )
+{
+ int opt, rc;
+ char *exename;
+ char *configfile = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+
+ fstring opname;
+
+ smb_init_locale();
+
+ opt_debug = 0; /* todo set this from getopts */
+
+ exename = argv[0];
+
+ /* default */
+
+ fstrcpy( opname, "write" ); /* the default */
+
+#if 0 /* TESTING CODE */
+ eventlog_add_source( "System", "TestSourceX", "SomeTestPathX" );
+#endif
+ while ( ( opt = getopt( argc, argv, "dho:s:" ) ) != EOF ) {
+ switch ( opt ) {
+
+ case 'o':
+ fstrcpy( opname, optarg );
+ break;
+
+ case 'h':
+ usage( exename );
+ display_eventlog_names( );
+ exit( 0 );
+ break;
+
+ case 'd':
+ opt_debug = 1;
+ break;
+ case 's':
+ configfile = talloc_strdup(frame, optarg);
+ break;
+
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ( argc < 1 ) {
+ printf( "\nNot enough arguments!\n" );
+ usage( exename );
+ exit( 1 );
+ }
+
+ if ( configfile == NULL ) {
+ lp_load_global(get_dyn_CONFIGFILE());
+ } else if (!lp_load_global(configfile)) {
+ printf("Unable to parse configfile '%s'\n",configfile);
+ exit( 1 );
+ }
+
+ /* note that the separate command types should call usage if they need to... */
+ while ( 1 ) {
+ if ( !strcasecmp_m( opname, "addsource" ) ) {
+ rc = DoAddSourceCommand( argc, argv, opt_debug,
+ exename );
+ break;
+ }
+ if ( !strcasecmp_m( opname, "write" ) ) {
+ rc = DoWriteCommand( argc, argv, opt_debug, exename );
+ break;
+ }
+ if ( !strcasecmp_m( opname, "dump" ) ) {
+ rc = DoDumpCommand( argc, argv, opt_debug, exename );
+ break;
+ }
+ printf( "unknown command [%s]\n", opname );
+ usage( exename );
+ exit( 1 );
+ break;
+ }
+ TALLOC_FREE(frame);
+ return rc;
+}
diff --git a/source3/utils/interact.c b/source3/utils/interact.c
new file mode 100644
index 0000000..f8fed6d
--- /dev/null
+++ b/source3/utils/interact.c
@@ -0,0 +1,135 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Functions to interact with an user.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Aug 2011
+ *
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+
+#include "interact.h"
+
+#include <termios.h>
+
+static const char* get_editor(void) {
+ static char editor[64] = {0};
+
+ if (editor[0] == '\0') {
+ const char *tmp = getenv("VISUAL");
+ if (tmp == NULL) {
+ tmp = getenv("EDITOR");
+ }
+ if (tmp == NULL) {
+ tmp = "vi";
+ }
+ snprintf(editor, sizeof(editor), "%s", tmp);
+ }
+
+ return editor;
+}
+
+int interact_prompt(const char* msg, const char* acc, char def) {
+ struct termios old_tio, new_tio;
+ int c;
+
+ tcgetattr(STDIN_FILENO, &old_tio);
+ new_tio=old_tio;
+ new_tio.c_lflag &=(~ICANON & ~ECHO);
+ tcsetattr(STDIN_FILENO, TCSANOW, &new_tio);
+
+ do {
+ d_printf("%s? [%c]\n", msg, def);
+ fflush(stdout);
+ c = getchar();
+ if (c == '\n') {
+ c = def;
+ break;
+ }
+ else if (strchr(acc, tolower(c)) != NULL) {
+ break;
+ }
+ d_printf("Invalid input '%c'\n", c);
+ } while(c != EOF);
+ tcsetattr(STDIN_FILENO, TCSANOW, &old_tio);
+ return c;
+}
+
+
+char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) {
+ char fname[] = "/tmp/net_idmap_check.XXXXXX";
+ char buf[128];
+ char* ret = NULL;
+ FILE* file;
+ mode_t mask;
+ int fd;
+
+ mask = umask(S_IRWXO | S_IRWXG);
+ fd = mkstemp(fname);
+ umask(mask);
+ if (fd == -1) {
+ DEBUG(0, ("failed to mkstemp %s: %s\n", fname,
+ strerror(errno)));
+ return NULL;
+ }
+
+ file = fdopen(fd, "w");
+ if (!file) {
+ DEBUG(0, ("failed to open %s for writing: %s\n", fname,
+ strerror(errno)));
+ close(fd);
+ unlink(fname);
+ return NULL;
+ }
+
+ fprintf(file, "%s", str);
+ fclose(file);
+
+ snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname);
+ if (system(buf) != 0) {
+ DEBUG(0, ("failed to start editor %s: %s\n", buf,
+ strerror(errno)));
+ unlink(fname);
+ return NULL;
+ }
+
+ file = fopen(fname, "r");
+ if (!file) {
+ DEBUG(0, ("failed to open %s for reading: %s\n", fname,
+ strerror(errno)));
+ unlink(fname);
+ return NULL;
+ }
+ while ( fgets(buf, sizeof(buf), file) ) {
+ ret = talloc_strdup_append(ret, buf);
+ }
+ fclose(file);
+ unlink(fname);
+
+ return talloc_steal(mem_ctx, ret);
+}
+
+
+
+/*Local Variables:*/
+/*mode: c*/
+/*End:*/
diff --git a/source3/utils/interact.h b/source3/utils/interact.h
new file mode 100644
index 0000000..c719d09
--- /dev/null
+++ b/source3/utils/interact.h
@@ -0,0 +1,36 @@
+/* * Samba Unix/Linux SMB client library
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Functions to interact with an user.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Aug 2011
+ */
+
+#ifndef __INTERACT_H
+#define __INTERACT_H
+#include <talloc.h>
+
+char* interact_edit(TALLOC_CTX* mem_ctx, const char* str);
+int interact_prompt(const char* msg, const char* accept, char def);
+
+
+
+#endif /* __INTERACT_H */
+
+/*Local Variables:*/
+/*mode: c++*/
+/*End:*/
diff --git a/source3/utils/log2pcaphex.c b/source3/utils/log2pcaphex.c
new file mode 100644
index 0000000..8113d8e
--- /dev/null
+++ b/source3/utils/log2pcaphex.c
@@ -0,0 +1,408 @@
+/*
+ Unix SMB/CIFS implementation.
+ Utility to extract pcap files from samba (log level 10) log files
+
+ Copyright (C) Jelmer Vernooij 2003
+ Thanks to Tim Potter for the genial idea
+
+ Portions (from capconvert.c) (C) Andrew Tridgell 1997
+ Portions (from text2pcap.c) (C) Ashok Narayanan 2001
+
+ Example:
+ Output NBSS(SMB) packets in hex and convert to pcap adding
+ Eth/IP/TCP headers
+
+ log2pcap -h < samba.log | text2pcap -T 139,139 - samba.pcap
+
+ Output directly to pcap format without Eth headers or TCP
+ sequence numbers
+
+ log2pcap samba.log samba.pcap
+
+ TODO:
+ - Hex to text2pcap outputs are not properly parsed in Wireshark
+ the NBSS or SMB level. This is a bug.
+ - Writing directly to pcap format doesn't include sequence numbers
+ in the TCP packets
+ - Check if a packet is a response or request and set IP to/from
+ addresses accordingly. Currently all packets come from the same
+ dummy IP and go to the same dummy IP
+ - Add a message when done parsing about the number of pacekts
+ processed
+ - Parse NBSS packet header data from log file
+ - Have correct IP and TCP checksums.
+
+ Warning:
+ Samba log level 10 outputs a max of 512 bytes from the packet data
+ section. Packets larger than this will be truncated.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include <popt.h>
+
+/* We don't care about the paranoid malloc checker in this standalone
+ program */
+#undef malloc
+
+#include <assert.h>
+
+int quiet = 0;
+int hexformat = 0;
+
+#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa))
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+/* tcpdump file format */
+struct tcpdump_file_header {
+ uint32_t magic;
+ uint16_t major;
+ uint16_t minor;
+ int32_t zone;
+ uint32_t sigfigs;
+ uint32_t snaplen;
+ uint32_t linktype;
+};
+
+struct tcpdump_packet {
+ struct timeval ts;
+ uint32_t caplen;
+ uint32_t len;
+};
+
+typedef struct {
+ uint8_t ver_hdrlen;
+ uint8_t dscp;
+ uint16_t packet_length;
+ uint16_t identification;
+ uint8_t flags;
+ uint8_t fragment;
+ uint8_t ttl;
+ uint8_t protocol;
+ uint16_t hdr_checksum;
+ uint32_t src_addr;
+ uint32_t dest_addr;
+} hdr_ip_t;
+
+static hdr_ip_t HDR_IP = {0x45, 0, 0, 0x3412, 0, 0, 0xff, 6, 0, 0x01010101, 0x02020202};
+
+typedef struct {
+ uint16_t source_port;
+ uint16_t dest_port;
+ uint32_t seq_num;
+ uint32_t ack_num;
+ uint8_t hdr_length;
+ uint8_t flags;
+ uint16_t window;
+ uint16_t checksum;
+ uint16_t urg;
+} hdr_tcp_t;
+
+static hdr_tcp_t HDR_TCP = {139, 139, 0, 0, 0x50, 0, 0, 0, 0};
+
+static void print_pcap_header(FILE *out)
+{
+ struct tcpdump_file_header h;
+ h.magic = TCPDUMP_MAGIC;
+ h.major = 2;
+ h.minor = 4;
+ h.zone = 0;
+ h.sigfigs = 0;
+ h.snaplen = 102400; /* As long packets as possible */
+ h.linktype = 101; /* Raw IP */
+ fwrite(&h, sizeof(struct tcpdump_file_header), 1, out);
+}
+
+static void print_pcap_packet(FILE *out, unsigned char *data, long length,
+ long caplen)
+{
+ struct tcpdump_packet p;
+ p.ts.tv_usec = 0;
+ p.ts.tv_sec = 0;
+ p.caplen = caplen;
+ p.len = length;
+ fwrite(&p, sizeof(struct tcpdump_packet), 1, out);
+ fwrite(data, sizeof(unsigned char), caplen, out);
+}
+
+static void print_hex_packet(FILE *out, unsigned char *data, long length)
+{
+ long i,cur = 0;
+ while(cur < length) {
+ fprintf(out, "%06lX ", cur);
+ for(i = cur; i < length && i < cur + 16; i++) {
+ fprintf(out, "%02x ", data[i]);
+ }
+ cur = i;
+ fprintf(out, "\n");
+ }
+}
+
+static void print_netbios_packet(FILE *out, unsigned char *data, long length,
+ long actual_length)
+{
+ unsigned char *newdata; long offset = 0;
+ long newlen;
+
+ newlen = length+sizeof(HDR_IP)+sizeof(HDR_TCP);
+ newdata = (unsigned char *)malloc(newlen);
+
+ HDR_IP.packet_length = htons(newlen);
+ HDR_TCP.window = htons(0x2000);
+ HDR_TCP.source_port = HDR_TCP.dest_port = htons(139);
+
+ memcpy(newdata+offset, &HDR_IP, sizeof(HDR_IP));offset+=sizeof(HDR_IP);
+ memcpy(newdata+offset, &HDR_TCP, sizeof(HDR_TCP));offset+=sizeof(HDR_TCP);
+ memcpy(newdata+offset,data,length);
+
+ print_pcap_packet(out, newdata, newlen, actual_length+offset);
+ free(newdata);
+}
+
+unsigned char *curpacket = NULL;
+unsigned short curpacket_len = 0;
+long line_num = 0;
+
+/* Read the log message produced by lib/util.c:show_msg() containing the:
+ * SMB_HEADER
+ * SMB_PARAMETERS
+ * SMB_DATA.ByteCount
+ *
+ * Example:
+ * [2007/04/08 20:41:39, 5] lib/util.c:show_msg(516)
+ * size=144
+ * smb_com=0x73
+ * smb_rcls=0
+ * smb_reh=0
+ * smb_err=0
+ * smb_flg=136
+ * smb_flg2=49153
+ * smb_tid=1
+ * smb_pid=65279
+ * smb_uid=0
+ * smb_mid=64
+ * smt_wct=3
+ * smb_vwv[ 0]= 117 (0x75)
+ * smb_vwv[ 1]= 128 (0x80)
+ * smb_vwv[ 2]= 1 (0x1)
+ * smb_bcc=87
+ */
+static void read_log_msg(FILE *in, unsigned char **_buffer,
+ unsigned short *buffersize, long *data_offset,
+ long *data_length)
+{
+ unsigned char *buffer;
+ int tmp; long i;
+ assert(fscanf(in, " size=%hu\n", buffersize)); line_num++;
+ buffer = (unsigned char *)malloc(*buffersize+4); /* +4 for NBSS Header */
+ memset(buffer, 0, *buffersize+4);
+ /* NetBIOS Session Service */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ memcpy(buffer+2, &buffersize, 2); /* TODO: need to copy as little-endian regardless of platform */
+ /* SMB Packet */
+ buffer[4] = 0xFF;
+ buffer[5] = 'S';
+ buffer[6] = 'M';
+ buffer[7] = 'B';
+ assert(fscanf(in, " smb_com=0x%x\n", &tmp)); buffer[smb_com] = tmp; line_num++;
+ assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp; line_num++;
+ assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp; line_num++;
+ assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2); line_num++;
+ assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp; line_num++;
+ assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2); line_num++;
+ assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2); line_num++;
+ assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2); line_num++;
+ assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2); line_num++;
+ assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2); line_num++;
+ assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp; line_num++;
+ for(i = 0; i < buffer[smb_wct]; i++) {
+ assert(fscanf(in, " smb_vwv[%*3d]=%*5d (0x%X)\n", &tmp)); line_num++;
+ memcpy(buffer+smb_vwv+i*2, &tmp, 2);
+ }
+
+ *data_offset = smb_vwv+buffer[smb_wct]*2;
+ assert(fscanf(in, " smb_bcc=%ld\n", data_length)); buffer[(*data_offset)] = *data_length; line_num++;
+ (*data_offset)+=2;
+ *_buffer = buffer;
+}
+
+/* Read the log message produced by lib/util.c:dump_data() containing:
+ * SMB_DATA.Bytes
+ *
+ * Example:
+ * [2007/04/08 20:41:39, 10] lib/util.c:dump_data(2243)
+ * [000] 00 55 00 6E 00 69 00 78 00 00 00 53 00 61 00 6D .U.n.i.x ...S.a.m
+ * [010] 00 62 00 61 00 20 00 33 00 2E 00 30 00 2E 00 32 .b.a. .3 ...0...2
+ * [020] 00 34 00 2D 00 49 00 73 00 69 00 6C 00 6F 00 6E .4.-.I.s .i.l.o.n
+ * [030] 00 20 00 4F 00 6E 00 65 00 46 00 53 00 20 00 76 . .O.n.e .F.S. .v
+ * [040] 00 34 00 2E 00 30 00 00 00 49 00 53 00 49 00 4C .4...0.. .I.S.I.L
+ * [050] 00 4F 00 4E 00 00 00 .O.N...
+ */
+static long read_log_data(FILE *in, unsigned char *buffer, long data_length)
+{
+ long i, addr; char real[2][16]; int ret;
+ unsigned int tmp;
+ for(i = 0; i < data_length; i++) {
+ if(i % 16 == 0){
+ if(i != 0) {
+ /* Read and discard the ascii data after each line. */
+ assert(fscanf(in, " %8c %8c\n", real[0], real[1]) == 2);
+ }
+ ret = fscanf(in, " [%03lX]", &addr); line_num++;
+ if(!ret) {
+ if(!quiet)
+ fprintf(stderr, "%ld: Only first %ld bytes are logged, "
+ "packet trace will be incomplete\n", line_num, i-1);
+ return i-1;
+ }
+ assert(addr == i);
+ }
+ if(!fscanf(in, "%02X", &tmp)) {
+ if(!quiet)
+ fprintf(stderr, "%ld: Log message formatted incorrectly. "
+ "Only first %ld bytes are logged, packet trace will "
+ "be incomplete\n", line_num, i-1);
+ while ((tmp = getc(in)) != '\n');
+ return i-1;
+ }
+ buffer[i] = tmp;
+ }
+
+ /* Consume the newline so we don't increment num_lines twice */
+ while ((tmp = getc(in)) != '\n');
+ return data_length;
+}
+
+int main(int argc, const char **argv)
+{
+ const char *infile, *outfile;
+ FILE *out, *in;
+ int opt;
+ poptContext pc;
+ char buffer[4096];
+ long data_offset = 0;
+ long data_length = 0;
+ long data_bytes_read = 0;
+ size_t in_packet = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "quiet",
+ .shortName = 'q',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &quiet,
+ .val = 0,
+ .descrip = "Be quiet, don't output warnings",
+ },
+ {
+ .longName = "hex",
+ .shortName = 'h',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &hexformat,
+ .val = 0,
+ .descrip = "Output format readable by text2pcap",
+ },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]");
+
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ infile = poptGetArg(pc);
+
+ if(infile) {
+ in = fopen(infile, "r");
+ if(!in) {
+ perror("fopen");
+ poptFreeContext(pc);
+ return 1;
+ }
+ } else in = stdin;
+
+ outfile = poptGetArg(pc);
+
+ if(outfile) {
+ out = fopen(outfile, "w+");
+ if(!out) {
+ perror("fopen");
+ fprintf(stderr, "Can't find %s, using stdout...\n", outfile);
+ poptFreeContext(pc);
+ return 1;
+ }
+ }
+
+ if(!outfile) out = stdout;
+
+ if(!hexformat)print_pcap_header(out);
+
+ while(!feof(in)) {
+ char *p;
+ p = fgets(buffer, sizeof(buffer), in);
+ if (p == NULL) {
+ fprintf(stderr, "error reading from input file\n");
+ break;
+ }
+ line_num++;
+ if(buffer[0] == '[') { /* Header */
+ if(strstr(buffer, "show_msg")) {
+ in_packet++;
+ if(in_packet == 1)continue;
+ read_log_msg(in, &curpacket, &curpacket_len, &data_offset, &data_length);
+ } else if(in_packet && strstr(buffer, "dump_data")) {
+ data_bytes_read = read_log_data(in, curpacket+data_offset, data_length);
+ } else {
+ if(in_packet){
+ if(hexformat) print_hex_packet(out, curpacket, curpacket_len);
+ else print_netbios_packet(out, curpacket, curpacket_len, data_bytes_read+data_offset);
+ free(curpacket);
+ }
+ in_packet = 0;
+ }
+ }
+ }
+
+ if (in != stdin) {
+ fclose(in);
+ }
+
+ if (out != stdout) {
+ fclose(out);
+ }
+
+ poptFreeContext(pc);
+ return 0;
+}
diff --git a/source3/utils/mdsearch.c b/source3/utils/mdsearch.c
new file mode 100644
index 0000000..0f5b887
--- /dev/null
+++ b/source3/utils/mdsearch.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2019, Ralph Boehme <slow@samba.org.>
+ *
+ * This library 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 library 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 library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/cmdline_contexts.h"
+#include "param.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_mdssvc.h"
+#include "librpc/gen_ndr/ndr_mdssvc_c.h"
+
+static char *opt_path;
+static int opt_live;
+
+int main(int argc, char **argv)
+{
+ const char **const_argv = discard_const_p(const char *, argv);
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+ struct tevent_context *ev = NULL;
+ struct cli_credentials *creds = NULL;
+ struct rpc_pipe_client *rpccli = NULL;
+ struct mdscli_ctx *mdscli_ctx = NULL;
+ struct mdscli_search_ctx *search = NULL;
+ const char *server = NULL;
+ const char *share = NULL;
+ const char *mds_query = NULL;
+ struct cli_state *cli = NULL;
+ char *basepath = NULL;
+ uint32_t flags = CLI_FULL_CONNECTION_IPC;
+ uint64_t *cnids = NULL;
+ size_t ncnids;
+ size_t i;
+ int opt;
+ poptContext pc;
+ NTSTATUS status;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "path",
+ .shortName = 'p',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_path,
+ .descrip = "Server-relative search path",
+ },
+ {
+ .longName = "live",
+ .shortName = 'L',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt_live,
+ .descrip = "live query",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S3
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ const_argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "mdsearch [OPTIONS] <server> <share> <query>\n");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ DBG_ERR("Invalid option %s: %s\n",
+ poptBadOption(pc, 0),
+ poptStrerror(opt));
+ poptPrintHelp(pc, stderr, 0);
+ goto fail;
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+ server = poptGetArg(pc);
+ share = poptGetArg(pc);
+ mds_query = poptGetArg(pc);
+
+ if (server == NULL || mds_query == NULL) {
+ poptPrintHelp(pc, stderr, 0);
+ goto fail;
+ }
+
+ samba_cmdline_burn(argc, argv);
+
+ if ((server[0] == '/' && server[1] == '/') ||
+ (server[0] == '\\' && server[1] == '\\'))
+ {
+ server += 2;
+ }
+
+ ev = samba_tevent_context_init(frame);
+ if (ev == NULL) {
+ goto fail;
+ }
+
+ cmdline_messaging_context(get_dyn_CONFIGFILE());
+
+ creds = samba_cmdline_get_creds();
+
+ status = cli_full_connection_creds(&cli,
+ lp_netbios_name(),
+ server,
+ NULL,
+ 0,
+ "IPC$",
+ "IPC",
+ creds,
+ flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Cannot connect to server: %s\n", nt_errstr(status));
+ goto fail_free_messaging;
+ }
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_mdssvc, &rpccli);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail_free_messaging;
+ }
+
+ status = mdscli_connect(frame,
+ rpccli->binding_handle,
+ share,
+ "/foo/bar",
+ &mdscli_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to connect mdssvc\n");
+ goto fail_free_messaging;
+ }
+
+ if (opt_path == NULL) {
+ basepath = mdscli_get_basepath(frame, mdscli_ctx);
+ } else {
+ basepath = talloc_strdup(frame, opt_path);
+ }
+ if (basepath == NULL) {
+ goto fail_free_messaging;
+ }
+
+ status = mdscli_search(frame,
+ mdscli_ctx,
+ mds_query,
+ basepath,
+ opt_live == 1 ? true : false,
+ &search);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mdscli_search failed\n");
+ goto fail_free_messaging;
+ }
+
+ if (!opt_live) {
+ sleep(1);
+ }
+
+ while (true) {
+ status = mdscli_get_results(frame,
+ search,
+ &cnids);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES)) {
+ if (opt_live) {
+ sleep(1);
+ continue;
+ }
+ break;
+ }
+
+ ncnids = talloc_array_length(cnids);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING) &&
+ ncnids == 0)
+ {
+ sleep(1);
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mdscli_get_results failed\n");
+ goto fail_free_messaging;
+ }
+
+ if (ncnids == 0) {
+ break;
+ }
+
+ for (i = 0; i < ncnids; i++) {
+ char *path = NULL;
+
+ status = mdscli_get_path(frame,
+ mdscli_ctx,
+ cnids[i],
+ &path);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Get path for CNID 0x%"PRIx64" failed\n",
+ cnids[i]);
+ goto fail_free_messaging;
+ }
+ printf("%s\n", path);
+ TALLOC_FREE(path);
+ }
+ }
+
+ status = mdscli_close_search(&search);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mdscli_close_search failed\n");
+ goto fail_free_messaging;
+ }
+
+ status = mdscli_disconnect(mdscli_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("mdscli_disconnect failed\n");
+ goto fail_free_messaging;
+ }
+
+ cmdline_messaging_context_free();
+ TALLOC_FREE(frame);
+ poptFreeContext(pc);
+ return 0;
+
+fail_free_messaging:
+ cmdline_messaging_context_free();
+fail:
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return 1;
+}
diff --git a/source3/utils/mvxattr.c b/source3/utils/mvxattr.c
new file mode 100644
index 0000000..dd8da79
--- /dev/null
+++ b/source3/utils/mvxattr.c
@@ -0,0 +1,227 @@
+/*
+ Unix SMB/CIFS implementation.
+ xattr renaming
+ Copyright (C) Ralph Boehme 2017
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <popt.h>
+#include <ftw.h>
+
+static struct rename_xattr_state {
+ int follow_symlink;
+ int print;
+ int force;
+ int verbose;
+ char *xattr_from;
+ char *xattr_to;
+} state;
+
+static int rename_xattr(const char *path,
+ const struct stat *sb,
+ int typeflag,
+ struct FTW *ftwbuf)
+{
+ ssize_t len;
+ int ret;
+
+ if (typeflag == FTW_SL) {
+ d_printf("Ignoring symlink %s\n", path);
+ return 0;
+ }
+
+ if (state.verbose) {
+ d_printf("%s\n", path);
+ }
+
+ len = getxattr(path, state.xattr_from, NULL, 0);
+ if (len < 0) {
+ if (errno == ENOATTR) {
+ return 0;
+ }
+ d_printf("getxattr [%s] failed [%s]\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ {
+ uint8_t buf[len];
+
+ len = getxattr(path, state.xattr_from, &buf[0], len);
+ if (len == -1) {
+ d_printf("getxattr [%s] failed [%s]\n",
+ path, strerror(errno));
+ return -1;
+ }
+
+ ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_CREATE);
+ if (ret != 0) {
+ if (errno != EEXIST) {
+ d_printf("setxattr [%s] failed [%s]\n",
+ path, strerror(errno));
+ return -1;
+ }
+ if (!state.force) {
+ d_printf("destination [%s:%s] exists, use -f to force\n",
+ path, state.xattr_to);
+ return -1;
+ }
+ ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_REPLACE);
+ if (ret != 0) {
+ d_printf("setxattr [%s:%s] failed [%s]\n",
+ path, state.xattr_to, strerror(errno));
+ return -1;
+ }
+ }
+
+ ret = removexattr(path, state.xattr_from);
+ if (ret != 0) {
+ d_printf("removexattr [%s:%s] failed [%s]\n",
+ path, state.xattr_from, strerror(errno));
+ return -1;
+ }
+
+ if (state.print) {
+ d_printf("Renamed %s to %s on %s\n",
+ state.xattr_from, state.xattr_to, path);
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ int c;
+ const char *path = NULL;
+ poptContext pc = NULL;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "from",
+ .shortName = 's',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &state.xattr_from,
+ .val = 's',
+ .descrip = "xattr source name",
+ },
+ {
+ .longName = "to",
+ .shortName = 'd',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &state.xattr_to,
+ .val = 'd',
+ .descrip = "xattr destination name",
+ },
+ {
+ .longName = "follow-symlinks",
+ .shortName = 'l',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &state.follow_symlink,
+ .val = 'l',
+ .descrip = "follow symlinks, the default is to "
+ "ignore them",
+ },
+ {
+ .longName = "print",
+ .shortName = 'p',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &state.print,
+ .val = 'p',
+ .descrip = "print files where the xattr got "
+ "renamed",
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &state.verbose,
+ .val = 'v',
+ .descrip = "print files as they are checked",
+ },
+ {
+ .longName = "force",
+ .shortName = 'f',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &state.force,
+ .val = 'f',
+ .descrip = "force overwriting of destination xattr",
+ },
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *s = NULL;
+ int ret = 0;
+
+ if (getuid() != 0) {
+ d_printf("%s only works as root!\n", argv[0]);
+ ret = 1;
+ goto done;
+ }
+
+ pc = poptGetContext(NULL, argc, argv, long_options, 0);
+ poptSetOtherOptionHelp(pc, "-s STRING -d STRING PATH [PATH ...]");
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 's':
+ s = poptGetOptArg(pc);
+ state.xattr_from = talloc_strdup(frame, s);
+ if (state.xattr_from == NULL) {
+ ret = 1;
+ goto done;
+ }
+ break;
+ case 'd':
+ s = poptGetOptArg(pc);
+ state.xattr_to = talloc_strdup(frame, s);
+ if (state.xattr_to == NULL) {
+ ret = 1;
+ goto done;
+ }
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(c));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (state.xattr_from == NULL || state.xattr_to == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ if (poptPeekArg(pc) == NULL) {
+ poptPrintUsage(pc, stderr, 0);
+ ret = 1;
+ goto done;
+ }
+
+ while ((path = poptGetArg(pc)) != NULL) {
+ ret = nftw(path, rename_xattr, 256,
+ state.follow_symlink ? 0 : FTW_PHYS);
+ }
+
+done:
+ poptFreeContext(pc);
+
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/net.c b/source3/utils/net.c
new file mode 100644
index 0000000..badf2e0
--- /dev/null
+++ b/source3/utils/net.c
@@ -0,0 +1,1450 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) 2008 Kai Blin (kai@samba.org)
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 2001.
+
+ Reworked again by abartlet in December 2001
+
+ Another overhaul, moving functionality into plug-ins loaded on demand by Kai
+ in May 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/*****************************************************/
+/* */
+/* Distributed SMB/CIFS Server Management Utility */
+/* */
+/* The intent was to make the syntax similar */
+/* to the NET utility (first developed in DOS */
+/* with additional interesting & useful functions */
+/* added in later SMB server network operating */
+/* systems). */
+/* */
+/*****************************************************/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "utils/net.h"
+#include "secrets.h"
+#include "lib/netapi/netapi.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "messages.h"
+#include "cmdline_contexts.h"
+#include "lib/gencache.h"
+#include "auth/credentials/credentials.h"
+#include "source3/utils/passwd_proto.h"
+#include "auth/gensec/gensec.h"
+#include "lib/param/param.h"
+
+#ifdef WITH_FAKE_KASERVER
+#include "utils/net_afs.h"
+#endif
+
+/***********************************************************************/
+/* end of internationalization section */
+/***********************************************************************/
+
+enum netr_SchannelType get_sec_channel_type(const char *param)
+{
+ if (!(param && *param)) {
+ return get_default_sec_channel();
+ } else {
+ if (strequal(param, "PDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "BDC")) {
+ return SEC_CHAN_BDC;
+ } else if (strequal(param, "MEMBER")) {
+ return SEC_CHAN_WKSTA;
+#if 0
+ } else if (strequal(param, "DOMAIN")) {
+ return SEC_CHAN_DOMAIN;
+#endif
+ } else {
+ return get_default_sec_channel();
+ }
+ }
+}
+
+static int net_changetrustpw(struct net_context *c, int argc, const char **argv)
+{
+ net_warn_member_options();
+
+ if (net_ads_check_our_domain(c) == 0)
+ return net_ads_changetrustpw(c, argc, argv);
+
+ return net_rpc_changetrustpw(c, argc, argv);
+}
+
+static void set_line_buffering(FILE *f)
+{
+ setvbuf(f, NULL, _IOLBF, 0);
+}
+
+static int net_primarytrust_dumpinfo(struct net_context *c, int argc,
+ const char **argv)
+{
+ int role = lp_server_role();
+ const char *domain = lp_workgroup();
+ struct secrets_domain_info1 *info = NULL;
+ bool include_secrets = c->opt_force;
+ char *str = NULL;
+ NTSTATUS status;
+
+ if (role >= ROLE_ACTIVE_DIRECTORY_DC) {
+ d_printf(_("net primarytrust dumpinfo is only supported "
+ "on a DOMAIN_MEMBER for now.\n"));
+ return 1;
+ }
+
+ net_warn_member_options();
+
+ if (c->opt_stdin) {
+ set_line_buffering(stdin);
+ set_line_buffering(stdout);
+ set_line_buffering(stderr);
+ }
+
+ status = secrets_fetch_or_upgrade_domain_info(domain,
+ talloc_tos(),
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to fetch the information for domain[%s] "
+ "in the secrets database.\n"),
+ domain);
+ return 1;
+ }
+
+ str = secrets_domain_info_string(info, info, domain, include_secrets);
+ if (str == NULL) {
+ d_fprintf(stderr, "secrets_domain_info_string() failed.\n");
+ return 1;
+ }
+
+ d_printf("%s", str);
+ if (!c->opt_force) {
+ d_printf(_("The password values are only included using "
+ "-f flag.\n"));
+ }
+
+ TALLOC_FREE(info);
+ return 0;
+}
+
+/**
+ * Entrypoint for 'net primarytrust' code.
+ *
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ */
+
+static int net_primarytrust(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ .funcname = "dumpinfo",
+ .fn = net_primarytrust_dumpinfo,
+ .valid_transports = NET_TRANSPORT_LOCAL,
+ .description = N_("Dump the details of the "
+ "workstation trust"),
+ .usage = N_(" net [options] primarytrust "
+ "dumpinfo'\n"
+ " Dump the details of the "
+ "workstation trust in "
+ "secrets.tdb.\n"
+ " Requires the -f flag to "
+ "include the password values."),
+ },
+ {
+ .funcname = NULL,
+ },
+ };
+
+ return net_run_function(c, argc, argv, "net primarytrust", func);
+}
+
+static int net_changesecretpw(struct net_context *c, int argc,
+ const char **argv)
+{
+ char *trust_pw;
+ int role = lp_server_role();
+
+ if (role != ROLE_DOMAIN_MEMBER) {
+ d_printf(_("Machine account password change only supported on a DOMAIN_MEMBER.\n"
+ "Do NOT use this function unless you know what it does!\n"
+ "This function will change the ADS Domain member "
+ "machine account password in the secrets.tdb file!\n"));
+ return 1;
+ }
+
+ net_warn_member_options();
+
+ if(c->opt_force) {
+ struct secrets_domain_info1 *info = NULL;
+ struct secrets_domain_info1_change *prev = NULL;
+ NTSTATUS status;
+ struct timeval tv = timeval_current();
+ NTTIME now = timeval_to_nttime(&tv);
+
+ if (c->opt_stdin) {
+ set_line_buffering(stdin);
+ set_line_buffering(stdout);
+ set_line_buffering(stderr);
+ }
+
+ trust_pw = get_pass(_("Enter machine password: "), c->opt_stdin);
+ if (trust_pw == NULL) {
+ d_fprintf(stderr,
+ _("Error in reading machine password\n"));
+ return 1;
+ }
+
+ status = secrets_prepare_password_change(lp_workgroup(),
+ "localhost",
+ trust_pw,
+ talloc_tos(),
+ &info, &prev);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to write the machine account password in the secrets database"));
+ return 1;
+ }
+ if (prev != NULL) {
+ d_fprintf(stderr,
+ _("Pending machine account password change found - aborting."));
+ status = secrets_failed_password_change("localhost",
+ NT_STATUS_REQUEST_NOT_ACCEPTED,
+ NT_STATUS_NOT_COMMITTED,
+ info);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Failed to abort machine account password change"));
+ }
+ return 1;
+ }
+ status = secrets_finish_password_change("localhost", now, info);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to write the machine account password in the secrets database"));
+ return 1;
+ }
+
+ d_printf(_("Modified trust account password in secrets database\n"));
+ }
+ else {
+ d_printf(_("Machine account password change requires the -f flag.\n"
+ "Do NOT use this function unless you know what it does!\n"
+ "This function will change the ADS Domain member "
+ "machine account password in the secrets.tdb file!\n"));
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Set the authorised user for winbindd access in secrets.tdb
+ */
+static int net_setauthuser(struct net_context *c, int argc, const char **argv)
+{
+ const char *password = NULL;
+ bool ok;
+
+ if (!secrets_init()) {
+ d_fprintf(stderr, _("Failed to open secrets.tdb.\n"));
+ return 1;
+ }
+
+ /* Delete the settings. */
+ if (argc >= 1) {
+ if (strncmp(argv[0], "delete", 6) != 0) {
+ d_fprintf(stderr,_("Usage:\n"));
+ d_fprintf(stderr,
+ _(" net setauthuser -U user[%%password] \n"
+ " Set the auth user account to user"
+ "password. Prompt for password if not "
+ "specified.\n"));
+ d_fprintf(stderr,
+ _(" net setauthuser delete\n"
+ " Delete the auth user setting.\n"));
+ return 1;
+ }
+ secrets_delete_entry(SECRETS_AUTH_USER);
+ secrets_delete_entry(SECRETS_AUTH_DOMAIN);
+ secrets_delete_entry(SECRETS_AUTH_PASSWORD);
+ return 0;
+ }
+
+ if (!c->opt_user_specified) {
+ d_fprintf(stderr, _("Usage:\n"));
+ d_fprintf(stderr,
+ _(" net setauthuser -U user[%%password]\n"
+ " Set the auth user account to user"
+ "password. Prompt for password if not "
+ "specified.\n"));
+ d_fprintf(stderr,
+ _(" net setauthuser delete\n"
+ " Delete the auth user setting.\n"));
+ return 1;
+ }
+
+ password = net_prompt_pass(c, _("the auth user"));
+ if (password == NULL) {
+ d_fprintf(stderr,_("Failed to get the auth users password.\n"));
+ return 1;
+ }
+
+ ok = secrets_store_creds(c->creds);
+ if (!ok) {
+ d_fprintf(stderr, _("Failed storing auth user credentials\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Get the auth user settings
+ */
+static int net_getauthuser(struct net_context *c, int argc, const char **argv)
+{
+ char *user, *domain, *password;
+
+ /* Lift data from secrets file */
+
+ secrets_fetch_ipc_userpass(&user, &domain, &password);
+
+ if ((!user || !*user) && (!domain || !*domain ) &&
+ (!password || !*password)){
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ BURN_FREE_STR(password);
+ d_printf(_("No authorised user configured\n"));
+ return 0;
+ }
+
+ /* Pretty print authorised user info */
+
+ d_printf("%s%s%s%s%s\n", domain ? domain : "",
+ domain ? lp_winbind_separator(): "", user,
+ password ? "%" : "", password ? password : "");
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+ BURN_FREE_STR(password);
+
+ return 0;
+}
+/*
+ Retrieve our local SID or the SID for the specified name
+ */
+static int net_getlocalsid(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ const char *name;
+ struct dom_sid_buf sid_str;
+
+ if (argc >= 1) {
+ name = argv[0];
+ }
+ else {
+ name = lp_netbios_name();
+ }
+
+ if(!initialize_password_db(false, NULL)) {
+ d_fprintf(stderr, _("WARNING: Could not open passdb\n"));
+ return 1;
+ }
+
+ /* first check to see if we can even access secrets, so we don't
+ panic when we can't. */
+
+ if (!secrets_init()) {
+ d_fprintf(stderr,
+ _("Unable to open secrets.tdb. Can't fetch domain "
+ "SID for name: %s\n"), name);
+ return 1;
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!secrets_fetch_domain_sid(name, &sid)) {
+ DEBUG(0, ("Can't fetch domain SID for name: %s\n", name));
+ return 1;
+ }
+ d_printf(_("SID for domain %s is: %s\n"),
+ name,
+ dom_sid_str_buf(&sid, &sid_str));
+ return 0;
+}
+
+static int net_setlocalsid(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+
+ if ( (argc != 1)
+ || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0)
+ || (!string_to_sid(&sid, argv[0]))
+ || (sid.num_auths != 4)) {
+ d_printf(_("Usage:"));
+ d_printf(" net setlocalsid S-1-5-21-x-y-z\n");
+ return 1;
+ }
+
+ if (!secrets_store_domain_sid(lp_netbios_name(), &sid)) {
+ DEBUG(0,("Can't store domain SID as a pdc/bdc.\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int net_setdomainsid(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+
+ if ( (argc != 1)
+ || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0)
+ || (!string_to_sid(&sid, argv[0]))
+ || (sid.num_auths != 4)) {
+ d_printf(_("Usage:"));
+ d_printf(" net setdomainsid S-1-5-21-x-y-z\n");
+ return 1;
+ }
+
+ if (!secrets_store_domain_sid(lp_workgroup(), &sid)) {
+ DEBUG(0,("Can't store domain SID.\n"));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int net_getdomainsid(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid domain_sid;
+ struct dom_sid_buf sid_str;
+
+ if (argc > 0) {
+ d_printf(_("Usage:"));
+ d_printf(" net getdomainsid\n");
+ return 1;
+ }
+
+ if(!initialize_password_db(false, NULL)) {
+ d_fprintf(stderr, _("WARNING: Could not open passdb\n"));
+ return 1;
+ }
+
+ /* first check to see if we can even access secrets, so we don't
+ panic when we can't. */
+
+ if (!secrets_init()) {
+ d_fprintf(stderr, _("Unable to open secrets.tdb. Can't fetch "
+ "domain SID for name: %s\n"),
+ get_global_sam_name());
+ return 1;
+ }
+
+ /* Generate one, if it doesn't exist */
+ get_global_sam_sid();
+
+ if (!IS_DC) {
+ if (!secrets_fetch_domain_sid(lp_netbios_name(), &domain_sid)) {
+ d_fprintf(stderr, _("Could not fetch local SID\n"));
+ return 1;
+ }
+ d_printf(_("SID for local machine %s is: %s\n"),
+ lp_netbios_name(),
+ dom_sid_str_buf(&domain_sid, &sid_str));
+ }
+ if (!secrets_fetch_domain_sid(c->opt_workgroup, &domain_sid)) {
+ d_fprintf(stderr, _("Could not fetch domain SID\n"));
+ return 1;
+ }
+
+ d_printf(_("SID for domain %s is: %s\n"),
+ c->opt_workgroup,
+ dom_sid_str_buf(&domain_sid, &sid_str));
+
+ return 0;
+}
+
+static bool search_maxrid(struct pdb_search *search, const char *type,
+ uint32_t *max_rid)
+{
+ struct samr_displayentry *entries;
+ uint32_t i, num_entries;
+
+ if (search == NULL) {
+ d_fprintf(stderr, _("get_maxrid: Could not search %s\n"), type);
+ return false;
+ }
+
+ num_entries = pdb_search_entries(search, 0, 0xffffffff, &entries);
+ for (i=0; i<num_entries; i++)
+ *max_rid = MAX(*max_rid, entries[i].rid);
+ TALLOC_FREE(search);
+ return true;
+}
+
+static uint32_t get_maxrid(void)
+{
+ uint32_t max_rid = 0;
+
+ if (!search_maxrid(pdb_search_users(talloc_tos(), 0), "users", &max_rid))
+ return 0;
+
+ if (!search_maxrid(pdb_search_groups(talloc_tos()), "groups", &max_rid))
+ return 0;
+
+ if (!search_maxrid(pdb_search_aliases(talloc_tos(),
+ get_global_sam_sid()),
+ "aliases", &max_rid))
+ return 0;
+
+ return max_rid;
+}
+
+static int net_maxrid(struct net_context *c, int argc, const char **argv)
+{
+ uint32_t rid;
+
+ if (argc != 0) {
+ d_fprintf(stderr, "%s net maxrid\n", _("Usage:"));
+ return 1;
+ }
+
+ if ((rid = get_maxrid()) == 0) {
+ d_fprintf(stderr, _("can't get current maximum rid\n"));
+ return 1;
+ }
+
+ d_printf(_("Currently used maximum rid: %d\n"), rid);
+
+ return 0;
+}
+
+/* main function table */
+static struct functable net_func[] = {
+ {
+ "rpc",
+ net_rpc,
+ NET_TRANSPORT_RPC,
+ N_("Run functions using RPC transport"),
+ N_(" Use 'net help rpc' to get more extensive information "
+ "about 'net rpc' commands.")
+ },
+ {
+ "rap",
+ net_rap,
+ NET_TRANSPORT_RAP,
+ N_("Run functions using RAP transport"),
+ N_(" Use 'net help rap' to get more extensive information "
+ "about 'net rap' commands.")
+ },
+ {
+ "ads",
+ net_ads,
+ NET_TRANSPORT_ADS,
+ N_("Run functions using ADS transport"),
+ N_(" Use 'net help ads' to get more extensive information "
+ "about 'net ads' commands.")
+ },
+
+ /* eventually these should auto-choose the transport ... */
+ {
+ "file",
+ net_file,
+ NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ N_("Functions on remote opened files"),
+ N_(" Use 'net help file' to get more information about 'net "
+ "file' commands.")
+ },
+ {
+ "share",
+ net_share,
+ NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ N_("Functions on shares"),
+ N_(" Use 'net help share' to get more information about 'net "
+ "share' commands.")
+ },
+ {
+ "session",
+ net_rap_session,
+ NET_TRANSPORT_RAP,
+ N_("Manage sessions"),
+ N_(" Use 'net help session' to get more information about "
+ "'net session' commands.")
+ },
+ {
+ "server",
+ net_rap_server,
+ NET_TRANSPORT_RAP,
+ N_("List servers in workgroup"),
+ N_(" Use 'net help server' to get more information about 'net "
+ "server' commands.")
+ },
+ {
+ "domain",
+ net_rap_domain,
+ NET_TRANSPORT_RAP,
+ N_("List domains/workgroups on network"),
+ N_(" Use 'net help domain' to get more information about 'net "
+ "domain' commands.")
+ },
+ {
+ "printq",
+ net_rap_printq,
+ NET_TRANSPORT_RAP,
+ N_("Modify printer queue"),
+ N_(" Use 'net help printq' to get more information about 'net "
+ "printq' commands.")
+ },
+ {
+ "user",
+ net_user,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ N_("Manage users"),
+ N_(" Use 'net help user' to get more information about 'net "
+ "user' commands.")
+ },
+ {
+ "group",
+ net_group,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ N_("Manage groups"),
+ N_(" Use 'net help group' to get more information about 'net "
+ "group' commands.")
+ },
+ {
+ "groupmap",
+ net_groupmap,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage group mappings"),
+ N_(" Use 'net help groupmap' to get more information about "
+ "'net groupmap' commands.")
+ },
+ {
+ "sam",
+ net_sam,
+ NET_TRANSPORT_LOCAL,
+ N_("Functions on the SAM database"),
+ N_(" Use 'net help sam' to get more information about 'net "
+ "sam' commands.")
+ },
+ {
+ "validate",
+ net_rap_validate,
+ NET_TRANSPORT_RAP,
+ N_("Validate username and password"),
+ N_(" Use 'net help validate' to get more information about "
+ "'net validate' commands.")
+ },
+ {
+ "groupmember",
+ net_rap_groupmember,
+ NET_TRANSPORT_RAP,
+ N_("Modify group memberships"),
+ N_(" Use 'net help groupmember' to get more information about "
+ "'net groupmember' commands.")
+ },
+ { "admin",
+ net_rap_admin,
+ NET_TRANSPORT_RAP,
+ N_("Execute remote command on a remote OS/2 server"),
+ N_(" Use 'net help admin' to get more information about 'net "
+ "admin' commands.")
+ },
+ { "service",
+ net_rap_service,
+ NET_TRANSPORT_RAP,
+ N_("List/modify running services"),
+ N_(" Use 'net help service' to get more information about "
+ "'net service' commands.")
+ },
+ {
+ "password",
+ net_rap_password,
+ NET_TRANSPORT_RAP,
+ N_("Change user password on target server"),
+ N_(" Use 'net help password' to get more information about "
+ "'net password' commands.")
+ },
+ {
+ "primarytrust",
+ net_primarytrust,
+ NET_TRANSPORT_RPC,
+ N_("Run functions related to the primary workstation trust."),
+ N_(" Use 'net help primarytrust' to get more extensive information "
+ "about 'net primarytrust' commands.")
+ },
+ { "changetrustpw",
+ net_changetrustpw,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC,
+ N_("Change the trust password"),
+ N_(" Use 'net help changetrustpw' to get more information "
+ "about 'net changetrustpw'.")
+ },
+ { "changesecretpw",
+ net_changesecretpw,
+ NET_TRANSPORT_LOCAL,
+ N_("Change the secret password"),
+ N_(" net [options] changesecretpw\n"
+ " Change the ADS domain member machine account password "
+ "in secrets.tdb.\n"
+ " Do NOT use this function unless you know what it does.\n"
+ " Requires the -f flag to work.")
+ },
+ {
+ "setauthuser",
+ net_setauthuser,
+ NET_TRANSPORT_LOCAL,
+ N_("Set the winbind auth user"),
+ N_(" net -U user[%%password] [-W domain] setauthuser\n"
+ " Set the auth user, password (and optionally domain\n"
+ " Will prompt for password if not given.\n"
+ " net setauthuser delete\n"
+ " Delete the existing auth user settings.")
+ },
+ {
+ "getauthuser",
+ net_getauthuser,
+ NET_TRANSPORT_LOCAL,
+ N_("Get the winbind auth user settings"),
+ N_(" net getauthuser\n"
+ " Get the current winbind auth user settings.")
+ },
+ { "time",
+ net_time,
+ NET_TRANSPORT_LOCAL,
+ N_("Show/set time"),
+ N_(" Use 'net help time' to get more information about 'net "
+ "time' commands.")
+ },
+ { "lookup",
+ net_lookup,
+ NET_TRANSPORT_LOCAL,
+ N_("Look up host names/IP addresses"),
+ N_(" Use 'net help lookup' to get more information about 'net "
+ "lookup' commands.")
+ },
+ { "g_lock",
+ net_g_lock,
+ NET_TRANSPORT_LOCAL,
+ N_("Manipulate the global lock table"),
+ N_(" Use 'net help g_lock' to get more information about "
+ "'net g_lock' commands.")
+ },
+ { "join",
+ net_join,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC,
+ N_("Join a domain/AD"),
+ N_(" Use 'net help join' to get more information about 'net "
+ "join'.")
+ },
+ { "offlinejoin",
+ net_offlinejoin,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC,
+ N_("Perform offline domain join"),
+ N_(" Use 'net help offlinejoin' to get more information about 'net "
+ "offlinejoin'.")
+ },
+ { "dom",
+ net_dom,
+ NET_TRANSPORT_LOCAL,
+ N_("Join/unjoin (remote) machines to/from a domain/AD"),
+ N_(" Use 'net help dom' to get more information about 'net "
+ "dom' commands.")
+ },
+ { "cache",
+ net_cache,
+ NET_TRANSPORT_LOCAL,
+ N_("Operate on the cache tdb file"),
+ N_(" Use 'net help cache' to get more information about 'net "
+ "cache' commands.")
+ },
+ { "getlocalsid",
+ net_getlocalsid,
+ NET_TRANSPORT_LOCAL,
+ N_("Get the SID for the local domain"),
+ N_(" net getlocalsid")
+ },
+ { "setlocalsid",
+ net_setlocalsid,
+ NET_TRANSPORT_LOCAL,
+ N_("Set the SID for the local domain"),
+ N_(" net setlocalsid S-1-5-21-x-y-z")
+ },
+ { "setdomainsid",
+ net_setdomainsid,
+ NET_TRANSPORT_LOCAL,
+ N_("Set domain SID on member servers"),
+ N_(" net setdomainsid S-1-5-21-x-y-z")
+ },
+ { "getdomainsid",
+ net_getdomainsid,
+ NET_TRANSPORT_LOCAL,
+ N_("Get domain SID on member servers"),
+ N_(" net getdomainsid")
+ },
+ { "maxrid",
+ net_maxrid,
+ NET_TRANSPORT_LOCAL,
+ N_("Display the maximum RID currently used"),
+ N_(" net maxrid")
+ },
+ { "idmap",
+ net_idmap,
+ NET_TRANSPORT_LOCAL,
+ N_("IDmap functions"),
+ N_(" Use 'net help idmap to get more information about 'net "
+ "idmap' commands.")
+ },
+ { "status",
+ net_status,
+ NET_TRANSPORT_LOCAL,
+ N_("Display server status"),
+ N_(" Use 'net help status' to get more information about 'net "
+ "status' commands.")
+ },
+ { "usershare",
+ net_usershare,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage user-modifiable shares"),
+ N_(" Use 'net help usershare to get more information about "
+ "'net usershare' commands.")
+ },
+ { "usersidlist",
+ net_usersidlist,
+ NET_TRANSPORT_RPC,
+ N_("Display list of all users with SID"),
+ N_(" Use 'net help usersidlist' to get more information about "
+ "'net usersidlist'.")
+ },
+ { "conf",
+ net_conf,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage Samba registry based configuration"),
+ N_(" Use 'net help conf' to get more information about 'net "
+ "conf' commands.")
+ },
+ { "registry",
+ net_registry,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage the Samba registry"),
+ N_(" Use 'net help registry' to get more information about "
+ "'net registry' commands.")
+ },
+ { "eventlog",
+ net_eventlog,
+ NET_TRANSPORT_LOCAL,
+ N_("Process Win32 *.evt eventlog files"),
+ N_(" Use 'net help eventlog' to get more information about "
+ "'net eventlog' commands.")
+ },
+ { "printing",
+ net_printing,
+ NET_TRANSPORT_LOCAL,
+ N_("Process tdb printer files"),
+ N_(" Use 'net help printing' to get more information about "
+ "'net printing' commands.")
+ },
+
+ { "serverid",
+ net_serverid,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage the serverid tdb"),
+ N_(" Use 'net help serverid' to get more information about "
+ "'net serverid' commands.")
+ },
+
+ { "notify",
+ net_notify,
+ NET_TRANSPORT_LOCAL,
+ N_("notifyd client code"),
+ N_(" Use 'net help notify' to get more information about "
+ "'net notify' commands.")
+ },
+
+ { "tdb",
+ net_tdb,
+ NET_TRANSPORT_LOCAL,
+ N_("Show information from tdb records"),
+ N_(" Use 'net help tdb' to get more information about "
+ "'net tdb' commands.")
+ },
+
+ { "vfs",
+ net_vfs,
+ NET_TRANSPORT_LOCAL,
+ N_("Filesystem operation through the VFS stack"),
+ N_(" Use 'net help vfs' to get more information about "
+ "'net vfs' commands.")
+ },
+
+ { "witness",
+ net_witness,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage witness registrations"),
+ N_(" Use 'net help witness' to get more information about "
+ "'net witness' commands.")
+ },
+
+#ifdef WITH_FAKE_KASERVER
+ { "afs",
+ net_afs,
+ NET_TRANSPORT_LOCAL,
+ N_("Manage AFS tokens"),
+ N_(" Use 'net help afs' to get more information about 'net "
+ "afs' commands.")
+ },
+#endif
+
+ { "help",
+ net_help,
+ NET_TRANSPORT_LOCAL,
+ N_("Print usage information"),
+ N_(" Use 'net help help' to list usage information for 'net' "
+ "commands.")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+};
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, char **argv)
+{
+ int opt,i;
+ int rc = 0;
+ int argc_new = 0;
+ const char ** argv_new;
+ const char **argv_const = discard_const_p(const char *, argv);
+ poptContext pc;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_context *c = talloc_zero(frame, struct net_context);
+ bool ok;
+
+ struct poptOption long_options[] = {
+ {
+ .longName = "help",
+ .shortName = 'h',
+ .argInfo = POPT_ARG_NONE,
+ .val = 'h',
+ },
+ {
+ .longName = "target-workgroup",
+ .shortName = 'w',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_target_workgroup,
+ },
+ {
+ .longName = "ipaddress",
+ .shortName = 'I',
+ .argInfo = POPT_ARG_STRING,
+ .arg = 0,
+ .val = 'I',
+ },
+ {
+ .longName = "port",
+ .shortName = 'p',
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_port,
+ },
+ {
+ .longName = "myname",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_requester_name,
+ },
+ {
+ .longName = "server",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_host,
+ },
+ {
+ .longName = "container",
+ .shortName = 'c',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_container,
+ },
+ {
+ .longName = "comment",
+ .shortName = 'C',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_comment,
+ },
+ {
+ .longName = "maxusers",
+ .shortName = 'M',
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_maxusers,
+ },
+ {
+ .longName = "flags",
+ .shortName = 'F',
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_flags,
+ },
+ {
+ .longName = "long",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_long_list_entries,
+ },
+ {
+ .longName = "reboot",
+ .shortName = 'r',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_reboot,
+ },
+ {
+ .longName = "force",
+ .shortName = 'f',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_force,
+ },
+ {
+ .longName = "stdin",
+ .shortName = 'i',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_stdin,
+ },
+ {
+ .longName = "timeout",
+ .shortName = 't',
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_timeout,
+ },
+ {
+ .longName = "request-timeout",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_request_timeout,
+ },
+ {
+ .longName = "use-ccache",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_ccache,
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_verbose,
+ },
+ {
+ .longName = "test",
+ .shortName = 'T',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_testmode,
+ },
+ /* Options for 'net groupmap set' */
+ {
+ .longName = "local",
+ .shortName = 'L',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_localgroup,
+ },
+ {
+ .longName = "domain",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_domaingroup,
+ },
+ {
+ .longName = "ntname",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_newntname,
+ },
+ {
+ .longName = "rid",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_rid,
+ },
+ /* Options for 'net rpc share migrate' */
+ {
+ .longName = "acls",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_acls,
+ },
+ {
+ .longName = "attrs",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_attrs,
+ },
+ {
+ .longName = "timestamps",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_timestamps,
+ },
+ {
+ .longName = "exclude",
+ .shortName = 'X',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_exclude,
+ },
+ {
+ .longName = "destination",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_destination,
+ },
+ {
+ .longName = "tallocreport",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->do_talloc_report,
+ },
+ /* Options for 'net rpc vampire (keytab)' */
+ {
+ .longName = "force-full-repl",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_force_full_repl,
+ },
+ {
+ .longName = "single-obj-repl",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_single_obj_repl,
+ },
+ {
+ .longName = "clean-old-entries",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_clean_old_entries,
+ },
+ /* Options for 'net idmap'*/
+ {
+ .longName = "db",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_db,
+ },
+ {
+ .longName = "lock",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_lock,
+ },
+ {
+ .longName = "auto",
+ .shortName = 'a',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_auto,
+ },
+ {
+ .longName = "repair",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_repair,
+ },
+ /* Options for 'net registry check'*/
+ {
+ .longName = "reg-version",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_reg_version,
+ },
+ {
+ .longName = "output",
+ .shortName = 'o',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_output,
+ },
+ {
+ .longName = "wipe",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_wipe,
+ },
+ /* Options for 'net registry import' */
+ {
+ .longName = "precheck",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_precheck,
+ },
+ /* Options for 'net ads join or leave' */
+ {
+ .longName = "no-dns-updates",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_no_dns_updates,
+ },
+ {
+ .longName = "keep-account",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_keep_account,
+ },
+ {
+ .longName = "json",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_json,
+ },
+ /* Options for 'net vfs' */
+ {
+ .longName = "continue",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_continue_on_error,
+ .descrip = "Continue on errors",
+ },
+ {
+ .longName = "recursive",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_recursive,
+ .descrip = "Traverse directory hierarchy",
+ },
+ {
+ .longName = "follow-symlinks",
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_follow_symlink,
+ .descrip = "follow symlinks",
+ },
+ /* Options for 'net ads dns register' */
+ {
+ .longName = "dns-ttl",
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_dns_ttl,
+ .descrip = "TTL in seconds of DNS records",
+ },
+ /* Options for 'net witness {list,...}' */
+ {
+ .longName = "witness-registration",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_registration,
+ },
+ {
+ .longName = "witness-net-name",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_net_name,
+ },
+ {
+ .longName = "witness-share-name",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_share_name,
+ },
+ {
+ .longName = "witness-ip-address",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_ip_address,
+ },
+ {
+ .longName = "witness-client-computer-name",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_client_computer_name,
+ },
+ {
+ .longName = "witness-apply-to-all",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &c->opt_witness_apply_to_all,
+ },
+ {
+ .longName = "witness-new-ip",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_new_ip,
+ },
+ {
+ .longName = "witness-new-node",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &c->opt_witness_new_node,
+ },
+ {
+ .longName = "witness-forced-response",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &c->opt_witness_forced_response,
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_LEGACY_S3
+ POPT_TABLEEND
+ };
+
+ /* Ignore possible SIGPIPE upon ldap_unbind when over TLS */
+ BlockSignals(True, SIGPIPE);
+
+ zero_sockaddr(&c->opt_dest_ip);
+ c->opt_witness_new_node = -2;
+
+ smb_init_locale();
+
+ setlocale(LC_ALL, "");
+#if defined(HAVE_BINDTEXTDOMAIN)
+ bindtextdomain(MODULE_NAME, get_dyn_LOCALEDIR());
+#endif
+#if defined(HAVE_TEXTDOMAIN)
+ textdomain(MODULE_NAME);
+#endif
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ c->lp_ctx = samba_cmdline_get_lp_ctx();
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ lpcfg_set_cmdline(c->lp_ctx, "log level", "0");
+ c->private_data = net_func;
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'h':
+ c->display_usage = true;
+ break;
+ case 'I':
+ if (!interpret_string_addr(&c->opt_dest_ip,
+ poptGetOptArg(pc), 0)) {
+ d_fprintf(stderr, _("\nInvalid ip address specified\n"));
+ } else {
+ c->opt_have_ip = true;
+ }
+ break;
+ default:
+ d_fprintf(stderr, _("\nInvalid option %s: %s\n"),
+ poptBadOption(pc, 0), poptStrerror(opt));
+ net_help(c, argc, argv_const);
+ exit(1);
+ }
+ }
+
+ c->creds = samba_cmdline_get_creds();
+
+ {
+ enum credentials_obtained username_obtained =
+ CRED_UNINITIALISED;
+ enum smb_encryption_setting encrypt_state =
+ cli_credentials_get_smb_encryption(c->creds);
+ enum credentials_use_kerberos krb5_state =
+ cli_credentials_get_kerberos_state(c->creds);
+ uint32_t gensec_features;
+
+ c->opt_user_name = cli_credentials_get_username_and_obtained(
+ c->creds,
+ &username_obtained);
+ c->opt_user_specified = (username_obtained == CRED_SPECIFIED);
+
+ c->opt_workgroup = cli_credentials_get_domain(c->creds);
+
+ c->smb_encrypt = (encrypt_state == SMB_ENCRYPTION_REQUIRED);
+
+ c->opt_kerberos = (krb5_state > CRED_USE_KERBEROS_DESIRED);
+
+ gensec_features = cli_credentials_get_gensec_features(c->creds);
+ c->opt_ccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE);
+ }
+
+ c->msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
+
+#if defined(HAVE_BIND_TEXTDOMAIN_CODESET)
+ /* Bind our gettext results to 'unix charset'
+
+ This ensures that the translations and any embedded strings are in the
+ same charset. It won't be the one from the user's locale (we no
+ longer auto-detect that), but it will be self-consistent.
+ */
+ bind_textdomain_codeset(MODULE_NAME, lp_unix_charset());
+#endif
+
+ argv_new = (const char **)poptGetArgs(pc);
+
+ argc_new = argc;
+ for (i=0; i<argc; i++) {
+ if (argv_new[i] == NULL) {
+ argc_new = i;
+ break;
+ }
+ }
+
+ if (c->do_talloc_report) {
+ talloc_enable_leak_report();
+ }
+
+ if (c->opt_requester_name) {
+ lpcfg_set_cmdline(c->lp_ctx, "netbios name", c->opt_requester_name);
+ }
+
+ if (!c->opt_target_workgroup) {
+ c->opt_target_workgroup = talloc_strdup(c, lp_workgroup());
+ }
+
+ load_interfaces();
+
+ /* this makes sure that when we do things like call scripts,
+ that it won't assert because we are not root */
+ sec_init();
+
+ samba_cmdline_burn(argc, argv);
+
+ rc = net_run_function(c, argc_new-1, argv_new+1, "net", net_func);
+
+ DEBUG(2,("return code = %d\n", rc));
+
+ libnetapi_free(c->netapi_ctx);
+
+ poptFreeContext(pc);
+
+ cmdline_messaging_context_free();
+
+ gfree_all();
+
+ TALLOC_FREE(frame);
+ return rc;
+}
diff --git a/source3/utils/net.h b/source3/utils/net.h
new file mode 100644
index 0000000..fca3a99
--- /dev/null
+++ b/source3/utils/net.h
@@ -0,0 +1,208 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+/*
+ * A function of this type is passed to the '
+ * run_rpc_command' wrapper. Must go before the net_proto.h
+ * include
+ */
+
+struct cli_state;
+
+#include "../librpc/gen_ndr/lsa.h"
+
+#include "intl.h"
+#ifdef HAVE_LIBINTL_H
+#include <libintl.h>
+#endif
+
+#if defined(HAVE_GETTEXT) && !defined(__LCLINT__)
+#define _(foo) gettext(foo)
+#else
+#define _(foo) foo
+#endif
+
+#define MODULE_NAME "net"
+
+struct net_context {
+ const char *opt_requester_name;
+ const char *opt_host;
+ const char *opt_password;
+ const char *opt_user_name;
+ bool opt_user_specified;
+ const char *opt_workgroup;
+ int opt_long_list_entries;
+ int opt_reboot;
+ int opt_force;
+ int opt_stdin;
+ int opt_port;
+ int opt_verbose;
+ int opt_maxusers;
+ const char *opt_comment;
+ const char *opt_container;
+ int opt_flags;
+ int opt_timeout;
+ int opt_request_timeout;
+ const char *opt_target_workgroup;
+ int opt_machine_pass;
+ int opt_localgroup;
+ int opt_domaingroup;
+ int do_talloc_report;
+ const char *opt_newntname;
+ int opt_rid;
+ int opt_acls;
+ int opt_attrs;
+ int opt_timestamps;
+ const char *opt_exclude;
+ const char *opt_destination;
+ int opt_testmode;
+ int opt_kerberos;
+ int opt_force_full_repl;
+ int opt_ccache;
+ int opt_single_obj_repl;
+ int opt_clean_old_entries;
+ const char *opt_db;
+ int opt_lock;
+ int opt_auto;
+ int opt_repair;
+ int opt_reg_version;
+ const char *opt_output;
+ int opt_wipe;
+ const char *opt_precheck;
+ int opt_no_dns_updates;
+ int opt_keep_account;
+ int opt_json;
+ int opt_continue_on_error;
+ int opt_recursive;
+ int opt_follow_symlink;
+ int opt_dns_ttl;
+ const char *opt_witness_registration;
+ const char *opt_witness_net_name;
+ const char *opt_witness_share_name;
+ const char *opt_witness_ip_address;
+ const char *opt_witness_client_computer_name;
+ int opt_witness_apply_to_all;
+ const char *opt_witness_new_ip;
+ int opt_witness_new_node;
+ const char *opt_witness_forced_response;
+
+ int opt_have_ip;
+ struct sockaddr_storage opt_dest_ip;
+ bool smb_encrypt;
+ struct libnetapi_ctx *netapi_ctx;
+ struct messaging_context *msg_ctx;
+ struct netlogon_creds_cli_context *netlogon_creds;
+ struct cli_credentials *creds;
+ struct loadparm_context *lp_ctx;
+
+ bool display_usage;
+ void *private_data;
+};
+
+struct net_dc_info {
+ bool is_dc;
+ bool is_pdc;
+ bool is_ad;
+ bool is_mixed_mode;
+ const char *netbios_domain_name;
+ const char *dns_domain_name;
+ const char *forest_name;
+};
+
+#define NET_TRANSPORT_LOCAL 0x01
+#define NET_TRANSPORT_RAP 0x02
+#define NET_TRANSPORT_RPC 0x04
+#define NET_TRANSPORT_ADS 0x08
+
+struct functable {
+ const char *funcname;
+ int (*fn)(struct net_context *c, int argc, const char **argv);
+ int valid_transports;
+ const char *description;
+ const char *usage;
+};
+
+typedef NTSTATUS (*rpc_command_fn)(struct net_context *c,
+ const struct dom_sid *,
+ const char *,
+ struct cli_state *cli,
+ struct rpc_pipe_client *,
+ TALLOC_CTX *,
+ int,
+ const char **);
+
+typedef struct copy_clistate {
+ TALLOC_CTX *mem_ctx;
+ struct cli_state *cli_share_src;
+ struct cli_state *cli_share_dst;
+ char *cwd;
+ uint16_t attribute;
+ struct net_context *c;
+}copy_clistate;
+
+struct rpc_sh_ctx {
+ struct cli_state *cli;
+
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+
+ const char *whoami;
+ const char *thiscmd;
+ struct rpc_sh_cmd *cmds;
+ struct rpc_sh_ctx *parent;
+};
+
+struct rpc_sh_cmd {
+ const char *name;
+ struct rpc_sh_cmd *(*sub)(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+ const struct ndr_interface_table *table;
+ NTSTATUS (*fn)(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv);
+ const char *help;
+};
+
+enum netdom_domain_t { ND_TYPE_NT4, ND_TYPE_AD };
+
+/* INCLUDE FILES */
+
+#include "utils/net_proto.h"
+#include "utils/net_help_common.h"
+
+/* MACROS & DEFINES */
+
+#define NET_FLAGS_MASTER 0x00000001
+#define NET_FLAGS_DMB 0x00000002
+#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 0x00000004 /* Would it be insane to set 'localhost'
+ as the default remote host for this
+ operation? For example, localhost
+ is insane for a 'join' operation. */
+#define NET_FLAGS_PDC 0x00000008 /* PDC only */
+#define NET_FLAGS_ANONYMOUS 0x00000010 /* use an anonymous connection */
+#define NET_FLAGS_NO_PIPE 0x00000020 /* don't open an RPC pipe */
+#define NET_FLAGS_SIGN 0x00000040 /* sign RPC connection */
+#define NET_FLAGS_SEAL 0x00000080 /* seal RPC connection */
+#define NET_FLAGS_TCP 0x00000100 /* use ncacn_ip_tcp */
+#define NET_FLAGS_EXPECT_FALLBACK 0x00000200 /* the caller will fallback */
+
+/* net share operation modes */
+#define NET_MODE_SHARE_MIGRATE 1
+
diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c
new file mode 100644
index 0000000..d95a209
--- /dev/null
+++ b/source3/utils/net_ads.c
@@ -0,0 +1,4184 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads commands
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2006 Gerald (Jerry) Carter (jerry@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "libsmb/namequery.h"
+#include "rpc_client/cli_pipe.h"
+#include "librpc/gen_ndr/ndr_krb5pac.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "ads.h"
+#include "libads/cldap.h"
+#include "../lib/addns/dnsquery.h"
+#include "../libds/common/flags.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "smb_krb5.h"
+#include "secrets.h"
+#include "krb5_env.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "lib/param/loadparm.h"
+#include "utils/net_dns.h"
+#include "auth/kerberos/pac_utils.h"
+#include "lib/util/string_wrappers.h"
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#include "auth/common_auth.h"
+#endif /* [HAVE_JANSSON] */
+
+#ifdef HAVE_ADS
+
+/* when we do not have sufficient input parameters to contact a remote domain
+ * we always fall back to our own realm - Guenther*/
+
+static const char *assume_own_realm(struct net_context *c)
+{
+ if (!c->opt_host && strequal(lp_workgroup(), c->opt_target_workgroup)) {
+ return lp_realm();
+ }
+
+ return NULL;
+}
+
+#ifdef HAVE_JANSSON
+
+/*
+ * note: JSON output deliberately bypasses gettext so as to provide the same
+ * output irrespective of the locale.
+ */
+
+static int output_json(const struct json_object *jsobj)
+{
+ TALLOC_CTX *ctx = NULL;
+ char *json = NULL;
+
+ if (json_is_invalid(jsobj)) {
+ return -1;
+ }
+
+ ctx = talloc_new(NULL);
+ if (ctx == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ return -1;
+ }
+
+ json = json_to_string(ctx, jsobj);
+ if (!json) {
+ d_fprintf(stderr, _("error encoding to JSON\n"));
+ return -1;
+ }
+
+ d_printf("%s\n", json);
+ TALLOC_FREE(ctx);
+
+ return 0;
+}
+
+static int net_ads_cldap_netlogon_json
+ (ADS_STRUCT *ads,
+ const char *addr,
+ const struct NETLOGON_SAM_LOGON_RESPONSE_EX *reply)
+{
+ struct json_object jsobj = json_new_object();
+ struct json_object flagsobj = json_new_object();
+ char response_type [32] = { '\0' };
+ int ret = 0;
+
+ if (json_is_invalid(&jsobj) || json_is_invalid(&flagsobj)) {
+ d_fprintf(stderr, _("error setting up JSON value\n"));
+
+ goto failure;
+ }
+
+ switch (reply->command) {
+ case LOGON_SAM_LOGON_USER_UNKNOWN_EX:
+ strncpy(response_type,
+ "LOGON_SAM_LOGON_USER_UNKNOWN_EX",
+ sizeof(response_type));
+ break;
+ case LOGON_SAM_LOGON_RESPONSE_EX:
+ strncpy(response_type, "LOGON_SAM_LOGON_RESPONSE_EX",
+ sizeof(response_type));
+ break;
+ default:
+ snprintf(response_type, sizeof(response_type), "0x%x",
+ reply->command);
+ break;
+ }
+
+ ret = json_add_string(&jsobj, "Information for Domain Controller",
+ addr);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Response Type", response_type);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_guid(&jsobj, "GUID", &reply->domain_uuid);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is a PDC",
+ reply->server_type & NBT_SERVER_PDC);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is a GC of the forest",
+ reply->server_type & NBT_SERVER_GC);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is an LDAP server",
+ reply->server_type & NBT_SERVER_LDAP);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Supports DS",
+ reply->server_type & NBT_SERVER_DS);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is running a KDC",
+ reply->server_type & NBT_SERVER_KDC);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is running time services",
+ reply->server_type & NBT_SERVER_TIMESERV);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is the closest DC",
+ reply->server_type & NBT_SERVER_CLOSEST);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is writable",
+ reply->server_type & NBT_SERVER_WRITABLE);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Has a hardware clock",
+ reply->server_type & NBT_SERVER_GOOD_TIMESERV);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj,
+ "Is a non-domain NC serviced by LDAP server",
+ reply->server_type & NBT_SERVER_NDNC);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool
+ (&flagsobj, "Is NT6 DC that has some secrets",
+ reply->server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool
+ (&flagsobj, "Is NT6 DC that has all secrets",
+ reply->server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Runs Active Directory Web Services",
+ reply->server_type & NBT_SERVER_ADS_WEB_SERVICE);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Runs on Windows 2012 or later",
+ reply->server_type & NBT_SERVER_DS_8);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Runs on Windows 2012R2 or later",
+ reply->server_type & NBT_SERVER_DS_9);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Runs on Windows 2016 or later",
+ reply->server_type & NBT_SERVER_DS_10);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Has a DNS name",
+ reply->server_type & NBT_SERVER_HAS_DNS_NAME);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is a default NC",
+ reply->server_type & NBT_SERVER_IS_DEFAULT_NC);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flagsobj, "Is the forest root",
+ reply->server_type & NBT_SERVER_FOREST_ROOT);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Forest", reply->forest);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Domain", reply->dns_domain);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Domain Controller", reply->pdc_dns_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+
+ ret = json_add_string(&jsobj, "Pre-Win2k Domain", reply->domain_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Pre-Win2k Hostname", reply->pdc_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ if (*reply->user_name) {
+ ret = json_add_string(&jsobj, "User name", reply->user_name);
+ if (ret != 0) {
+ goto failure;
+ }
+ }
+
+ ret = json_add_string(&jsobj, "Server Site Name", reply->server_site);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "Client Site Name", reply->client_site);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&jsobj, "NT Version", reply->nt_version);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&jsobj, "LMNT Token", reply->lmnt_token);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&jsobj, "LM20 Token", reply->lm20_token);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "Flags", &flagsobj);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = output_json(&jsobj);
+ json_free(&jsobj); /* frees flagsobj recursively */
+
+ return ret;
+
+failure:
+ json_free(&flagsobj);
+ json_free(&jsobj);
+
+ return ret;
+}
+
+#else /* [HAVE_JANSSON] */
+
+static int net_ads_cldap_netlogon_json
+ (ADS_STRUCT *ads,
+ const char *addr,
+ const struct NETLOGON_SAM_LOGON_RESPONSE_EX * reply)
+{
+ d_fprintf(stderr, _("JSON support not available\n"));
+
+ return -1;
+}
+
+#endif /* [HAVE_JANSSON] */
+
+/*
+ do a cldap netlogon query
+*/
+static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
+
+ print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+
+ if ( !ads_cldap_netlogon_5(talloc_tos(), &ads->ldap.ss, ads->server.realm, &reply ) ) {
+ d_fprintf(stderr, _("CLDAP query failed!\n"));
+ return -1;
+ }
+
+ if (c->opt_json) {
+ return net_ads_cldap_netlogon_json(ads, addr, &reply);
+ }
+
+ d_printf(_("Information for Domain Controller: %s\n\n"),
+ addr);
+
+ d_printf(_("Response Type: "));
+ switch (reply.command) {
+ case LOGON_SAM_LOGON_USER_UNKNOWN_EX:
+ d_printf("LOGON_SAM_LOGON_USER_UNKNOWN_EX\n");
+ break;
+ case LOGON_SAM_LOGON_RESPONSE_EX:
+ d_printf("LOGON_SAM_LOGON_RESPONSE_EX\n");
+ break;
+ default:
+ d_printf("0x%x\n", reply.command);
+ break;
+ }
+
+ d_printf(_("GUID: %s\n"), GUID_string(talloc_tos(),&reply.domain_uuid));
+
+ d_printf(_("Flags:\n"
+ "\tIs a PDC: %s\n"
+ "\tIs a GC of the forest: %s\n"
+ "\tIs an LDAP server: %s\n"
+ "\tSupports DS: %s\n"
+ "\tIs running a KDC: %s\n"
+ "\tIs running time services: %s\n"
+ "\tIs the closest DC: %s\n"
+ "\tIs writable: %s\n"
+ "\tHas a hardware clock: %s\n"
+ "\tIs a non-domain NC serviced by LDAP server: %s\n"
+ "\tIs NT6 DC that has some secrets: %s\n"
+ "\tIs NT6 DC that has all secrets: %s\n"
+ "\tRuns Active Directory Web Services: %s\n"
+ "\tRuns on Windows 2012 or later: %s\n"
+ "\tRuns on Windows 2012R2 or later: %s\n"
+ "\tRuns on Windows 2016 or later: %s\n"
+ "\tHas a DNS name: %s\n"
+ "\tIs a default NC: %s\n"
+ "\tIs the forest root: %s\n"),
+ (reply.server_type & NBT_SERVER_PDC) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_GC) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_LDAP) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_DS) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_KDC) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_TIMESERV) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_CLOSEST) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_WRITABLE) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_NDNC) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_ADS_WEB_SERVICE) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_DS_8) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_DS_9) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_DS_10) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_HAS_DNS_NAME) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_IS_DEFAULT_NC) ? _("yes") : _("no"),
+ (reply.server_type & NBT_SERVER_FOREST_ROOT) ? _("yes") : _("no"));
+
+
+ printf(_("Forest: %s\n"), reply.forest);
+ printf(_("Domain: %s\n"), reply.dns_domain);
+ printf(_("Domain Controller: %s\n"), reply.pdc_dns_name);
+
+ printf(_("Pre-Win2k Domain: %s\n"), reply.domain_name);
+ printf(_("Pre-Win2k Hostname: %s\n"), reply.pdc_name);
+
+ if (*reply.user_name) printf(_("User name: %s\n"), reply.user_name);
+
+ printf(_("Server Site Name: %s\n"), reply.server_site);
+ printf(_("Client Site Name: %s\n"), reply.client_site);
+
+ d_printf(_("NT Version: %d\n"), reply.nt_version);
+ d_printf(_("LMNT Token: %.2x\n"), reply.lmnt_token);
+ d_printf(_("LM20 Token: %.2x\n"), reply.lm20_token);
+
+ return 0;
+}
+
+/*
+ this implements the CLDAP based netlogon lookup requests
+ for finding the domain controller of a ADS domain
+*/
+static int net_ads_lookup(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n"
+ "net ads lookup\n"
+ " %s",
+ _("Usage:"),
+ _("Find the ADS DC using CLDAP lookup.\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup_nobind(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Didn't find the cldap server!\n"));
+ goto out;
+ }
+
+ if (!ads->config.realm) {
+ ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup);
+ if (ads->config.realm == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+ ads->ldap.port = 389;
+ }
+
+ ret = net_ads_cldap_netlogon(c, ads);
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+
+#ifdef HAVE_JANSSON
+
+static int net_ads_info_json(ADS_STRUCT *ads)
+{
+ int ret = 0;
+ char addr[INET6_ADDRSTRLEN];
+ time_t pass_time;
+ struct json_object jsobj = json_new_object();
+
+ if (json_is_invalid(&jsobj)) {
+ d_fprintf(stderr, _("error setting up JSON value\n"));
+
+ goto failure;
+ }
+
+ pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup);
+
+ print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+
+ ret = json_add_string (&jsobj, "LDAP server", addr);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string (&jsobj, "LDAP server name",
+ ads->config.ldap_server_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string (&jsobj, "Realm", ads->config.realm);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string (&jsobj, "Bind Path", ads->config.bind_path);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int (&jsobj, "LDAP port", ads->ldap.port);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int (&jsobj, "Server time", ads->config.current_time);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string (&jsobj, "KDC server", ads->auth.kdc_server);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int (&jsobj, "Server time offset",
+ ads->auth.time_offset);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int (&jsobj, "Last machine account password change",
+ pass_time);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = output_json(&jsobj);
+failure:
+ json_free(&jsobj);
+
+ return ret;
+}
+
+#else /* [HAVE_JANSSON] */
+
+static int net_ads_info_json(ADS_STRUCT *ads)
+{
+ d_fprintf(stderr, _("JSON support not available\n"));
+
+ return -1;
+}
+
+#endif /* [HAVE_JANSSON] */
+
+
+
+static int net_ads_info(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ char addr[INET6_ADDRSTRLEN];
+ time_t pass_time;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n"
+ "net ads info\n"
+ " %s",
+ _("Usage:"),
+ _("Display information about an Active Directory "
+ "server.\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup_nobind(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Didn't find the ldap server!\n"));
+ goto out;
+ }
+
+ if (!ads || !ads->config.realm) {
+ d_fprintf(stderr, _("Didn't find the ldap server!\n"));
+ goto out;
+ }
+
+ /* Try to set the server's current time since we didn't do a full
+ TCP LDAP session initially */
+
+ if ( !ADS_ERR_OK(ads_current_time( ads )) ) {
+ d_fprintf( stderr, _("Failed to get server's current time!\n"));
+ }
+
+ if (c->opt_json) {
+ ret = net_ads_info_json(ads);
+ goto out;
+ }
+
+ pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup);
+
+ print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+
+ d_printf(_("LDAP server: %s\n"), addr);
+ d_printf(_("LDAP server name: %s\n"), ads->config.ldap_server_name);
+ d_printf(_("Realm: %s\n"), ads->config.realm);
+ d_printf(_("Bind Path: %s\n"), ads->config.bind_path);
+ d_printf(_("LDAP port: %d\n"), ads->ldap.port);
+ d_printf(_("Server time: %s\n"),
+ http_timestring(tmp_ctx, ads->config.current_time));
+
+ d_printf(_("KDC server: %s\n"), ads->auth.kdc_server );
+ d_printf(_("Server time offset: %d\n"), ads->auth.time_offset );
+
+ d_printf(_("Last machine account password change: %s\n"),
+ http_timestring(tmp_ctx, pass_time));
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static ADS_STATUS ads_startup_int(struct net_context *c,
+ bool only_own_domain,
+ uint32_t auth_flags,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **ads_ret)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ bool need_password = false;
+ bool second_time = false;
+ char *cp;
+ const char *realm = NULL;
+ bool tried_closest_dc = false;
+ enum credentials_use_kerberos krb5_state =
+ CRED_USE_KERBEROS_DISABLED;
+
+ /* lp_realm() should be handled by a command line param,
+ However, the join requires that realm be set in smb.conf
+ and compares our realm with the remote server's so this is
+ ok until someone needs more flexibility */
+
+ *ads_ret = NULL;
+
+retry_connect:
+ if (only_own_domain) {
+ realm = lp_realm();
+ } else {
+ realm = assume_own_realm(c);
+ }
+
+ ads = ads_init(mem_ctx,
+ realm,
+ c->opt_target_workgroup,
+ c->opt_host,
+ ADS_SASL_PLAIN);
+ if (ads == NULL) {
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ if (!c->opt_user_name) {
+ c->opt_user_name = "administrator";
+ }
+
+ if (c->opt_user_specified) {
+ need_password = true;
+ }
+
+retry:
+ if (!c->opt_password && need_password && !c->opt_machine_pass) {
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+ if (!c->opt_password) {
+ TALLOC_FREE(ads);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ }
+
+ if (c->opt_password) {
+ use_in_memory_ccache();
+ ADS_TALLOC_CONST_FREE(ads->auth.password);
+ ads->auth.password = talloc_strdup(ads, c->opt_password);
+ if (ads->auth.password == NULL) {
+ TALLOC_FREE(ads);
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+ }
+
+ ADS_TALLOC_CONST_FREE(ads->auth.user_name);
+ ads->auth.user_name = talloc_strdup(ads, c->opt_user_name);
+ if (ads->auth.user_name == NULL) {
+ TALLOC_FREE(ads);
+ return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
+ }
+
+ ads->auth.flags |= auth_flags;
+
+ /* The ADS code will handle FIPS mode */
+ krb5_state = cli_credentials_get_kerberos_state(c->creds);
+ switch (krb5_state) {
+ case CRED_USE_KERBEROS_REQUIRED:
+ ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags &= ~ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ case CRED_USE_KERBEROS_DESIRED:
+ ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ case CRED_USE_KERBEROS_DISABLED:
+ ads->auth.flags |= ADS_AUTH_DISABLE_KERBEROS;
+ ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP;
+ break;
+ }
+
+ /*
+ * If the username is of the form "name@realm",
+ * extract the realm and convert to upper case.
+ * This is only used to establish the connection.
+ */
+ if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) {
+ *cp++ = '\0';
+ ADS_TALLOC_CONST_FREE(ads->auth.realm);
+ ads->auth.realm = talloc_asprintf_strupper_m(ads, "%s", cp);
+ if (ads->auth.realm == NULL) {
+ TALLOC_FREE(ads);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ } else if (ads->auth.realm == NULL) {
+ const char *c_realm = cli_credentials_get_realm(c->creds);
+
+ if (c_realm != NULL) {
+ ads->auth.realm = talloc_strdup(ads, c_realm);
+ if (ads->auth.realm == NULL) {
+ TALLOC_FREE(ads);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ }
+ }
+
+ status = ads_connect(ads);
+
+ if (!ADS_ERR_OK(status)) {
+
+ if (NT_STATUS_EQUAL(ads_ntstatus(status),
+ NT_STATUS_NO_LOGON_SERVERS)) {
+ DEBUG(0,("ads_connect: %s\n", ads_errstr(status)));
+ TALLOC_FREE(ads);
+ return status;
+ }
+
+ if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
+ need_password = true;
+ second_time = true;
+ goto retry;
+ } else {
+ TALLOC_FREE(ads);
+ return status;
+ }
+ }
+
+ /* when contacting our own domain, make sure we use the closest DC.
+ * This is done by reconnecting to ADS because only the first call to
+ * ads_connect will give us our own sitename */
+
+ if ((only_own_domain || !c->opt_host) && !tried_closest_dc) {
+
+ tried_closest_dc = true; /* avoid loop */
+
+ if (!ads_closest_dc(ads)) {
+
+ namecache_delete(ads->server.realm, 0x1C);
+ namecache_delete(ads->server.workgroup, 0x1C);
+
+ TALLOC_FREE(ads);
+
+ goto retry_connect;
+ }
+ }
+
+ *ads_ret = talloc_move(mem_ctx, &ads);
+ return status;
+}
+
+ADS_STATUS ads_startup(struct net_context *c,
+ bool only_own_domain,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **ads)
+{
+ return ads_startup_int(c, only_own_domain, 0, mem_ctx, ads);
+}
+
+ADS_STATUS ads_startup_nobind(struct net_context *c,
+ bool only_own_domain,
+ TALLOC_CTX *mem_ctx,
+ ADS_STRUCT **ads)
+{
+ return ads_startup_int(c,
+ only_own_domain,
+ ADS_AUTH_NO_BIND,
+ mem_ctx,
+ ads);
+}
+
+/*
+ Check to see if connection can be made via ads.
+ ads_startup() stores the password in opt_password if it needs to so
+ that rpc or rap can use it without re-prompting.
+*/
+static int net_ads_check_int(struct net_context *c,
+ const char *realm,
+ const char *workgroup,
+ const char *host)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ int ret = -1;
+
+ ads = ads_init(tmp_ctx, realm, workgroup, host, ADS_SASL_PLAIN);
+ if (ads == NULL) {
+ goto out;
+ }
+
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ status = ads_connect(ads);
+ if ( !ADS_ERR_OK(status) ) {
+ goto out;
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+int net_ads_check_our_domain(struct net_context *c)
+{
+ return net_ads_check_int(c, lp_realm(), lp_workgroup(), NULL);
+}
+
+int net_ads_check(struct net_context *c)
+{
+ return net_ads_check_int(c, NULL, c->opt_workgroup, c->opt_host);
+}
+
+/*
+ determine the netbios workgroup name for a domain
+ */
+static int net_ads_workgroup(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ struct NETLOGON_SAM_LOGON_RESPONSE_EX reply;
+ bool ok = false;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf ("%s\n"
+ "net ads workgroup\n"
+ " %s\n",
+ _("Usage:"),
+ _("Print the workgroup name"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup_nobind(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Didn't find the cldap server!\n"));
+ goto out;
+ }
+
+ if (!ads->config.realm) {
+ ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup);
+ if (ads->config.realm == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+ ads->ldap.port = 389;
+ }
+
+ ok = ads_cldap_netlogon_5(tmp_ctx,
+ &ads->ldap.ss, ads->server.realm, &reply);
+ if (!ok) {
+ d_fprintf(stderr, _("CLDAP query failed!\n"));
+ goto out;
+ }
+
+ d_printf(_("Workgroup: %s\n"), reply.domain_name);
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ret;
+}
+
+
+
+static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area)
+{
+ char **disp_fields = (char **) data_area;
+
+ if (!field) { /* must be end of record */
+ if (disp_fields[0]) {
+ if (!strchr_m(disp_fields[0], '$')) {
+ if (disp_fields[1])
+ d_printf("%-21.21s %s\n",
+ disp_fields[0], disp_fields[1]);
+ else
+ d_printf("%s\n", disp_fields[0]);
+ }
+ }
+ SAFE_FREE(disp_fields[0]);
+ SAFE_FREE(disp_fields[1]);
+ return true;
+ }
+ if (!values) /* must be new field, indicate string field */
+ return true;
+ if (strcasecmp_m(field, "sAMAccountName") == 0) {
+ disp_fields[0] = SMB_STRDUP((char *) values[0]);
+ }
+ if (strcasecmp_m(field, "description") == 0)
+ disp_fields[1] = SMB_STRDUP((char *) values[0]);
+ return true;
+}
+
+static int net_ads_user_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_user_usage(c, argc, argv);
+}
+
+static int ads_user_add(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ char *upn, *userdn;
+ LDAPMessage *res=NULL;
+ int rc = -1;
+ char *ou_str = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_user_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto done;
+ }
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_user_add: %s\n"), ads_errstr(status));
+ goto done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_fprintf(stderr, _("ads_user_add: User %s already exists\n"),
+ argv[0]);
+ goto done;
+ }
+
+ if (c->opt_container) {
+ ou_str = SMB_STRDUP(c->opt_container);
+ } else {
+ ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER);
+ }
+
+ status = ads_add_user_acct(ads, argv[0], ou_str, c->opt_comment);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Could not add user %s: %s\n"), argv[0],
+ ads_errstr(status));
+ goto done;
+ }
+
+ /* if no password is to be set, we're done */
+ if (argc == 1) {
+ d_printf(_("User %s added\n"), argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* try setting the password */
+ upn = talloc_asprintf(tmp_ctx,
+ "%s@%s",
+ argv[0],
+ ads->config.realm);
+ if (upn == NULL) {
+ goto done;
+ }
+
+ status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
+ ads->auth.time_offset);
+ if (ADS_ERR_OK(status)) {
+ d_printf(_("User %s added\n"), argv[0]);
+ rc = 0;
+ goto done;
+ }
+ TALLOC_FREE(upn);
+
+ /* password didn't set, delete account */
+ d_fprintf(stderr, _("Could not add user %s. "
+ "Error setting password %s\n"),
+ argv[0], ads_errstr(status));
+
+ ads_msgfree(ads, res);
+ res = NULL;
+
+ status=ads_find_user_acct(ads, &res, argv[0]);
+ if (ADS_ERR_OK(status)) {
+ userdn = ads_get_dn(ads, tmp_ctx, res);
+ ads_del_dn(ads, userdn);
+ TALLOC_FREE(userdn);
+ }
+
+ done:
+ ads_msgfree(ads, res);
+ SAFE_FREE(ou_str);
+ TALLOC_FREE(tmp_ctx);
+ return rc;
+}
+
+static int ads_user_info(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+ wbcErr wbc_status;
+ const char *attrs[] = {"memberOf", "primaryGroupID", NULL};
+ char *searchstring = NULL;
+ char **grouplist = NULL;
+ char *primary_group = NULL;
+ char *escaped_user = NULL;
+ struct dom_sid primary_group_sid;
+ uint32_t group_rid;
+ enum wbcSidType type;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_user_usage(c, argc, argv);
+ }
+
+ escaped_user = escape_ldap_string(tmp_ctx, argv[0]);
+ if (!escaped_user) {
+ d_fprintf(stderr,
+ _("ads_user_info: failed to escape user %s\n"),
+ argv[0]);
+ goto out;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ searchstring = talloc_asprintf(tmp_ctx,
+ "(sAMAccountName=%s)",
+ escaped_user);
+ if (searchstring == NULL) {
+ goto out;
+ }
+
+ status = ads_search(ads, &res, searchstring, attrs);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_search: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ if (!ads_pull_uint32(ads, res, "primaryGroupID", &group_rid)) {
+ d_fprintf(stderr, _("ads_pull_uint32 failed\n"));
+ goto out;
+ }
+
+ status = ads_domain_sid(ads, &primary_group_sid);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_domain_sid: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ sid_append_rid(&primary_group_sid, group_rid);
+
+ wbc_status = wbcLookupSid((struct wbcDomainSid *)&primary_group_sid,
+ NULL, /* don't look up domain */
+ &primary_group,
+ &type);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "wbcLookupSid: %s\n",
+ wbcErrorString(wbc_status));
+ goto out;
+ }
+
+ d_printf("%s\n", primary_group);
+
+ wbcFreeMemory(primary_group);
+
+ grouplist = ldap_get_values((LDAP *)ads->ldap.ld,
+ (LDAPMessage *)res, "memberOf");
+
+ if (grouplist) {
+ int i;
+ char **groupname;
+ for (i=0;grouplist[i];i++) {
+ groupname = ldap_explode_dn(grouplist[i], 1);
+ d_printf("%s\n", groupname[0]);
+ ldap_value_free(groupname);
+ }
+ ldap_value_free(grouplist);
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(escaped_user);
+ TALLOC_FREE(searchstring);
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int ads_user_delete(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ char *userdn = NULL;
+ int ret = -1;
+
+ if (argc < 1) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_user_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) {
+ d_printf(_("User %s does not exist.\n"), argv[0]);
+ goto out;
+ }
+
+ userdn = ads_get_dn(ads, tmp_ctx, res);
+ if (userdn == NULL) {
+ goto out;
+ }
+
+ status = ads_del_dn(ads, userdn);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Error deleting user %s: %s\n"), argv[0],
+ ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("User %s deleted\n"), argv[0]);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+int net_ads_user(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ ads_user_add,
+ NET_TRANSPORT_ADS,
+ N_("Add an AD user"),
+ N_("net ads user add\n"
+ " Add an AD user")
+ },
+ {
+ "info",
+ ads_user_info,
+ NET_TRANSPORT_ADS,
+ N_("Display information about an AD user"),
+ N_("net ads user info\n"
+ " Display information about an AD user")
+ },
+ {
+ "delete",
+ ads_user_delete,
+ NET_TRANSPORT_ADS,
+ N_("Delete an AD user"),
+ N_("net ads user delete\n"
+ " Delete an AD user")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+ int ret = -1;
+
+ if (argc > 0) {
+ TALLOC_FREE(tmp_ctx);
+ return net_run_function(c, argc, argv, "net ads user", func);
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads user\n"
+ " %s\n",
+ _("Usage:"),
+ _("List AD users"));
+ net_display_usage_from_functable(func);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (c->opt_long_list_entries)
+ d_printf(_("\nUser name Comment"
+ "\n-----------------------------\n"));
+
+ status = ads_do_search_all_fn(ads,
+ ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectCategory=user)",
+ c->opt_long_list_entries ?
+ longattrs : shortattrs,
+ usergrp_display,
+ disp_fields);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_group_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_group_usage(c, argc, argv);
+}
+
+static int ads_group_add(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+ char *ou_str = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_group_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_group_add: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_fprintf(stderr, _("ads_group_add: Group %s already exists\n"), argv[0]);
+ goto out;
+ }
+
+ if (c->opt_container) {
+ ou_str = SMB_STRDUP(c->opt_container);
+ } else {
+ ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER);
+ }
+
+ status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Could not add group %s: %s\n"), argv[0],
+ ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("Group %s added\n"), argv[0]);
+
+ ret = 0;
+ out:
+ ads_msgfree(ads, res);
+ SAFE_FREE(ou_str);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int ads_group_delete(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ char *groupdn = NULL;
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_group_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) {
+ d_printf(_("Group %s does not exist.\n"), argv[0]);
+ goto out;
+ }
+
+ groupdn = ads_get_dn(ads, tmp_ctx, res);
+ if (groupdn == NULL) {
+ goto out;
+ }
+
+ status = ads_del_dn(ads, groupdn);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Error deleting group %s: %s\n"), argv[0],
+ ads_errstr(status));
+ goto out;
+ }
+ d_printf(_("Group %s deleted\n"), argv[0]);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+int net_ads_group(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ ads_group_add,
+ NET_TRANSPORT_ADS,
+ N_("Add an AD group"),
+ N_("net ads group add\n"
+ " Add an AD group")
+ },
+ {
+ "delete",
+ ads_group_delete,
+ NET_TRANSPORT_ADS,
+ N_("Delete an AD group"),
+ N_("net ads group delete\n"
+ " Delete an AD group")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+ int ret = -1;
+
+ if (argc >= 0) {
+ TALLOC_FREE(tmp_ctx);
+ return net_run_function(c, argc, argv, "net ads group", func);
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads group\n"
+ " %s\n",
+ _("Usage:"),
+ _("List AD groups"));
+ net_display_usage_from_functable(func);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (c->opt_long_list_entries)
+ d_printf(_("\nGroup name Comment"
+ "\n-----------------------------\n"));
+
+ status = ads_do_search_all_fn(ads,
+ ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectCategory=group)",
+ c->opt_long_list_entries ?
+ longattrs : shortattrs,
+ usergrp_display,
+ disp_fields);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_status(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads status\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display machine account details"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_machine_acct(ads, &res, lp_netbios_name());
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_find_machine_acct: %s\n"),
+ ads_errstr(status));
+ goto out;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, _("No machine account for '%s' found\n"),
+ lp_netbios_name());
+ goto out;
+ }
+
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+/*******************************************************************
+ Leave an AD domain. Windows XP disables the machine account.
+ We'll try the same. The old code would do an LDAP delete.
+ That only worked using the machine creds because added the machine
+ with full control to the computer object's ACL.
+*******************************************************************/
+
+static int net_ads_leave(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ struct libnet_UnjoinCtx *r = NULL;
+ WERROR werr;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads leave [--keep-account]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Leave an AD domain"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (!*lp_realm()) {
+ d_fprintf(stderr, _("No realm set, are we joined ?\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (!c->opt_kerberos) {
+ use_in_memory_ccache();
+ }
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ goto done;
+ }
+
+ werr = libnet_init_UnjoinCtx(tmp_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("Could not initialise unjoin context.\n"));
+ goto done;
+ }
+
+ r->in.debug = true;
+ r->in.use_kerberos = c->opt_kerberos;
+ r->in.dc_name = c->opt_host;
+ r->in.domain_name = lp_realm();
+ r->in.admin_account = c->opt_user_name;
+ r->in.admin_password = net_prompt_pass(c, c->opt_user_name);
+ r->in.modify_config = lp_config_backend_is_registry();
+
+ /* Try to delete it, but if that fails, disable it. The
+ WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable */
+ r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+ if (c->opt_keep_account) {
+ r->in.delete_machine_account = false;
+ } else {
+ r->in.delete_machine_account = true;
+ }
+
+ r->in.msg_ctx = c->msg_ctx;
+
+ werr = libnet_Unjoin(tmp_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf(_("Failed to leave domain: %s\n"),
+ r->out.error_string ? r->out.error_string :
+ get_friendly_werror_msg(werr));
+ goto done;
+ }
+
+ if (r->out.deleted_machine_account) {
+ d_printf(_("Deleted account for '%s' in realm '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+ ret = 0;
+ goto done;
+ }
+
+ /* We couldn't delete it - see if the disable succeeded. */
+ if (r->out.disabled_machine_account) {
+ d_printf(_("Disabled account for '%s' in realm '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+ ret = 0;
+ goto done;
+ }
+
+ /* Based on what we requested, we shouldn't get here, but if
+ we did, it means the secrets were removed, and therefore
+ we have left the domain */
+ d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+
+ ret = 0;
+ done:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static ADS_STATUS net_ads_join_ok(struct net_context *c)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ fstring dc_name;
+ struct sockaddr_storage dcip;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ TALLOC_FREE(tmp_ctx);
+ return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED);
+ }
+
+ net_warn_member_options();
+
+ net_use_krb_machine_account(c);
+
+ get_dc_name(lp_workgroup(), lp_realm(), dc_name, &dcip);
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ADS_ERROR_NT(NT_STATUS_OK);
+out:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/*
+ check that an existing join is OK
+ */
+int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STATUS status;
+ use_in_memory_ccache();
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads testjoin\n"
+ " %s\n",
+ _("Usage:"),
+ _("Test if the existing join is ok"));
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ /* Display success or failure */
+ status = net_ads_join_ok(c);
+ if (!ADS_ERR_OK(status)) {
+ fprintf(stderr, _("Join to domain is not valid: %s\n"),
+ get_friendly_nt_error_msg(ads_ntstatus(status)));
+ return -1;
+ }
+
+ printf(_("Join is OK\n"));
+ return 0;
+}
+
+/*******************************************************************
+ Simple config checks before beginning the join
+ ********************************************************************/
+
+static WERROR check_ads_config( void )
+{
+ if (lp_server_role() != ROLE_DOMAIN_MEMBER ) {
+ d_printf(_("Host is not configured as a member server.\n"));
+ return WERR_INVALID_DOMAIN_ROLE;
+ }
+
+ if (strlen(lp_netbios_name()) > 15) {
+ d_printf(_("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n"), lp_netbios_name(),
+ (unsigned int)strlen(lp_netbios_name()));
+ return WERR_INVALID_COMPUTERNAME;
+ }
+
+ if ( lp_security() == SEC_ADS && !*lp_realm()) {
+ d_fprintf(stderr, _("realm must be set in %s for ADS "
+ "join to succeed.\n"), get_dyn_CONFIGFILE());
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static int net_ads_join_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net ads join [--no-dns-updates] [options]\n"
+ "Valid options:\n"));
+ d_printf(_(" dnshostname=FQDN Set the dnsHostName attribute during the join.\n"
+ " The default is in the form netbiosname.dnsdomain\n"));
+ d_printf(_(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n"
+ " The default UPN is in the form host/netbiosname@REALM.\n"));
+ d_printf(_(" createcomputer=OU Precreate the computer account in a specific OU.\n"
+ " The OU string read from top to bottom without RDNs\n"
+ " and delimited by a '/'.\n"
+ " E.g. \"createcomputer=Computers/Servers/Unix\"\n"
+ " NB: A backslash '\\' is used as escape at multiple\n"
+ " levels and may need to be doubled or even\n"
+ " quadrupled. It is not used as a separator.\n"));
+ d_printf(_(" machinepass=PASS Set the machine password to a specific value during\n"
+ " the join. The default password is random.\n"));
+ d_printf(_(" osName=string Set the operatingSystem attribute during the join.\n"));
+ d_printf(_(" osVer=string Set the operatingSystemVersion attribute during join.\n"
+ " NB: osName and osVer must be specified together for\n"
+ " either to take effect. The operatingSystemService\n"
+ " attribute is then also set along with the two\n"
+ " other attributes.\n"));
+ d_printf(_(" osServicePack=string Set the operatingSystemServicePack attribute\n"
+ " during the join.\n"
+ " NB: If not specified then by default the samba\n"
+ " version string is used instead.\n"));
+ return -1;
+}
+
+
+int net_ads_join(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ struct libnet_JoinCtx *r = NULL;
+ const char *domain = lp_realm();
+ WERROR werr = WERR_NERR_SETUPNOTJOINED;
+ bool createupn = false;
+ const char *dnshostname = NULL;
+ const char *machineupn = NULL;
+ const char *machine_password = NULL;
+ const char *create_in_ou = NULL;
+ int i;
+ const char *os_name = NULL;
+ const char *os_version = NULL;
+ const char *os_servicepack = NULL;
+ bool modify_config = lp_config_backend_is_registry();
+ enum libnetjoin_JoinDomNameType domain_name_type = JoinDomNameTypeDNS;
+ int ret = -1;
+
+ if (c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_join_usage(c, argc, argv);
+ }
+
+ net_warn_member_options();
+
+ if (!modify_config) {
+ werr = check_ads_config();
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("Invalid configuration. Exiting....\n"));
+ goto fail;
+ }
+ }
+
+ if (!c->opt_kerberos) {
+ use_in_memory_ccache();
+ }
+
+ werr = libnet_init_JoinCtx(tmp_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* process additional command line args */
+
+ for ( i=0; i<argc; i++ ) {
+ if ( !strncasecmp_m(argv[i], "dnshostname", strlen("dnshostname")) ) {
+ dnshostname = get_string_param(argv[i]);
+ }
+ else if ( !strncasecmp_m(argv[i], "createupn", strlen("createupn")) ) {
+ createupn = true;
+ machineupn = get_string_param(argv[i]);
+ }
+ else if ( !strncasecmp_m(argv[i], "createcomputer", strlen("createcomputer")) ) {
+ if ( (create_in_ou = get_string_param(argv[i])) == NULL ) {
+ d_fprintf(stderr, _("Please supply a valid OU path.\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "osName", strlen("osName")) ) {
+ if ( (os_name = get_string_param(argv[i])) == NULL ) {
+ d_fprintf(stderr, _("Please supply a operating system name.\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "osVer", strlen("osVer")) ) {
+ if ( (os_version = get_string_param(argv[i])) == NULL ) {
+ d_fprintf(stderr, _("Please supply a valid operating system version.\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "osServicePack", strlen("osServicePack")) ) {
+ if ( (os_servicepack = get_string_param(argv[i])) == NULL ) {
+ d_fprintf(stderr, _("Please supply a valid servicepack identifier.\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "machinepass", strlen("machinepass")) ) {
+ if ( (machine_password = get_string_param(argv[i])) == NULL ) {
+ d_fprintf(stderr, _("Please supply a valid password to set as trust account password.\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+ } else {
+ domain = argv[i];
+ if (strchr(domain, '.') == NULL) {
+ domain_name_type = JoinDomNameTypeUnknown;
+ } else {
+ domain_name_type = JoinDomNameTypeDNS;
+ }
+ }
+ }
+
+ if (!*domain) {
+ d_fprintf(stderr, _("Please supply a valid domain name\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto fail;
+ }
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ werr = WERR_ACCESS_DENIED;
+ goto fail;
+ }
+
+ /* Do the domain join here */
+
+ r->in.domain_name = domain;
+ r->in.domain_name_type = domain_name_type;
+ r->in.create_upn = createupn;
+ r->in.upn = machineupn;
+ r->in.dnshostname = dnshostname;
+ r->in.account_ou = create_in_ou;
+ r->in.os_name = os_name;
+ r->in.os_version = os_version;
+ r->in.os_servicepack = os_servicepack;
+ r->in.dc_name = c->opt_host;
+ r->in.admin_account = c->opt_user_name;
+ r->in.admin_password = net_prompt_pass(c, c->opt_user_name);
+ r->in.machine_password = machine_password;
+ r->in.debug = true;
+ r->in.use_kerberos = c->opt_kerberos;
+ r->in.modify_config = modify_config;
+ r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
+ WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
+ r->in.msg_ctx = c->msg_ctx;
+
+ werr = libnet_Join(tmp_ctx, r);
+ if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND) &&
+ strequal(domain, lp_realm())) {
+ r->in.domain_name = lp_workgroup();
+ r->in.domain_name_type = JoinDomNameTypeNBT;
+ werr = libnet_Join(tmp_ctx, r);
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* Check the short name of the domain */
+
+ if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
+ d_printf(_("The workgroup in %s does not match the short\n"
+ "domain name obtained from the server.\n"
+ "Using the name [%s] from the server.\n"
+ "You should set \"workgroup = %s\" in %s.\n"),
+ get_dyn_CONFIGFILE(), r->out.netbios_domain_name,
+ r->out.netbios_domain_name, get_dyn_CONFIGFILE());
+ }
+
+ d_printf(_("Using short domain name -- %s\n"), r->out.netbios_domain_name);
+
+ if (r->out.dns_domain_name) {
+ d_printf(_("Joined '%s' to dns domain '%s'\n"), r->in.machine_name,
+ r->out.dns_domain_name);
+ } else {
+ d_printf(_("Joined '%s' to domain '%s'\n"), r->in.machine_name,
+ r->out.netbios_domain_name);
+ }
+
+ /* print out informative error string in case there is one */
+ if (r->out.error_string != NULL) {
+ d_printf("%s\n", r->out.error_string);
+ }
+
+ /*
+ * We try doing the dns update (if it was compiled in
+ * and if it was not disabled on the command line).
+ * If the dns update fails, we still consider the join
+ * operation as succeeded if we came this far.
+ */
+ if (!c->opt_no_dns_updates) {
+ net_ads_join_dns_updates(c, tmp_ctx, r);
+ }
+
+ ret = 0;
+
+fail:
+ if (ret != 0) {
+ /* issue an overall failure message at the end. */
+ d_printf(_("Failed to join domain: %s\n"),
+ r && r->out.error_string ? r->out.error_string :
+ get_friendly_werror_msg(werr));
+ }
+
+ TALLOC_FREE(tmp_ctx);
+
+ return ret;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static int net_ads_dns_register(struct net_context *c, int argc, const char **argv)
+{
+#if defined(HAVE_KRB5)
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ NTSTATUS ntstatus;
+ const char *hostname = NULL;
+ const char **addrs_list = NULL;
+ struct sockaddr_storage *addrs = NULL;
+ int num_addrs = 0;
+ int count;
+ int ret = -1;
+
+#ifdef DEVELOPER
+ talloc_enable_leak_report();
+#endif
+
+ if (argc <= 1 && lp_clustering() && lp_cluster_addresses() == NULL) {
+ d_fprintf(stderr, _("Refusing DNS updates with automatic "
+ "detection of addresses in a clustered "
+ "setup.\n"));
+ c->display_usage = true;
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads dns register [hostname [IP [IP...]]] "
+ "[--force] [--dns-ttl TTL]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Register hostname with DNS\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (argc >= 1) {
+ hostname = argv[0];
+ }
+
+ if (argc > 1) {
+ num_addrs = argc - 1;
+ addrs_list = &argv[1];
+ } else if (lp_clustering()) {
+ addrs_list = lp_cluster_addresses();
+ num_addrs = str_list_length(addrs_list);
+ }
+
+ if (num_addrs > 0) {
+ addrs = talloc_zero_array(tmp_ctx,
+ struct sockaddr_storage,
+ num_addrs);
+ if (addrs == NULL) {
+ d_fprintf(stderr, _("Error allocating memory!\n"));
+ goto out;
+ }
+ }
+
+ for (count = 0; count < num_addrs; count++) {
+ if (!interpret_string_addr(&addrs[count], addrs_list[count], 0)) {
+ d_fprintf(stderr, "%s '%s'.\n",
+ _("Cannot interpret address"),
+ addrs_list[count]);
+ goto out;
+ }
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if ( !ADS_ERR_OK(status) ) {
+ DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+ goto out;
+ }
+
+ ntstatus = net_update_dns_ext(c,
+ tmp_ctx,
+ ads,
+ hostname,
+ addrs,
+ num_addrs,
+ false);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ d_fprintf( stderr, _("DNS update failed!\n") );
+ goto out;
+ }
+
+ d_fprintf( stderr, _("Successfully registered hostname with DNS\n") );
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ret;
+#else
+ d_fprintf(stderr,
+ _("DNS update support not enabled at compile time!\n"));
+ return -1;
+#endif
+}
+
+static int net_ads_dns_unregister(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+#if defined(HAVE_KRB5)
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ NTSTATUS ntstatus;
+ const char *hostname = NULL;
+ int ret = -1;
+
+#ifdef DEVELOPER
+ talloc_enable_leak_report();
+#endif
+
+ if (argc != 1) {
+ c->display_usage = true;
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads dns unregister [hostname]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Remove all IP Address entries for a given\n"
+ " hostname from the Active Directory server.\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ /* Get the hostname for un-registering */
+ hostname = argv[0];
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if ( !ADS_ERR_OK(status) ) {
+ DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+ goto out;
+ }
+
+ ntstatus = net_update_dns_ext(c,
+ tmp_ctx,
+ ads,
+ hostname,
+ NULL,
+ 0,
+ true);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ d_fprintf( stderr, _("DNS update failed!\n") );
+ goto out;
+ }
+
+ d_fprintf( stderr, _("Successfully un-registered hostname from DNS\n"));
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ret;
+#else
+ d_fprintf(stderr,
+ _("DNS update support not enabled at compile time!\n"));
+ return -1;
+#endif
+}
+
+
+static int net_ads_dns_async(struct net_context *c, int argc, const char **argv)
+{
+ size_t num_names = 0;
+ char **hostnames = NULL;
+ size_t i = 0;
+ struct samba_sockaddr *addrs = NULL;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf( "%s\n"
+ " %s\n"
+ " %s\n",
+ _("Usage:"),
+ _("net ads dns async <name>\n"),
+ _(" Async look up hostname from the DNS server\n"
+ " hostname\tName to look up\n"));
+ return -1;
+ }
+
+ status = ads_dns_lookup_a(talloc_tos(),
+ argv[0],
+ &num_names,
+ &hostnames,
+ &addrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Looking up A record for %s got error %s\n",
+ argv[0],
+ nt_errstr(status));
+ return -1;
+ }
+ d_printf("Async A record lookup - got %u names for %s\n",
+ (unsigned int)num_names,
+ argv[0]);
+ for (i = 0; i < num_names; i++) {
+ char addr_buf[INET6_ADDRSTRLEN];
+ print_sockaddr(addr_buf,
+ sizeof(addr_buf),
+ &addrs[i].u.ss);
+ d_printf("hostname[%u] = %s, IPv4addr = %s\n",
+ (unsigned int)i,
+ hostnames[i],
+ addr_buf);
+ }
+
+#if defined(HAVE_IPV6)
+ status = ads_dns_lookup_aaaa(talloc_tos(),
+ argv[0],
+ &num_names,
+ &hostnames,
+ &addrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Looking up AAAA record for %s got error %s\n",
+ argv[0],
+ nt_errstr(status));
+ return -1;
+ }
+ d_printf("Async AAAA record lookup - got %u names for %s\n",
+ (unsigned int)num_names,
+ argv[0]);
+ for (i = 0; i < num_names; i++) {
+ char addr_buf[INET6_ADDRSTRLEN];
+ print_sockaddr(addr_buf,
+ sizeof(addr_buf),
+ &addrs[i].u.ss);
+ d_printf("hostname[%u] = %s, IPv6addr = %s\n",
+ (unsigned int)i,
+ hostnames[i],
+ addr_buf);
+ }
+#endif
+ return 0;
+}
+
+
+static int net_ads_dns(struct net_context *c, int argc, const char *argv[])
+{
+ struct functable func[] = {
+ {
+ "register",
+ net_ads_dns_register,
+ NET_TRANSPORT_ADS,
+ N_("Add host dns entry to AD"),
+ N_("net ads dns register\n"
+ " Add host dns entry to AD")
+ },
+ {
+ "unregister",
+ net_ads_dns_unregister,
+ NET_TRANSPORT_ADS,
+ N_("Remove host dns entry from AD"),
+ N_("net ads dns unregister\n"
+ " Remove host dns entry from AD")
+ },
+ {
+ "async",
+ net_ads_dns_async,
+ NET_TRANSPORT_ADS,
+ N_("Look up host"),
+ N_("net ads dns async\n"
+ " Look up host using async DNS")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads dns", func);
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+int net_ads_printer_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+"\nnet ads printer search <printer>"
+"\n\tsearch for a printer in the directory\n"
+"\nnet ads printer info <printer> <server>"
+"\n\tlookup info in directory for printer on server"
+"\n\t(note: printer defaults to \"*\", server defaults to local)\n"
+"\nnet ads printer publish <printername>"
+"\n\tpublish printer in directory"
+"\n\t(note: printer name is required)\n"
+"\nnet ads printer remove <printername>"
+"\n\tremove printer from directory"
+"\n\t(note: printer name is required)\n"));
+ return -1;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static int net_ads_printer_search(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads printer search\n"
+ " %s\n",
+ _("Usage:"),
+ _("List printers in the AD"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_printers(ads, &res);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_find_printer: %s\n"),
+ ads_errstr(status));
+ goto out;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, _("No results found\n"));
+ goto out;
+ }
+
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_printer_info(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *servername = NULL;
+ const char *printername = NULL;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads printer info [printername [servername]]\n"
+ " Display printer info from AD\n"
+ " printername\tPrinter name or wildcard\n"
+ " servername\tName of the print server\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (argc > 0) {
+ printername = argv[0];
+ } else {
+ printername = "*";
+ }
+
+ if (argc > 1) {
+ servername = argv[1];
+ } else {
+ servername = lp_netbios_name();
+ }
+
+ status = ads_find_printer_on_server(ads, &res, printername, servername);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Server '%s' not found: %s\n"),
+ servername, ads_errstr(status));
+ goto out;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, _("Printer '%s' not found\n"), printername);
+ goto out;
+ }
+
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_printer_publish(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *servername = NULL;
+ const char *printername = NULL;
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct sockaddr_storage server_ss = { 0 };
+ NTSTATUS nt_status;
+ ADS_MODLIST mods = NULL;
+ char *prt_dn = NULL;
+ char *srv_dn = NULL;
+ char **srv_cn = NULL;
+ char *srv_cn_escaped = NULL;
+ char *printername_escaped = NULL;
+ LDAPMessage *res = NULL;
+ bool ok;
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads printer publish <printername> [servername]\n"
+ " Publish printer in AD\n"
+ " printername\tName of the printer\n"
+ " servername\tName of the print server\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ mods = ads_init_mods(tmp_ctx);
+ if (mods == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ printername = argv[0];
+
+ if (argc == 2) {
+ servername = argv[1];
+ } else {
+ servername = lp_netbios_name();
+ }
+
+ /* Get printer data from SPOOLSS */
+
+ ok = resolve_name(servername, &server_ss, 0x20, false);
+ if (!ok) {
+ d_fprintf(stderr, _("Could not find server %s\n"),
+ servername);
+ goto out;
+ }
+
+ cli_credentials_set_kerberos_state(c->creds,
+ CRED_USE_KERBEROS_REQUIRED,
+ CRED_SPECIFIED);
+
+ nt_status = cli_full_connection_creds(&cli, lp_netbios_name(), servername,
+ &server_ss, 0,
+ "IPC$", "IPC",
+ c->creds,
+ CLI_FULL_CONNECTION_IPC);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ d_fprintf(stderr, _("Unable to open a connection to %s to "
+ "obtain data for %s\n"),
+ servername, printername);
+ goto out;
+ }
+
+ /* Publish on AD server */
+
+ ads_find_machine_acct(ads, &res, servername);
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, _("Could not find machine account for server "
+ "%s\n"),
+ servername);
+ goto out;
+ }
+
+ srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res);
+ srv_cn = ldap_explode_dn(srv_dn, 1);
+
+ srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]);
+ printername_escaped = escape_rdn_val_string_alloc(printername);
+ if (!srv_cn_escaped || !printername_escaped) {
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(printername_escaped);
+ d_fprintf(stderr, _("Internal error, out of memory!"));
+ goto out;
+ }
+
+ prt_dn = talloc_asprintf(tmp_ctx,
+ "cn=%s-%s,%s",
+ srv_cn_escaped,
+ printername_escaped,
+ srv_dn);
+ if (prt_dn == NULL) {
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(printername_escaped);
+ d_fprintf(stderr, _("Internal error, out of memory!"));
+ goto out;
+ }
+
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(printername_escaped);
+
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss, &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, _("Unable to open a connection to the spoolss pipe on %s\n"),
+ servername);
+ goto out;
+ }
+
+ if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd,
+ tmp_ctx,
+ &mods,
+ printername))) {
+ goto out;
+ }
+
+ status = ads_add_printer_entry(ads, prt_dn, tmp_ctx, &mods);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, "ads_publish_printer: %s\n",
+ ads_errstr(status));
+ goto out;
+ }
+
+ d_printf("published printer\n");
+
+ ret = 0;
+out:
+ talloc_destroy(tmp_ctx);
+
+ return ret;
+}
+
+static int net_ads_printer_remove(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *servername = NULL;
+ char *prt_dn = NULL;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads printer remove <printername> [servername]\n"
+ " Remove a printer from the AD\n"
+ " printername\tName of the printer\n"
+ " servername\tName of the print server\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (argc > 1) {
+ servername = argv[1];
+ } else {
+ servername = lp_netbios_name();
+ }
+
+ status = ads_find_printer_on_server(ads, &res, argv[0], servername);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_find_printer_on_server: %s\n"),
+ ads_errstr(status));
+ goto out;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, _("Printer '%s' not found\n"), argv[1]);
+ goto out;
+ }
+
+ prt_dn = ads_get_dn(ads, tmp_ctx, res);
+ if (prt_dn == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ status = ads_del_dn(ads, prt_dn);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("ads_del_dn: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_printer(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "search",
+ net_ads_printer_search,
+ NET_TRANSPORT_ADS,
+ N_("Search for a printer"),
+ N_("net ads printer search\n"
+ " Search for a printer")
+ },
+ {
+ "info",
+ net_ads_printer_info,
+ NET_TRANSPORT_ADS,
+ N_("Display printer information"),
+ N_("net ads printer info\n"
+ " Display printer information")
+ },
+ {
+ "publish",
+ net_ads_printer_publish,
+ NET_TRANSPORT_ADS,
+ N_("Publish a printer"),
+ N_("net ads printer publish\n"
+ " Publish a printer")
+ },
+ {
+ "remove",
+ net_ads_printer_remove,
+ NET_TRANSPORT_ADS,
+ N_("Delete a printer"),
+ N_("net ads printer remove\n"
+ " Delete a printer")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads printer", func);
+}
+
+
+static int net_ads_password(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ const char *auth_principal = cli_credentials_get_username(c->creds);
+ const char *auth_password = cli_credentials_get_password(c->creds);
+ const char *realm = NULL;
+ char *new_password = NULL;
+ char *chr = NULL;
+ char *prompt = NULL;
+ const char *user = NULL;
+ char pwd[256] = {0};
+ ADS_STATUS status;
+ int ret = 0;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads password <username>\n"
+ " Change password for user\n"
+ " username\tName of user to change password for\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (auth_principal == NULL || auth_password == NULL) {
+ d_fprintf(stderr, _("You must supply an administrator "
+ "username/password\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (argc < 1) {
+ d_fprintf(stderr, _("ERROR: You must say which username to "
+ "change password for\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (strchr_m(argv[0], '@')) {
+ user = talloc_strdup(tmp_ctx, argv[0]);
+ } else {
+ user = talloc_asprintf(tmp_ctx, "%s@%s", argv[0], lp_realm());
+ }
+ if (user == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ use_in_memory_ccache();
+ chr = strchr_m(auth_principal, '@');
+ if (chr) {
+ realm = ++chr;
+ } else {
+ realm = lp_realm();
+ }
+
+ /* use the realm so we can eventually change passwords for users
+ in realms other than default */
+ ads = ads_init(tmp_ctx,
+ realm,
+ c->opt_workgroup,
+ c->opt_host,
+ ADS_SASL_PLAIN);
+ if (ads == NULL) {
+ goto out;
+ }
+
+ /* we don't actually need a full connect, but it's the easy way to
+ fill in the KDC's address */
+ ads_connect(ads);
+
+ if (!ads->config.realm) {
+ d_fprintf(stderr, _("Didn't find the kerberos server!\n"));
+ goto out;
+ }
+
+ if (argv[1] != NULL) {
+ new_password = talloc_strdup(tmp_ctx, argv[1]);
+ } else {
+ int rc;
+
+ prompt = talloc_asprintf(tmp_ctx, _("Enter new password for %s:"), user);
+ if (prompt == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true);
+ if (rc < 0) {
+ goto out;
+ }
+ new_password = talloc_strdup(tmp_ctx, pwd);
+ memset(pwd, '\0', sizeof(pwd));
+ }
+
+ if (new_password == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ status = kerberos_set_password(ads->auth.kdc_server,
+ auth_principal,
+ auth_password,
+ user,
+ new_password,
+ ads->auth.time_offset);
+ memset(new_password, '\0', strlen(new_password));
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Password change failed: %s\n"),
+ ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("Password change for %s completed.\n"), user);
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ char *host_principal = NULL;
+ char *my_name = NULL;
+ ADS_STATUS status;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads changetrustpw\n"
+ " %s\n",
+ _("Usage:"),
+ _("Change the machine account's trust password"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ goto out;
+ }
+
+ net_warn_member_options();
+
+ net_use_krb_machine_account(c);
+
+ use_in_memory_ccache();
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ my_name = talloc_asprintf_strlower_m(tmp_ctx, "%s", lp_netbios_name());
+ if (my_name == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ host_principal = talloc_asprintf(tmp_ctx, "%s$@%s", my_name, ads->config.realm);
+ if (host_principal == NULL) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ goto out;
+ }
+
+ d_printf(_("Changing password for principal: %s\n"), host_principal);
+
+ status = ads_change_trust_account_password(ads, host_principal);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("Password change for principal %s succeeded.\n"), host_principal);
+
+ if (USE_SYSTEM_KEYTAB) {
+ d_printf(_("Attempting to update system keytab with new password.\n"));
+ if (ads_keytab_create_default(ads)) {
+ d_printf(_("Failed to update system keytab.\n"));
+ }
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(tmp_ctx);
+
+ return ret;
+}
+
+/*
+ help for net ads search
+*/
+static int net_ads_search_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "\nnet ads search <expression> <attributes...>\n"
+ "\nPerform a raw LDAP search on a ADS server and dump the results.\n"
+ "The expression is a standard LDAP search expression, and the\n"
+ "attributes are a list of LDAP fields to show in the results.\n\n"
+ "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n"
+ ));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_search(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *ldap_exp = NULL;
+ const char **attrs = NULL;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_search_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ldap_exp = argv[0];
+ attrs = (argv + 1);
+
+ status = ads_do_search_retry(ads,
+ ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ ldap_exp,
+ attrs,
+ &res);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+
+/*
+ help for net ads search
+*/
+static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "\nnet ads dn <dn> <attributes...>\n"
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"
+ "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"
+ "to show in the results\n\n"
+ "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n"
+ "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n"
+ ));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_dn(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *dn = NULL;
+ const char **attrs = NULL;
+ LDAPMessage *res = NULL;
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_dn_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ dn = argv[0];
+ attrs = (argv + 1);
+
+ status = ads_do_search_all(ads,
+ dn,
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)",
+ attrs,
+ &res);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+/*
+ help for net ads sid search
+*/
+static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "\nnet ads sid <sid> <attributes...>\n"
+ "\nperform a raw LDAP search on a ADS server and dump the results\n"
+ "The SID is in string format, and the attributes are a list of LDAP fields \n"
+ "to show in the results\n\n"
+ "Example: net ads sid 'S-1-5-32' distinguishedName\n\n"
+ ));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+
+/*
+ general ADS search function. Useful in diagnosing problems in ADS
+*/
+static int net_ads_sid(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ const char *sid_string = NULL;
+ const char **attrs = NULL;
+ LDAPMessage *res = NULL;
+ struct dom_sid sid = { 0 };
+ int ret = -1;
+
+ if (argc < 1 || c->display_usage) {
+ TALLOC_FREE(tmp_ctx);
+ return net_ads_sid_usage(c, argc, argv);
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ sid_string = argv[0];
+ attrs = (argv + 1);
+
+ if (!string_to_sid(&sid, sid_string)) {
+ d_fprintf(stderr, _("could not convert sid\n"));
+ goto out;
+ }
+
+ status = ads_search_retry_sid(ads, &res, &sid, attrs);
+ if (!ADS_ERR_OK(status)) {
+ d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ret = 0;
+out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_keytab_flush(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads keytab flush\n"
+ " %s\n",
+ _("Usage:"),
+ _("Delete the whole keytab"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (!c->opt_user_specified && c->opt_password == NULL) {
+ net_use_krb_machine_account(c);
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ret = ads_keytab_flush(ads);
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_keytab_add(struct net_context *c,
+ int argc,
+ const char **argv,
+ bool update_ads)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ int i;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads keytab add <principal> [principal ...]\n"
+ " Add principals to local keytab\n"
+ " principal\tKerberos principal to add to "
+ "keytab\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ d_printf(_("Processing principals to add...\n"));
+
+ if (!c->opt_user_specified && c->opt_password == NULL) {
+ net_use_krb_machine_account(c);
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ for (ret = 0, i = 0; i < argc; i++) {
+ ret |= ads_keytab_add_entry(ads, argv[i], update_ads);
+ }
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_keytab_add_default(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ return net_ads_keytab_add(c, argc, argv, false);
+}
+
+static int net_ads_keytab_add_update_ads(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ return net_ads_keytab_add(c, argc, argv, true);
+}
+
+static int net_ads_keytab_delete(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ int i;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads keytab delete <principal> [principal ...]\n"
+ " Remove entries for service principal, "
+ " from the keytab file only."
+ " Remove principals from local keytab\n"
+ " principal\tKerberos principal to remove from "
+ "keytab\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ d_printf(_("Processing principals to delete...\n"));
+
+ if (!c->opt_user_specified && c->opt_password == NULL) {
+ net_use_krb_machine_account(c);
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ for (ret = 0, i = 0; i < argc; i++) {
+ ret |= ads_keytab_delete_entry(ads, argv[i]);
+ }
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads keytab create\n"
+ " %s\n",
+ _("Usage:"),
+ _("Create new default keytab"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ if (!c->opt_user_specified && c->opt_password == NULL) {
+ net_use_krb_machine_account(c);
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ret = ads_keytab_create_default(ads);
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_keytab_list(struct net_context *c, int argc, const char **argv)
+{
+ const char *keytab = NULL;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads keytab list [keytab]\n"
+ " List a local keytab\n"
+ " keytab\tKeytab to list\n"));
+ return -1;
+ }
+
+ if (argc >= 1) {
+ keytab = argv[0];
+ }
+
+ return ads_keytab_list(keytab);
+}
+
+
+int net_ads_keytab(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ net_ads_keytab_add_default,
+ NET_TRANSPORT_ADS,
+ N_("Add a service principal"),
+ N_("net ads keytab add\n"
+ " Add a service principal, updates keytab file only.")
+ },
+ {
+ "delete",
+ net_ads_keytab_delete,
+ NET_TRANSPORT_ADS,
+ N_("Delete a service principal"),
+ N_("net ads keytab delete\n"
+ " Remove entries for service principal, from the keytab file only.")
+ },
+ {
+ "add_update_ads",
+ net_ads_keytab_add_update_ads,
+ NET_TRANSPORT_ADS,
+ N_("Add a service principal"),
+ N_("net ads keytab add_update_ads\n"
+ " Add a service principal, depending on the param passed may update ADS computer object in addition to the keytab file.")
+ },
+ {
+ "create",
+ net_ads_keytab_create,
+ NET_TRANSPORT_ADS,
+ N_("Create a fresh keytab"),
+ N_("net ads keytab create\n"
+ " Create a fresh keytab or update existing one.")
+ },
+ {
+ "flush",
+ net_ads_keytab_flush,
+ NET_TRANSPORT_ADS,
+ N_("Remove all keytab entries"),
+ N_("net ads keytab flush\n"
+ " Remove all keytab entries")
+ },
+ {
+ "list",
+ net_ads_keytab_list,
+ NET_TRANSPORT_ADS,
+ N_("List a keytab"),
+ N_("net ads keytab list\n"
+ " List a keytab")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (!USE_KERBEROS_KEYTAB) {
+ d_printf(_("\nWarning: \"kerberos method\" must be set to a "
+ "keytab method to use keytab functions.\n"));
+ }
+
+ return net_run_function(c, argc, argv, "net ads keytab", func);
+}
+
+static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads kerberos renew\n"
+ " %s\n",
+ _("Usage:"),
+ _("Renew TGT from existing credential cache"));
+ return -1;
+ }
+
+ ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL);
+ if (ret) {
+ d_printf(_("failed to renew kerberos ticket: %s\n"),
+ error_message(ret));
+ }
+ return ret;
+}
+
+static int net_ads_kerberos_pac_common(struct net_context *c, int argc, const char **argv,
+ struct PAC_DATA_CTR **pac_data_ctr)
+{
+ NTSTATUS status;
+ int ret = -1;
+ const char *impersonate_princ_s = NULL;
+ const char *local_service = NULL;
+ int i;
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "impersonate", strlen("impersonate"))) {
+ impersonate_princ_s = get_string_param(argv[i]);
+ if (impersonate_princ_s == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "local_service", strlen("local_service"))) {
+ local_service = get_string_param(argv[i]);
+ if (local_service == NULL) {
+ return -1;
+ }
+ }
+ }
+
+ if (local_service == NULL) {
+ local_service = talloc_asprintf(c, "%s$@%s",
+ lp_netbios_name(), lp_realm());
+ if (local_service == NULL) {
+ goto out;
+ }
+ }
+
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+
+ status = kerberos_return_pac(c,
+ c->opt_user_name,
+ c->opt_password,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ true,
+ 2592000, /* one month */
+ impersonate_princ_s,
+ local_service,
+ NULL,
+ NULL,
+ pac_data_ctr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("failed to query kerberos PAC: %s\n"),
+ nt_errstr(status));
+ goto out;
+ }
+
+ ret = 0;
+ out:
+ return ret;
+}
+
+static int net_ads_kerberos_pac_dump(struct net_context *c, int argc, const char **argv)
+{
+ struct PAC_DATA_CTR *pac_data_ctr = NULL;
+ int i, num_buffers;
+ int ret = -1;
+ enum PAC_TYPE type = 0;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads kerberos pac dump [impersonate=string] [local_service=string] [pac_buffer_type=int]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Dump the Kerberos PAC"));
+ return -1;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "pac_buffer_type", strlen("pac_buffer_type"))) {
+ type = get_int_param(argv[i]);
+ }
+ }
+
+ ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr);
+ if (ret) {
+ return ret;
+ }
+
+ if (type == 0) {
+
+ char *s = NULL;
+
+ s = NDR_PRINT_STRUCT_STRING(c, PAC_DATA,
+ pac_data_ctr->pac_data);
+ if (s != NULL) {
+ d_printf(_("The Pac: %s\n"), s);
+ talloc_free(s);
+ }
+
+ return 0;
+ }
+
+ num_buffers = pac_data_ctr->pac_data->num_buffers;
+
+ for (i=0; i<num_buffers; i++) {
+
+ char *s = NULL;
+
+ if (pac_data_ctr->pac_data->buffers[i].type != type) {
+ continue;
+ }
+
+ s = NDR_PRINT_UNION_STRING(c, PAC_INFO, type,
+ pac_data_ctr->pac_data->buffers[i].info);
+ if (s != NULL) {
+ d_printf(_("The Pac: %s\n"), s);
+ talloc_free(s);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int net_ads_kerberos_pac_save(struct net_context *c, int argc, const char **argv)
+{
+ struct PAC_DATA_CTR *pac_data_ctr = NULL;
+ char *filename = NULL;
+ int ret = -1;
+ int i;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads kerberos pac save [impersonate=string] [local_service=string] [filename=string]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Save the Kerberos PAC"));
+ return -1;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "filename", strlen("filename"))) {
+ filename = get_string_param(argv[i]);
+ if (filename == NULL) {
+ return -1;
+ }
+ }
+ }
+
+ ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr);
+ if (ret) {
+ return ret;
+ }
+
+ if (filename == NULL) {
+ d_printf(_("please define \"filename=<filename>\" to save the PAC\n"));
+ return -1;
+ }
+
+ /* save the raw format */
+ if (!file_save(filename, pac_data_ctr->pac_blob.data, pac_data_ctr->pac_blob.length)) {
+ d_printf(_("failed to save PAC in %s\n"), filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "dump",
+ net_ads_kerberos_pac_dump,
+ NET_TRANSPORT_ADS,
+ N_("Dump Kerberos PAC"),
+ N_("net ads kerberos pac dump\n"
+ " Dump a Kerberos PAC to stdout")
+ },
+ {
+ "save",
+ net_ads_kerberos_pac_save,
+ NET_TRANSPORT_ADS,
+ N_("Save Kerberos PAC"),
+ N_("net ads kerberos pac save\n"
+ " Save a Kerberos PAC in a file")
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads kerberos pac", func);
+}
+
+static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+ NTSTATUS status;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads kerberos kinit\n"
+ " %s\n",
+ _("Usage:"),
+ _("Get Ticket Granting Ticket (TGT) for the user"));
+ return -1;
+ }
+
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+
+ ret = kerberos_kinit_password_ext(c->opt_user_name,
+ c->opt_password,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ true,
+ 2592000, /* one month */
+ NULL,
+ NULL,
+ NULL,
+ &status);
+ if (ret) {
+ d_printf(_("failed to kinit password: %s\n"),
+ nt_errstr(status));
+ }
+ return ret;
+}
+
+int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "kinit",
+ net_ads_kerberos_kinit,
+ NET_TRANSPORT_ADS,
+ N_("Retrieve Ticket Granting Ticket (TGT)"),
+ N_("net ads kerberos kinit\n"
+ " Receive Ticket Granting Ticket (TGT)")
+ },
+ {
+ "renew",
+ net_ads_kerberos_renew,
+ NET_TRANSPORT_ADS,
+ N_("Renew Ticket Granting Ticket from credential cache"),
+ N_("net ads kerberos renew\n"
+ " Renew Ticket Granting Ticket (TGT) from "
+ "credential cache")
+ },
+ {
+ "pac",
+ net_ads_kerberos_pac,
+ NET_TRANSPORT_ADS,
+ N_("Dump Kerberos PAC"),
+ N_("net ads kerberos pac\n"
+ " Dump Kerberos PAC")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads kerberos", func);
+}
+
+static int net_ads_setspn_list(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ bool ok = false;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads setspn list <machinename>\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (argc) {
+ ok = ads_setspn_list(ads, argv[0]);
+ } else {
+ ok = ads_setspn_list(ads, lp_netbios_name());
+ }
+
+ ret = ok ? 0 : -1;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_setspn_add(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ bool ok = false;
+ int ret = -1;
+
+ if (c->display_usage || argc < 1) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads setspn add <machinename> SPN\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (argc > 1) {
+ ok = ads_setspn_add(ads, argv[0], argv[1]);
+ } else {
+ ok = ads_setspn_add(ads, lp_netbios_name(), argv[0]);
+ }
+
+ ret = ok ? 0 : -1;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_setspn_delete(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ bool ok = false;
+ int ret = -1;
+
+ if (c->display_usage || argc < 1) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net ads setspn delete <machinename> SPN\n"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, true, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (argc > 1) {
+ ok = ads_setspn_delete(ads, argv[0], argv[1]);
+ } else {
+ ok = ads_setspn_delete(ads, lp_netbios_name(), argv[0]);
+ }
+
+ ret = ok ? 0 : -1;
+out:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+int net_ads_setspn(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_ads_setspn_list,
+ NET_TRANSPORT_ADS,
+ N_("List Service Principal Names (SPN)"),
+ N_("net ads setspn list machine\n"
+ " List Service Principal Names (SPN)")
+ },
+ {
+ "add",
+ net_ads_setspn_add,
+ NET_TRANSPORT_ADS,
+ N_("Add Service Principal Names (SPN)"),
+ N_("net ads setspn add machine spn\n"
+ " Add Service Principal Names (SPN)")
+ },
+ {
+ "delete",
+ net_ads_setspn_delete,
+ NET_TRANSPORT_ADS,
+ N_("Delete Service Principal Names (SPN)"),
+ N_("net ads setspn delete machine spn\n"
+ " Delete Service Principal Names (SPN)")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads setspn", func);
+}
+
+static int net_ads_enctype_lookup_account(struct net_context *c,
+ ADS_STRUCT *ads,
+ const char *account,
+ LDAPMessage **res,
+ const char **enctype_str)
+{
+ const char *filter;
+ const char *attrs[] = {
+ "msDS-SupportedEncryptionTypes",
+ NULL
+ };
+ int count;
+ int ret = -1;
+ ADS_STATUS status;
+
+ filter = talloc_asprintf(c, "(&(objectclass=user)(sAMAccountName=%s))",
+ account);
+ if (filter == NULL) {
+ goto done;
+ }
+
+ status = ads_search(ads, res, filter, attrs);
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("no account found with filter: %s\n"), filter);
+ goto done;
+ }
+
+ count = ads_count_replies(ads, *res);
+ switch (count) {
+ case 1:
+ break;
+ case 0:
+ d_printf(_("no account found with filter: %s\n"), filter);
+ goto done;
+ default:
+ d_printf(_("multiple accounts found with filter: %s\n"), filter);
+ goto done;
+ }
+
+ if (enctype_str) {
+ *enctype_str = ads_pull_string(ads, c, *res,
+ "msDS-SupportedEncryptionTypes");
+ if (*enctype_str == NULL) {
+ d_printf(_("no msDS-SupportedEncryptionTypes attribute found\n"));
+ goto done;
+ }
+ }
+
+ ret = 0;
+ done:
+ return ret;
+}
+
+static void net_ads_enctype_dump_enctypes(const char *username,
+ const char *enctype_str)
+{
+ int enctypes = atoi(enctype_str);
+
+ d_printf(_("'%s' uses \"msDS-SupportedEncryptionTypes\": %d (0x%08x)\n"),
+ username, enctypes, enctypes);
+
+ printf("[%s] 0x%08x DES-CBC-CRC\n",
+ enctypes & ENC_CRC32 ? "X" : " ",
+ ENC_CRC32);
+ printf("[%s] 0x%08x DES-CBC-MD5\n",
+ enctypes & ENC_RSA_MD5 ? "X" : " ",
+ ENC_RSA_MD5);
+ printf("[%s] 0x%08x RC4-HMAC\n",
+ enctypes & ENC_RC4_HMAC_MD5 ? "X" : " ",
+ ENC_RC4_HMAC_MD5);
+ printf("[%s] 0x%08x AES128-CTS-HMAC-SHA1-96\n",
+ enctypes & ENC_HMAC_SHA1_96_AES128 ? "X" : " ",
+ ENC_HMAC_SHA1_96_AES128);
+ printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96\n",
+ enctypes & ENC_HMAC_SHA1_96_AES256 ? "X" : " ",
+ ENC_HMAC_SHA1_96_AES256);
+ printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96-SK\n",
+ enctypes & ENC_HMAC_SHA1_96_AES256_SK ? "X" : " ",
+ ENC_HMAC_SHA1_96_AES256_SK);
+ printf("[%s] 0x%08x RESOURCE-SID-COMPRESSION-DISABLED\n",
+ enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED ? "X" : " ",
+ KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED);
+}
+
+static int net_ads_enctypes_list(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ ADS_STATUS status;
+ ADS_STRUCT *ads = NULL;
+ LDAPMessage *res = NULL;
+ const char *str = NULL;
+ int ret = -1;
+
+ if (c->display_usage || (argc < 1)) {
+ d_printf( "%s\n"
+ "net ads enctypes list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List supported enctypes"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str);
+ if (ret) {
+ goto out;
+ }
+
+ net_ads_enctype_dump_enctypes(argv[0], str);
+
+ ret = 0;
+ out:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_enctypes_set(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ int ret = -1;
+ ADS_STATUS status;
+ ADS_STRUCT *ads = NULL;
+ LDAPMessage *res = NULL;
+ const char *etype_list_str = NULL;
+ const char *dn = NULL;
+ ADS_MODLIST mods = NULL;
+ uint32_t etype_list;
+ const char *str = NULL;
+
+ if (c->display_usage || argc < 1) {
+ d_printf( "%s\n"
+ "net ads enctypes set <sAMAccountName> [enctypes]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Set supported enctypes"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto done;
+ }
+
+ ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
+ if (ret) {
+ goto done;
+ }
+
+ dn = ads_get_dn(ads, tmp_ctx, res);
+ if (dn == NULL) {
+ goto done;
+ }
+
+ etype_list = 0;
+ etype_list |= ENC_RC4_HMAC_MD5;
+ etype_list |= ENC_HMAC_SHA1_96_AES128;
+ etype_list |= ENC_HMAC_SHA1_96_AES256;
+
+ if (argv[1] != NULL) {
+ sscanf(argv[1], "%i", &etype_list);
+ }
+
+ etype_list_str = talloc_asprintf(tmp_ctx, "%d", etype_list);
+ if (!etype_list_str) {
+ goto done;
+ }
+
+ mods = ads_init_mods(tmp_ctx);
+ if (!mods) {
+ goto done;
+ }
+
+ status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes",
+ etype_list_str);
+ if (!ADS_ERR_OK(status)) {
+ goto done;
+ }
+
+ status = ads_gen_mod(ads, dn, mods);
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("failed to add msDS-SupportedEncryptionTypes: %s\n"),
+ ads_errstr(status));
+ goto done;
+ }
+
+ ads_msgfree(ads, res);
+ res = NULL;
+
+ ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str);
+ if (ret) {
+ goto done;
+ }
+
+ net_ads_enctype_dump_enctypes(argv[0], str);
+
+ ret = 0;
+ done:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_enctypes_delete(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ int ret = -1;
+ ADS_STATUS status;
+ ADS_STRUCT *ads = NULL;
+ LDAPMessage *res = NULL;
+ const char *dn = NULL;
+ ADS_MODLIST mods = NULL;
+
+ if (c->display_usage || argc < 1) {
+ d_printf( "%s\n"
+ "net ads enctypes delete <sAMAccountName>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Delete supported enctypes"));
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ status = ads_startup(c, false, tmp_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto done;
+ }
+
+ ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL);
+ if (ret) {
+ goto done;
+ }
+
+ dn = ads_get_dn(ads, tmp_ctx, res);
+ if (dn == NULL) {
+ goto done;
+ }
+
+ mods = ads_init_mods(tmp_ctx);
+ if (!mods) {
+ goto done;
+ }
+
+ status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes", NULL);
+ if (!ADS_ERR_OK(status)) {
+ goto done;
+ }
+
+ status = ads_gen_mod(ads, dn, mods);
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("failed to remove msDS-SupportedEncryptionTypes: %s\n"),
+ ads_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+
+ done:
+ ads_msgfree(ads, res);
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static int net_ads_enctypes(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_ads_enctypes_list,
+ NET_TRANSPORT_ADS,
+ N_("List the supported encryption types"),
+ N_("net ads enctypes list\n"
+ " List the supported encryption types")
+ },
+ {
+ "set",
+ net_ads_enctypes_set,
+ NET_TRANSPORT_ADS,
+ N_("Set the supported encryption types"),
+ N_("net ads enctypes set\n"
+ " Set the supported encryption types")
+ },
+ {
+ "delete",
+ net_ads_enctypes_delete,
+ NET_TRANSPORT_ADS,
+ N_("Delete the supported encryption types"),
+ N_("net ads enctypes delete\n"
+ " Delete the supported encryption types")
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads enctypes", func);
+}
+
+
+int net_ads(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "info",
+ net_ads_info,
+ NET_TRANSPORT_ADS,
+ N_("Display details on remote ADS server"),
+ N_("net ads info\n"
+ " Display details on remote ADS server")
+ },
+ {
+ "join",
+ net_ads_join,
+ NET_TRANSPORT_ADS,
+ N_("Join the local machine to ADS realm"),
+ N_("net ads join\n"
+ " Join the local machine to ADS realm")
+ },
+ {
+ "testjoin",
+ net_ads_testjoin,
+ NET_TRANSPORT_ADS,
+ N_("Validate machine account"),
+ N_("net ads testjoin\n"
+ " Validate machine account")
+ },
+ {
+ "leave",
+ net_ads_leave,
+ NET_TRANSPORT_ADS,
+ N_("Remove the local machine from ADS"),
+ N_("net ads leave\n"
+ " Remove the local machine from ADS")
+ },
+ {
+ "status",
+ net_ads_status,
+ NET_TRANSPORT_ADS,
+ N_("Display machine account details"),
+ N_("net ads status\n"
+ " Display machine account details")
+ },
+ {
+ "user",
+ net_ads_user,
+ NET_TRANSPORT_ADS,
+ N_("List/modify users"),
+ N_("net ads user\n"
+ " List/modify users")
+ },
+ {
+ "group",
+ net_ads_group,
+ NET_TRANSPORT_ADS,
+ N_("List/modify groups"),
+ N_("net ads group\n"
+ " List/modify groups")
+ },
+ {
+ "dns",
+ net_ads_dns,
+ NET_TRANSPORT_ADS,
+ N_("Issue dynamic DNS update"),
+ N_("net ads dns\n"
+ " Issue dynamic DNS update")
+ },
+ {
+ "password",
+ net_ads_password,
+ NET_TRANSPORT_ADS,
+ N_("Change user passwords"),
+ N_("net ads password\n"
+ " Change user passwords")
+ },
+ {
+ "changetrustpw",
+ net_ads_changetrustpw,
+ NET_TRANSPORT_ADS,
+ N_("Change trust account password"),
+ N_("net ads changetrustpw\n"
+ " Change trust account password")
+ },
+ {
+ "printer",
+ net_ads_printer,
+ NET_TRANSPORT_ADS,
+ N_("List/modify printer entries"),
+ N_("net ads printer\n"
+ " List/modify printer entries")
+ },
+ {
+ "search",
+ net_ads_search,
+ NET_TRANSPORT_ADS,
+ N_("Issue LDAP search using filter"),
+ N_("net ads search\n"
+ " Issue LDAP search using filter")
+ },
+ {
+ "dn",
+ net_ads_dn,
+ NET_TRANSPORT_ADS,
+ N_("Issue LDAP search by DN"),
+ N_("net ads dn\n"
+ " Issue LDAP search by DN")
+ },
+ {
+ "sid",
+ net_ads_sid,
+ NET_TRANSPORT_ADS,
+ N_("Issue LDAP search by SID"),
+ N_("net ads sid\n"
+ " Issue LDAP search by SID")
+ },
+ {
+ "workgroup",
+ net_ads_workgroup,
+ NET_TRANSPORT_ADS,
+ N_("Display workgroup name"),
+ N_("net ads workgroup\n"
+ " Display the workgroup name")
+ },
+ {
+ "lookup",
+ net_ads_lookup,
+ NET_TRANSPORT_ADS,
+ N_("Perform CLDAP query on DC"),
+ N_("net ads lookup\n"
+ " Find the ADS DC using CLDAP lookups")
+ },
+ {
+ "keytab",
+ net_ads_keytab,
+ NET_TRANSPORT_ADS,
+ N_("Manage local keytab file"),
+ N_("net ads keytab\n"
+ " Manage local keytab file")
+ },
+ {
+ "setspn",
+ net_ads_setspn,
+ NET_TRANSPORT_ADS,
+ N_("Manage Service Principal Names (SPN)s"),
+ N_("net ads spnset\n"
+ " Manage Service Principal Names (SPN)s")
+ },
+ {
+ "gpo",
+ net_ads_gpo,
+ NET_TRANSPORT_ADS,
+ N_("Manage group policy objects"),
+ N_("net ads gpo\n"
+ " Manage group policy objects")
+ },
+ {
+ "kerberos",
+ net_ads_kerberos,
+ NET_TRANSPORT_ADS,
+ N_("Manage kerberos keytab"),
+ N_("net ads kerberos\n"
+ " Manage kerberos keytab")
+ },
+ {
+ "enctypes",
+ net_ads_enctypes,
+ NET_TRANSPORT_ADS,
+ N_("List/modify supported encryption types"),
+ N_("net ads enctypes\n"
+ " List/modify enctypes")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads", func);
+}
+
+#else
+
+static int net_ads_noads(void)
+{
+ d_fprintf(stderr, _("ADS support not compiled in\n"));
+ return -1;
+}
+
+int net_ads_keytab(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_kerberos(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_setspn(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_join(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_user(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_group(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+int net_ads_gpo(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+/* this one shouldn't display a message */
+int net_ads_check(struct net_context *c)
+{
+ return -1;
+}
+
+int net_ads_check_our_domain(struct net_context *c)
+{
+ return -1;
+}
+
+int net_ads(struct net_context *c, int argc, const char **argv)
+{
+ return net_ads_noads();
+}
+
+#endif /* HAVE_ADS */
diff --git a/source3/utils/net_ads_gpo.c b/source3/utils/net_ads_gpo.c
new file mode 100644
index 0000000..12d8e22
--- /dev/null
+++ b/source3/utils/net_ads_gpo.c
@@ -0,0 +1,428 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads commands for Group Policy
+ Copyright (C) 2005-2008 Guenther Deschner (gd@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "ads.h"
+#include "../libgpo/gpo.h"
+#include "libgpo/gpo_proto.h"
+#include "../libds/common/flags.h"
+
+#ifdef HAVE_ADS
+
+static int net_ads_gpo_list_all(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ int num_reply = 0;
+ LDAPMessage *msg = NULL;
+ struct GROUP_POLICY_OBJECT gpo;
+ TALLOC_CTX *mem_ctx;
+ char *dn;
+ const char *attrs[] = {
+ "versionNumber",
+ "flags",
+ "gPCFileSysPath",
+ "displayName",
+ "name",
+ "gPCMachineExtensionNames",
+ "gPCUserExtensionNames",
+ "ntSecurityDescriptor",
+ NULL
+ };
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net ads gpo listall\n"
+ " %s\n",
+ _("Usage:"),
+ _("List all GPOs on the DC"));
+ return 0;
+ }
+
+ mem_ctx = talloc_init("net_ads_gpo_list_all");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_do_search_all_sd_flags(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ "(objectclass=groupPolicyContainer)",
+ attrs,
+ SECINFO_DACL,
+ &res);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("search failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+ num_reply = ads_count_replies(ads, res);
+
+ d_printf(_("Got %d replies\n\n"), num_reply);
+
+ /* dump the results */
+ for (msg = ads_first_entry(ads, res);
+ msg;
+ msg = ads_next_entry(ads, msg)) {
+
+ if ((dn = ads_get_dn(ads, mem_ctx, msg)) == NULL) {
+ goto out;
+ }
+
+ status = ads_parse_gpo(ads, mem_ctx, msg, dn, &gpo);
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("ads_parse_gpo failed: %s\n"),
+ ads_errstr(status));
+ goto out;
+ }
+
+ dump_gpo(&gpo, 0);
+ }
+
+out:
+ ads_msgfree(ads, res);
+
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+}
+
+static int net_ads_gpo_list(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ TALLOC_CTX *mem_ctx;
+ const char *dn = NULL;
+ uint32_t uac = 0;
+ uint32_t flags = 0;
+ struct GROUP_POLICY_OBJECT *gpo_list;
+ struct security_token *token = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s\n%s",
+ _("Usage:"),
+ _("net ads gpo list <username|machinename>"),
+ _(" Lists all GPOs for machine/user\n"
+ " username\tUser to list GPOs for\n"
+ " machinename\tMachine to list GPOs for\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("net_ads_gpo_list");
+ if (mem_ctx == NULL) {
+ goto out;
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_find_samaccount(ads, mem_ctx, argv[0], &uac, &dn);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (uac & UF_WORKSTATION_TRUST_ACCOUNT) {
+ flags |= GPO_LIST_FLAG_MACHINE;
+ }
+
+ d_printf(_("%s: '%s' has dn: '%s'\n"),
+ (uac & UF_WORKSTATION_TRUST_ACCOUNT) ? _("machine") : _("user"),
+ argv[0], dn);
+
+ if (uac & UF_WORKSTATION_TRUST_ACCOUNT) {
+ status = gp_get_machine_token(ads, mem_ctx, dn, &token);
+ } else {
+ status = ads_get_sid_token(ads, mem_ctx, dn, &token);
+ }
+
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_get_gpo_list(ads, mem_ctx, dn, flags, token, &gpo_list);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ dump_gpo_list(gpo_list, 0);
+
+out:
+ ads_msgfree(ads, res);
+
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+static int net_ads_gpo_link_get(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct GP_LINK gp_link;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s\n%s",
+ _("Usage:"),
+ _("net ads gpo linkget <container>"),
+ _(" Lists gPLink of a container\n"
+ " container\tContainer to get link for\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("add_gpo_link");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_get_gpo_link(ads, mem_ctx, argv[0], &gp_link);
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("get link for %s failed: %s\n"), argv[0],
+ ads_errstr(status));
+ goto out;
+ }
+
+ dump_gplink(&gp_link);
+
+out:
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+static int net_ads_gpo_link_add(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ uint32_t gpo_opt = 0;
+ TALLOC_CTX *mem_ctx;
+
+ if (argc < 2 || c->display_usage) {
+ d_printf("%s\n%s\n%s",
+ _("Usage:"),
+ _("net ads gpo linkadd <linkdn> <gpodn> [options]"),
+ _(" Link a container to a GPO\n"
+ " linkdn\tContainer to link to a GPO\n"
+ " gpodn\tGPO to link container to\n"));
+ d_printf(_("note: DNs must be provided properly escaped.\n"
+ "See RFC 4514 for details\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("add_gpo_link");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ if (argc == 3) {
+ gpo_opt = atoi(argv[2]);
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_add_gpo_link(ads, mem_ctx, argv[0], argv[1], gpo_opt);
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("link add failed: %s\n"), ads_errstr(status));
+ goto out;
+ }
+
+out:
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+#if 0 /* broken */
+
+static int net_ads_gpo_link_delete(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ TALLOC_CTX *mem_ctx;
+
+ if (argc < 2 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads gpo linkdelete <linkdn> <gpodn>\n"
+ " Delete a GPO link\n"
+ " <linkdn>\tContainer to delete GPO from\n"
+ " <gpodn>\tGPO to delete from container\n");
+ return -1;
+ }
+
+ mem_ctx = talloc_init("delete_gpo_link");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ status = ads_delete_gpo_link(ads, mem_ctx, argv[0], argv[1]);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("delete link failed: %s\n", ads_errstr(status));
+ goto out;
+ }
+
+out:
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+#endif
+
+/*
+Arguments:
+- struct net_context *: Pointer to net_context*
+- argc: Number of command line arguments passed to 'net ads gpo getgpo' command
+- **argv: Command line argument string passed to 'net ads gpo getgpo' command
+
+This function performs following operations:
+1. Create talloc context using talloc_init
+2. Perform ads_startup()
+3. Call ads_get_gpo() to retrieve gpo details inside 'struct GROUP_POLICY_OBJECT'
+4. Call dumps_gpo() to dump GPO on stdout
+*/
+static int net_ads_gpo_get_gpo(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct GROUP_POLICY_OBJECT gpo;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s\n%s",
+ _("Usage:"),
+ _("net ads gpo getgpo <gpo>"),
+ _(" List specified GPO\n"
+ " gpo\t\tGPO to list\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("ads_gpo_get_gpo");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, mem_ctx, &ads);
+ if (!ADS_ERR_OK(status)) {
+ goto out;
+ }
+
+ if (strnequal(argv[0], "CN={", strlen("CN={"))) {
+ status = ads_get_gpo(ads, mem_ctx, argv[0], NULL, NULL, &gpo);
+ } else {
+ status = ads_get_gpo(ads, mem_ctx, NULL, argv[0], NULL, &gpo);
+ }
+
+ if (!ADS_ERR_OK(status)) {
+ d_printf(_("get gpo for [%s] failed: %s\n"), argv[0],
+ ads_errstr(status));
+ goto out;
+ }
+
+ dump_gpo(&gpo, 0);
+
+out:
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+int net_ads_gpo(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "getgpo",
+ net_ads_gpo_get_gpo,
+ NET_TRANSPORT_ADS,
+ N_("List specified GPO"),
+ N_("net ads gpo getgpo\n"
+ " List specified GPO")
+ },
+ {
+ "linkadd",
+ net_ads_gpo_link_add,
+ NET_TRANSPORT_ADS,
+ N_("Link a container to a GPO"),
+ N_("net ads gpo linkadd\n"
+ " Link a container to a GPO")
+ },
+#if 0
+ {
+ "linkdelete",
+ net_ads_gpo_link_delete,
+ NET_TRANSPORT_ADS,
+ "Delete GPO link from a container",
+ "net ads gpo linkdelete\n"
+ " Delete GPO link from a container"
+ },
+#endif
+ {
+ "linkget",
+ net_ads_gpo_link_get,
+ NET_TRANSPORT_ADS,
+ N_("Lists gPLink of container"),
+ N_("net ads gpo linkget\n"
+ " Lists gPLink of container")
+ },
+ {
+ "list",
+ net_ads_gpo_list,
+ NET_TRANSPORT_ADS,
+ N_("Lists all GPOs for machine/user"),
+ N_("net ads gpo list\n"
+ " Lists all GPOs for machine/user")
+ },
+ {
+ "listall",
+ net_ads_gpo_list_all,
+ NET_TRANSPORT_ADS,
+ N_("Lists all GPOs on a DC"),
+ N_("net ads gpo listall\n"
+ " Lists all GPOs on a DC")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads gpo", func);
+}
+
+#endif /* HAVE_ADS */
diff --git a/source3/utils/net_ads_join_dns.c b/source3/utils/net_ads_join_dns.c
new file mode 100644
index 0000000..3437f96
--- /dev/null
+++ b/source3/utils/net_ads_join_dns.c
@@ -0,0 +1,342 @@
+/*
+ Samba Unix/Linux SMB client library
+ net ads dns internal functions
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2006 Gerald (Jerry) Carter (jerry@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "../lib/addns/dnsquery.h"
+#include "secrets.h"
+#include "krb5_env.h"
+#include "utils/net_dns.h"
+#include "lib/util/string_wrappers.h"
+
+#ifdef HAVE_ADS
+
+/*******************************************************************
+ Send a DNS update request
+*******************************************************************/
+
+#if defined(HAVE_KRB5)
+#include "../lib/addns/dns.h"
+
+void use_in_memory_ccache(void) {
+ /* Use in-memory credentials cache so we do not interfere with
+ * existing credentials */
+ setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1);
+}
+
+static NTSTATUS net_update_dns_internal(struct net_context *c,
+ TALLOC_CTX *ctx, ADS_STRUCT *ads,
+ const char *machine_name,
+ const struct sockaddr_storage *addrs,
+ int num_addrs, bool remove_host)
+{
+ struct dns_rr_ns *nameservers = NULL;
+ size_t ns_count = 0, i;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ DNS_ERROR dns_err;
+ fstring dns_server;
+ const char *dnsdomain = NULL;
+ char *root_domain = NULL;
+ uint32_t ttl = 3600;
+
+ if (c->opt_dns_ttl > 0) {
+ ttl = MIN(c->opt_dns_ttl, UINT32_MAX);
+ }
+
+ if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) {
+ d_printf(_("No DNS domain configured for %s. "
+ "Unable to perform DNS Update.\n"), machine_name);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+ dnsdomain++;
+
+ status = ads_dns_lookup_ns(ctx,
+ dnsdomain,
+ &nameservers,
+ &ns_count);
+ if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
+ /* Child domains often do not have NS records. Look
+ for the NS record for the forest root domain
+ (rootDomainNamingContext in therootDSE) */
+
+ const char *rootname_attrs[] = { "rootDomainNamingContext", NULL };
+ LDAPMessage *msg = NULL;
+ char *root_dn;
+ ADS_STATUS ads_status;
+
+ if ( !ads->ldap.ld ) {
+ ads_status = ads_connect( ads );
+ if ( !ADS_ERR_OK(ads_status) ) {
+ DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n"));
+ goto done;
+ }
+ }
+
+ ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
+ "(objectclass=*)", rootname_attrs, &msg);
+ if (!ADS_ERR_OK(ads_status)) {
+ goto done;
+ }
+
+ root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext");
+ if ( !root_dn ) {
+ ads_msgfree( ads, msg );
+ goto done;
+ }
+
+ root_domain = ads_build_domain( root_dn );
+
+ /* cleanup */
+ ads_msgfree( ads, msg );
+
+ /* try again for NS servers */
+
+ status = ads_dns_lookup_ns(ctx,
+ root_domain,
+ &nameservers,
+ &ns_count);
+
+ if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) {
+ DEBUG(3,("net_update_dns_internal: Failed to find name server for the %s "
+ "realm\n", ads->config.realm));
+ if (ns_count == 0) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+ goto done;
+ }
+
+ dnsdomain = root_domain;
+
+ }
+
+ for (i=0; i < ns_count; i++) {
+
+ uint32_t flags = DNS_UPDATE_SIGNED |
+ DNS_UPDATE_UNSIGNED |
+ DNS_UPDATE_UNSIGNED_SUFFICIENT |
+ DNS_UPDATE_PROBE |
+ DNS_UPDATE_PROBE_SUFFICIENT;
+
+ if (c->opt_force) {
+ flags &= ~DNS_UPDATE_PROBE_SUFFICIENT;
+ flags &= ~DNS_UPDATE_UNSIGNED_SUFFICIENT;
+ }
+
+ /*
+ * Do not return after PROBE completion if this function
+ * is called for DNS removal.
+ */
+ if (remove_host) {
+ flags &= ~DNS_UPDATE_PROBE_SUFFICIENT;
+ }
+
+ status = NT_STATUS_UNSUCCESSFUL;
+
+ /* Now perform the dns update - we'll try non-secure and if we fail,
+ we'll follow it up with a secure update */
+
+ fstrcpy( dns_server, nameservers[i].hostname );
+
+ dns_err = DoDNSUpdate(dns_server,
+ dnsdomain,
+ machine_name,
+ addrs,
+ num_addrs,
+ flags,
+ ttl,
+ remove_host);
+ if (ERR_DNS_IS_OK(dns_err)) {
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ if (ERR_DNS_EQUAL(dns_err, ERROR_DNS_INVALID_NAME_SERVER) ||
+ ERR_DNS_EQUAL(dns_err, ERROR_DNS_CONNECTION_FAILED) ||
+ ERR_DNS_EQUAL(dns_err, ERROR_DNS_SOCKET_ERROR)) {
+ DEBUG(1,("retrying DNS update with next nameserver after receiving %s\n",
+ dns_errstr(dns_err)));
+ continue;
+ }
+
+ d_printf(_("DNS Update for %s failed: %s\n"),
+ machine_name, dns_errstr(dns_err));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+done:
+
+ SAFE_FREE( root_domain );
+
+ return status;
+}
+
+NTSTATUS net_update_dns_ext(struct net_context *c,
+ TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
+ const char *hostname,
+ struct sockaddr_storage *iplist,
+ int num_addrs, bool remove_host)
+{
+ struct sockaddr_storage *iplist_alloc = NULL;
+ fstring machine_name;
+ NTSTATUS status;
+
+ if (hostname) {
+ fstrcpy(machine_name, hostname);
+ } else {
+ name_to_fqdn( machine_name, lp_netbios_name() );
+ }
+ if (!strlower_m( machine_name )) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * If remove_host is true, then remove all IP addresses associated with
+ * this hostname from the AD server.
+ */
+ if (!remove_host && (num_addrs == 0 || iplist == NULL)) {
+ /*
+ * Get our ip address
+ * (not the 127.0.0.x address but a real ip address)
+ */
+ num_addrs = get_my_ip_address(&iplist_alloc);
+ if ( num_addrs <= 0 ) {
+ DEBUG(4, ("net_update_dns_ext: Failed to find my "
+ "non-loopback IP addresses!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ iplist = iplist_alloc;
+ }
+
+ status = net_update_dns_internal(c, mem_ctx, ads, machine_name,
+ iplist, num_addrs, remove_host);
+
+ SAFE_FREE(iplist_alloc);
+ return status;
+}
+
+static NTSTATUS net_update_dns(struct net_context *c, TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, const char *hostname)
+{
+ NTSTATUS status;
+
+ status = net_update_dns_ext(c, mem_ctx, ads, hostname, NULL, 0, false);
+ return status;
+}
+#endif
+
+void net_ads_join_dns_updates(struct net_context *c, TALLOC_CTX *ctx, struct libnet_JoinCtx *r)
+{
+#if defined(HAVE_KRB5)
+ ADS_STRUCT *ads_dns = NULL;
+ int ret;
+ NTSTATUS status;
+ char *machine_password = NULL;
+
+ /*
+ * In a clustered environment, don't do dynamic dns updates:
+ * Registering the set of ip addresses that are assigned to
+ * the interfaces of the node that performs the join does usually
+ * not have the desired effect, since the local interfaces do not
+ * carry the complete set of the cluster's public IP addresses.
+ * And it can also contain internal addresses that should not
+ * be visible to the outside at all.
+ * In order to do dns updates in a clustererd setup, use
+ * net ads dns register.
+ */
+ if (lp_clustering()) {
+ d_fprintf(stderr, _("Not doing automatic DNS update in a "
+ "clustered setup.\n"));
+ return;
+ }
+
+ if (!r->out.domain_is_ad) {
+ return;
+ }
+
+ /*
+ * We enter this block with user creds.
+ * kinit with the machine password to do dns update.
+ */
+
+ ads_dns = ads_init(ctx,
+ lp_realm(),
+ NULL,
+ r->in.dc_name,
+ ADS_SASL_PLAIN);
+ if (ads_dns == NULL) {
+ d_fprintf(stderr, _("DNS update failed: out of memory!\n"));
+ goto done;
+ }
+
+ use_in_memory_ccache();
+
+ ads_dns->auth.user_name = talloc_asprintf(ads_dns,
+ "%s$",
+ lp_netbios_name());
+ if (ads_dns->auth.user_name == NULL) {
+ d_fprintf(stderr, _("DNS update failed: out of memory\n"));
+ goto done;
+ }
+
+ machine_password = secrets_fetch_machine_password(
+ r->out.netbios_domain_name, NULL, NULL);
+ if (machine_password != NULL) {
+ ads_dns->auth.password = talloc_strdup(ads_dns,
+ machine_password);
+ SAFE_FREE(machine_password);
+ if (ads_dns->auth.password == NULL) {
+ d_fprintf(stderr,
+ _("DNS update failed: out of memory\n"));
+ goto done;
+ }
+ }
+
+ ads_dns->auth.realm = talloc_asprintf_strupper_m(ads_dns, "%s", r->out.dns_domain_name);
+ if (ads_dns->auth.realm == NULL) {
+ d_fprintf(stderr, _("talloc_asprintf_strupper_m %s failed\n"),
+ ads_dns->auth.realm);
+ goto done;
+ }
+
+ ret = ads_kinit_password(ads_dns);
+ if (ret != 0) {
+ d_fprintf(stderr,
+ _("DNS update failed: kinit failed: %s\n"),
+ error_message(ret));
+ goto done;
+ }
+
+ status = net_update_dns(c, ctx, ads_dns, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf( stderr, _("DNS update failed: %s\n"),
+ nt_errstr(status));
+ }
+
+done:
+ TALLOC_FREE(ads_dns);
+#endif
+
+ return;
+}
+
+#endif /* HAVE_ADS */
diff --git a/source3/utils/net_afs.c b/source3/utils/net_afs.c
new file mode 100644
index 0000000..36d4310
--- /dev/null
+++ b/source3/utils/net_afs.c
@@ -0,0 +1,127 @@
+/*
+ Samba Unix/Linux SMB client library
+ net afs commands
+ Copyright (C) 2003 Volker Lendecke (vl@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "utils/net_afs.h"
+#include "secrets.h"
+#include "system/filesys.h"
+#include "lib/afs/afs_funcs.h"
+#include "lib/afs/afs_settoken.h"
+
+#ifdef WITH_FAKE_KASERVER
+
+int net_afs_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(" net afs key filename\n"
+ "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n"));
+ d_printf(_(" net afs impersonate <user> <cell>\n"
+ "\tCreates a token for user@cell\n\n"));
+ return -1;
+}
+
+int net_afs_key(struct net_context *c, int argc, const char **argv)
+{
+ int fd;
+ struct afs_keyfile keyfile;
+
+ if (argc != 2) {
+ d_printf("%s net afs key <keyfile> cell\n", _("Usage:"));
+ return -1;
+ }
+
+ if (!secrets_init()) {
+ d_fprintf(stderr, _("Could not open secrets.tdb\n"));
+ return -1;
+ }
+
+ if ((fd = open(argv[0], O_RDONLY, 0)) < 0) {
+ d_fprintf(stderr, _("Could not open %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) {
+ d_fprintf(stderr, _("Could not read keyfile\n"));
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ if (!secrets_store_afs_keyfile(argv[1], &keyfile)) {
+ d_fprintf(stderr, _("Could not write keyfile to secrets.tdb\n"));
+ ZERO_STRUCT(keyfile);
+ return -1;
+ }
+
+ ZERO_STRUCT(keyfile);
+ return 0;
+}
+
+int net_afs_impersonate(struct net_context *c, int argc,
+ const char **argv)
+{
+ char *token;
+
+ if (argc != 2) {
+ d_fprintf(stderr, "%s net afs impersonate <user> <cell>\n",
+ _("Usage:"));
+ exit(1);
+ }
+
+ token = afs_createtoken_str(argv[0], argv[1]);
+
+ if (token == NULL) {
+ fprintf(stderr, _("Could not create token\n"));
+ exit(1);
+ }
+
+ if (!afs_settoken_str(token)) {
+ fprintf(stderr, _("Could not set token into kernel\n"));
+ exit(1);
+ }
+
+ printf(_("Success: %s@%s\n"), argv[0], argv[1]);
+ return 0;
+}
+
+int net_afs(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "key",
+ net_afs_key,
+ NET_TRANSPORT_LOCAL,
+ N_("Import an OpenAFS keyfile"),
+ N_("net afs key <filename>\n"
+ " Import kefile from <filename>.")
+ },
+ {
+ "impersonate",
+ net_afs_impersonate,
+ NET_TRANSPORT_LOCAL,
+ N_("Get a user token"),
+ N_("net afs impersonate <user> <cell>\n"
+ " Create token for user@cell")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ return net_run_function(c, argc, argv, "net afs", func);
+}
+
+#endif /* WITH_FAKE_KASERVER */
diff --git a/source3/utils/net_afs.h b/source3/utils/net_afs.h
new file mode 100644
index 0000000..31606dd
--- /dev/null
+++ b/source3/utils/net_afs.h
@@ -0,0 +1,29 @@
+/*
+ Samba Unix/Linux SMB client library
+ net afs commands
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NET_AFS_H_
+#define _NET_AFS_H_
+
+int net_afs_usage(struct net_context *c, int argc, const char **argv);
+int net_afs_key(struct net_context *c, int argc, const char **argv);
+int net_afs_impersonate(struct net_context *c, int argc,
+ const char **argv);
+int net_afs(struct net_context *c, int argc, const char **argv);
+
+#endif /*_NET_AFS_H_*/
diff --git a/source3/utils/net_cache.c b/source3/utils/net_cache.c
new file mode 100644
index 0000000..a5a67ea
--- /dev/null
+++ b/source3/utils/net_cache.c
@@ -0,0 +1,652 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) Rafal Szczesniak 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "net.h"
+#include "libsmb/samlogon_cache.h"
+#include "../librpc/gen_ndr/netlogon.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/util/strv.h"
+#include "lib/gencache.h"
+
+/**
+ * @file net_cache.c
+ * @brief This is part of the net tool which is basically command
+ * line wrapper for gencache.c functions (mainly for testing)
+ *
+ **/
+
+
+/*
+ * These routines are used via gencache_iterate() to display the cache's contents
+ * (print_cache_entry) and to flush it (delete_cache_entry).
+ * Both of them are defined by first arg of gencache_iterate() routine.
+ */
+static void print_cache_entry(const char* keystr, DATA_BLOB value,
+ const time_t timeout, void* dptr)
+{
+ char *timeout_str;
+ char *alloc_str = NULL;
+ const char *datastr;
+ char *datastr_free = NULL;
+ time_t now_t = time(NULL);
+ struct tm timeout_tm, now_tm;
+ struct tm *ptimeout_tm, *pnow_tm;
+
+ ptimeout_tm = localtime_r(&timeout, &timeout_tm);
+ if (ptimeout_tm == NULL) {
+ return;
+ }
+ pnow_tm = localtime_r(&now_t, &now_tm);
+ if (pnow_tm == NULL) {
+ return;
+ }
+
+ /* form up timeout string depending whether it's today's date or not */
+ if (timeout_tm.tm_year != now_tm.tm_year ||
+ timeout_tm.tm_mon != now_tm.tm_mon ||
+ timeout_tm.tm_mday != now_tm.tm_mday) {
+
+ timeout_str = asctime(&timeout_tm);
+ if (!timeout_str) {
+ return;
+ }
+ timeout_str[strlen(timeout_str) - 1] = '\0'; /* remove tailing CR */
+ } else {
+ if (asprintf(&alloc_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour,
+ timeout_tm.tm_min, timeout_tm.tm_sec) == -1) {
+ return;
+ }
+ timeout_str = alloc_str;
+ }
+
+ datastr = (char *)value.data;
+
+ if (strnequal(keystr, "NAME2SID/", strlen("NAME2SID/"))) {
+ const char *strv = (char *)value.data;
+ size_t strv_len = value.length;
+ const char *sid = strv_len_next(strv, strv_len, NULL);
+ const char *type = strv_len_next(strv, strv_len, sid);
+ datastr = talloc_asprintf(talloc_tos(), "%s (%s)", sid, type);
+ }
+
+ if (strnequal(keystr, "SID2NAME/", strlen("SID2NAME/"))) {
+ const char *strv = (char *)value.data;
+ size_t strv_len = value.length;
+ const char *domain = strv_len_next(strv, strv_len, NULL);
+ const char *name = strv_len_next(strv, strv_len, domain);
+ const char *type = strv_len_next(strv, strv_len, name);
+ datastr = talloc_asprintf(talloc_tos(), "%s\\%s (%s)",
+ domain, name, type);
+ }
+
+ if ((value.length > 0) && (value.data[value.length-1] != '\0')) {
+ datastr_free = talloc_asprintf(
+ talloc_tos(), "<binary length %d>",
+ (int)value.length);
+ datastr = datastr_free;
+ if (datastr == NULL) {
+ datastr = "<binary>";
+ }
+ }
+
+ d_printf(_("Key: %s\t Timeout: %s\t Value: %s %s\n"), keystr,
+ timeout_str, datastr, timeout > now_t ? "": _("(expired)"));
+
+ SAFE_FREE(alloc_str);
+}
+
+static void delete_cache_entry(const char* keystr, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ if (!gencache_del(keystr))
+ d_fprintf(stderr, _("Couldn't delete entry! key = %s\n"),
+ keystr);
+}
+
+
+/**
+ * Parse text representation of timeout value
+ *
+ * @param timeout_str string containing text representation of the timeout
+ * @return numeric timeout of time_t type
+ **/
+static time_t parse_timeout(const char* timeout_str)
+{
+ char sign = '\0', *number = NULL, unit = '\0';
+ int len, number_begin, number_end;
+ time_t timeout;
+
+ /* sign detection */
+ if (timeout_str[0] == '!' || timeout_str[0] == '+') {
+ sign = timeout_str[0];
+ number_begin = 1;
+ } else {
+ number_begin = 0;
+ }
+
+ /* unit detection */
+ len = strlen(timeout_str);
+ switch (timeout_str[len - 1]) {
+ case 's':
+ case 'm':
+ case 'h':
+ case 'd':
+ case 'w': unit = timeout_str[len - 1];
+ }
+
+ /* number detection */
+ len = (sign) ? strlen(&timeout_str[number_begin]) : len;
+ number_end = (unit) ? len - 1 : len;
+ number = SMB_STRNDUP(&timeout_str[number_begin], number_end);
+
+ /* calculate actual timeout value */
+ timeout = (time_t)atoi(number);
+
+ switch (unit) {
+ case 'm': timeout *= 60; break;
+ case 'h': timeout *= 60*60; break;
+ case 'd': timeout *= 60*60*24; break;
+ case 'w': timeout *= 60*60*24*7; break; /* that's fair enough, I think :) */
+ }
+
+ switch (sign) {
+ case '!': timeout = time(NULL) - timeout; break;
+ case '+':
+ default: timeout += time(NULL); break;
+ }
+
+ if (number) SAFE_FREE(number);
+ return timeout;
+}
+
+
+/**
+ * Add an entry to the cache. If it does exist, then set it.
+ *
+ * @param c A net_context structure
+ * @param argv key, value and timeout are passed in command line
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_add(struct net_context *c, int argc, const char **argv)
+{
+ const char *keystr, *datastr, *timeout_str;
+ time_t timeout;
+
+ if (argc < 3 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net cache add <key string> <data string> "
+ "<timeout>\n"));
+ return -1;
+ }
+
+ keystr = argv[0];
+ datastr = argv[1];
+ timeout_str = argv[2];
+
+ /* parse timeout given in command line */
+ timeout = parse_timeout(timeout_str);
+ if (!timeout) {
+ d_fprintf(stderr, _("Invalid timeout argument.\n"));
+ return -1;
+ }
+
+ if (gencache_set(keystr, datastr, timeout)) {
+ d_printf(_("New cache entry stored successfully.\n"));
+ return 0;
+ }
+
+ d_fprintf(stderr, _("Entry couldn't be added. Perhaps there's already such a key.\n"));
+ return -1;
+}
+
+/**
+ * Delete an entry in the cache
+ *
+ * @param c A net_context structure
+ * @param argv key to delete an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_del(struct net_context *c, int argc, const char **argv)
+{
+ const char *keystr = argv[0];
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net cache del <key string>\n"));
+ return -1;
+ }
+
+ if(gencache_del(keystr)) {
+ d_printf(_("Entry deleted.\n"));
+ return 0;
+ }
+
+ d_fprintf(stderr, _("Couldn't delete specified entry\n"));
+ return -1;
+}
+
+
+/**
+ * Get and display an entry from the cache
+ *
+ * @param c A net_context structure
+ * @param argv key to search an entry of
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_get(struct net_context *c, int argc, const char **argv)
+{
+ const char* keystr = argv[0];
+ DATA_BLOB value;
+ time_t timeout;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net cache get <key>\n"));
+ return -1;
+ }
+
+ if (gencache_get_data_blob(keystr, NULL, &value, &timeout, NULL)) {
+ print_cache_entry(keystr, value, timeout, NULL);
+ data_blob_free(&value);
+ return 0;
+ }
+
+ d_fprintf(stderr, _("Failed to find entry\n"));
+ return -1;
+}
+
+
+/**
+ * Search an entry/entries in the cache
+ *
+ * @param c A net_context structure
+ * @param argv key pattern to match the entries to
+ * @return 0 on success, otherwise failure
+ **/
+static int net_cache_search(struct net_context *c, int argc, const char **argv)
+{
+ const char* pattern;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net cache search <pattern>\n"));
+ return -1;
+ }
+
+ pattern = argv[0];
+ gencache_iterate_blobs(print_cache_entry, NULL, pattern);
+ return 0;
+}
+
+
+/**
+ * List the contents of the cache
+ *
+ * @param c A net_context structure
+ * @param argv ignored in this functionality
+ * @return always returns 0
+ **/
+static int net_cache_list(struct net_context *c, int argc, const char **argv)
+{
+ const char* pattern = "*";
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net cache list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List all cache entries."));
+ return 0;
+ }
+ gencache_iterate_blobs(print_cache_entry, NULL, pattern);
+ return 0;
+}
+
+
+/**
+ * Flush the whole cache
+ *
+ * @param c A net_context structure
+ * @param argv ignored in this functionality
+ * @return always returns 0
+ **/
+static int net_cache_flush(struct net_context *c, int argc, const char **argv)
+{
+ const char* pattern = "*";
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net cache flush\n"
+ " %s",
+ _("Usage:"),
+ _("Delete all cache entries."));
+ return 0;
+ }
+ gencache_iterate(delete_cache_entry, NULL, pattern);
+ return 0;
+}
+
+static int netsamlog_cache_for_all_cb(const char *sid_str,
+ time_t when_cached,
+ struct netr_SamInfo3 *info3,
+ void *private_data)
+{
+ struct net_context *c = (struct net_context *)private_data;
+ char *name = NULL;
+
+ name = talloc_asprintf(c, "%s\\%s",
+ info3->base.logon_domain.string,
+ info3->base.account_name.string);
+ if (name == NULL) {
+ return -1;
+ }
+
+ d_printf("%-50s %-40s %s\n",
+ sid_str,
+ name,
+ timestring(c, when_cached));
+
+ return 0;
+}
+
+static int net_cache_samlogon_list(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ int ret;
+
+ d_printf("%-50s %-40s When cached\n", "SID", "Name");
+ d_printf("------------------------------------------------------------"
+ "------------------------------------------------------------"
+ "----\n");
+
+ ret = netsamlog_cache_for_all(netsamlog_cache_for_all_cb, c);
+ if (ret == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_cache_samlogon_show(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ const char *sid_str = argv[0];
+ struct dom_sid sid;
+ struct dom_sid *user_sids = NULL;
+ uint32_t num_user_sids;
+ struct netr_SamInfo3 *info3 = NULL;
+ char *name = NULL;
+ uint32_t i;
+ NTSTATUS status;
+ bool ok;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n"
+ "net cache samlogon show SID\n"
+ " %s\n",
+ _("Usage:"),
+ _("Show samlogon cache entry for SID."));
+ return 0;
+ }
+
+ ok = string_to_sid(&sid, sid_str);
+ if (!ok) {
+ d_printf("String to SID failed for %s\n", sid_str);
+ return -1;
+ }
+
+ info3 = netsamlogon_cache_get(c, &sid);
+ if (info3 == NULL) {
+ d_printf("SID %s not found in samlogon cache\n", sid_str);
+ return -1;
+ }
+
+ name = talloc_asprintf(c, "%s\\%s",
+ info3->base.logon_domain.string,
+ info3->base.account_name.string);
+ if (name == NULL) {
+ return -1;
+ }
+
+ d_printf("Name: %s\n", name);
+
+ status = sid_array_from_info3(c,
+ info3,
+ &user_sids,
+ &num_user_sids,
+ true);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(user_sids);
+ d_printf("sid_array_from_info3 failed for %s\n", sid_str);
+ return -1;
+ }
+
+ for (i = 0; i < num_user_sids; i++) {
+ struct dom_sid_buf buf;
+ d_printf("SID %2" PRIu32 ": %s\n",
+ i,
+ dom_sid_str_buf(&user_sids[i], &buf));
+ }
+
+ TALLOC_FREE(user_sids);
+
+ return 0;
+}
+
+static int net_cache_samlogon_ndrdump(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ const char *sid_str = NULL;
+ struct dom_sid sid;
+ struct netr_SamInfo3 *info3 = NULL;
+ struct ndr_print *ndr_print = NULL;
+ bool ok;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf( "%s\n"
+ "net cache samlogon ndrdump SID\n"
+ " %s\n",
+ _("Usage:"),
+ _("Show samlogon cache entry for SID."));
+ return 0;
+ }
+
+ sid_str = argv[0];
+
+ ok = string_to_sid(&sid, sid_str);
+ if (!ok) {
+ d_printf("String to SID failed for %s\n", sid_str);
+ return -1;
+ }
+
+ info3 = netsamlogon_cache_get(c, &sid);
+ if (info3 == NULL) {
+ d_printf("SID %s not found in samlogon cache\n", sid_str);
+ return -1;
+ }
+
+ ndr_print = talloc_zero(c, struct ndr_print);
+ if (ndr_print == NULL) {
+ d_printf("Could not allocate memory.\n");
+ return -1;
+ }
+
+ ndr_print->print = ndr_print_printf_helper;
+ ndr_print->depth = 1;
+ ndr_print_netr_SamInfo3(ndr_print, "netr_SamInfo3", info3);
+ TALLOC_FREE(ndr_print);
+
+ return 0;
+}
+
+static int net_cache_samlogon_delete(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ const char *sid_str = argv[0];
+ struct dom_sid sid;
+ bool ok;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf( "%s\n"
+ "net cache samlogon delete SID\n"
+ " %s\n",
+ _("Usage:"),
+ _("Delete samlogon cache entry for SID."));
+ return 0;
+ }
+
+ ok = string_to_sid(&sid, sid_str);
+ if (!ok) {
+ d_printf("String to SID failed for %s\n", sid_str);
+ return -1;
+ }
+
+ netsamlogon_clear_cached_user(&sid);
+
+ return 0;
+}
+
+static int net_cache_samlogon(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_cache_samlogon_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List samlogon cache"),
+ N_("net cache samlogon list\n"
+ " List samlogon cachen\n")
+ },
+ {
+ "show",
+ net_cache_samlogon_show,
+ NET_TRANSPORT_LOCAL,
+ N_("Show samlogon cache entry"),
+ N_("net cache samlogon show SID\n"
+ " Show samlogon cache entry\n")
+ },
+ {
+ "ndrdump",
+ net_cache_samlogon_ndrdump,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump the samlogon cache entry NDR blob"),
+ N_("net cache samlogon ndrdump SID\n"
+ " Dump the samlogon cache entry NDR blob\n")
+ },
+ {
+ "delete",
+ net_cache_samlogon_delete,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete samlogon cache entry"),
+ N_("net cache samlogon delete SID\n"
+ " Delete samlogon cache entry\n")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net cache samlogon", func);
+}
+
+/**
+ * Entry point to 'net cache' subfunctionality
+ *
+ * @param c A net_context structure
+ * @param argv arguments passed to further called functions
+ * @return whatever further functions return
+ **/
+int net_cache(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ net_cache_add,
+ NET_TRANSPORT_LOCAL,
+ N_("Add new cache entry"),
+ N_("net cache add <key string> <data string> <timeout>\n"
+ " Add new cache entry.\n"
+ " key string\tKey string to add cache data under.\n"
+ " data string\tData to store under given key.\n"
+ " timeout\tTimeout for cache data.")
+ },
+ {
+ "del",
+ net_cache_del,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete existing cache entry by key"),
+ N_("net cache del <key string>\n"
+ " Delete existing cache entry by key.\n"
+ " key string\tKey string to delete.")
+ },
+ {
+ "get",
+ net_cache_get,
+ NET_TRANSPORT_LOCAL,
+ N_("Get cache entry by key"),
+ N_("net cache get <key string>\n"
+ " Get cache entry by key.\n"
+ " key string\tKey string to look up cache entry for.")
+
+ },
+ {
+ "search",
+ net_cache_search,
+ NET_TRANSPORT_LOCAL,
+ N_("Search entry by pattern"),
+ N_("net cache search <pattern>\n"
+ " Search entry by pattern.\n"
+ " pattern\tPattern to search for in cache.")
+ },
+ {
+ "list",
+ net_cache_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List all cache entries"),
+ N_("net cache list\n"
+ " List all cache entries")
+ },
+ {
+ "flush",
+ net_cache_flush,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete all cache entries"),
+ N_("net cache flush\n"
+ " Delete all cache entries")
+ },
+ {
+ "samlogon",
+ net_cache_samlogon,
+ NET_TRANSPORT_LOCAL,
+ N_("List contents of the samlogon cache"),
+ N_("net cache samlogon\n"
+ " List contents of the samlogon cache")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net cache", func);
+}
diff --git a/source3/utils/net_conf.c b/source3/utils/net_conf.c
new file mode 100644
index 0000000..c16f240
--- /dev/null
+++ b/source3/utils/net_conf.c
@@ -0,0 +1,1305 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Local configuration interface
+ * Copyright (C) Michael Adam 2007-2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is an interface to Samba's configuration as made available
+ * by the libsmbconf interface (source/lib/smbconf/smbconf.c).
+ *
+ * This currently supports local interaction with the configuration
+ * stored in the registry. But other backends and remote access via
+ * rpc might get implemented in the future.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "utils/net.h"
+#include "utils/net_conf_util.h"
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_init.h"
+#include "lib/smbconf/smbconf_reg.h"
+#include "lib/param/loadparm.h"
+
+/**********************************************************************
+ *
+ * usage functions
+ *
+ **********************************************************************/
+
+static int net_conf_list_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s net conf list\n", _("Usage:"));
+ return -1;
+}
+
+static int net_conf_import_usage(struct net_context *c, int argc,
+ const char**argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf import [--test|-T] <filename> "
+ "[<servicename>]\n"
+ "\t[--test|-T] testmode - do not act, just print "
+ "what would be done\n"
+ "\t<servicename> only import service <servicename>, "
+ "ignore the rest\n"));
+ return -1;
+}
+
+static int net_conf_listshares_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet conf listshares\n", _("Usage:"));
+ return -1;
+}
+
+static int net_conf_drop_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet conf drop\n", _("Usage:"));
+ return -1;
+}
+
+static int net_conf_showshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net conf showshare <sharename>\n"));
+ return -1;
+}
+
+static int net_conf_addshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf addshare <sharename> <path> "
+ "[writeable={y|N} [guest_ok={y|N} [<comment>]]]\n"
+ "\t<sharename> the new share name.\n"
+ "\t<path> the path on the filesystem to export.\n"
+ "\twriteable={y|N} set \"writeable to \"yes\" or "
+ "\"no\" (default) on this share.\n"
+ "\tguest_ok={y|N} set \"guest ok\" to \"yes\" or "
+ "\"no\" (default) on this share.\n"
+ "\t<comment> optional comment for the new share.\n"));
+ return -1;
+}
+
+static int net_conf_delshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net conf delshare <sharename>\n"));
+ return -1;
+}
+
+static int net_conf_setparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf setparm <section> <param> <value>\n"));
+ return -1;
+}
+
+static int net_conf_getparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf getparm <section> <param>\n"));
+ return -1;
+}
+
+static int net_conf_delparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf delparm <section> <param>\n"));
+ return -1;
+}
+
+static int net_conf_getincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf getincludes <section>\n"));
+ return -1;
+}
+
+static int net_conf_setincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf setincludes <section> [<filename>]*\n"));
+ return -1;
+}
+
+static int net_conf_delincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net conf delincludes <section>\n"));
+ return -1;
+}
+
+
+/**********************************************************************
+ *
+ * Helper functions
+ *
+ **********************************************************************/
+
+/**
+ * This functions process a service previously loaded with libsmbconf.
+ */
+static sbcErr import_process_service(struct net_context *c,
+ struct smbconf_ctx *conf_ctx,
+ struct smbconf_service *service)
+{
+ sbcErr err = SBC_ERR_OK;
+
+ if (c->opt_testmode) {
+ uint32_t idx;
+ const char *indent = "";
+ if (service->name != NULL) {
+ d_printf("[%s]\n", service->name);
+ indent = "\t";
+ }
+ for (idx = 0; idx < service->num_params; idx++) {
+ d_printf("%s%s = %s\n", indent,
+ service->param_names[idx],
+ service->param_values[idx]);
+ }
+ d_printf("\n");
+ goto done;
+ }
+
+ if (smbconf_share_exists(conf_ctx, service->name)) {
+ err = smbconf_delete_share(conf_ctx, service->name);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+ }
+
+ err = smbconf_create_set_share(conf_ctx, service);
+
+done:
+ return err;
+}
+
+
+/**********************************************************************
+ *
+ * the main conf functions
+ *
+ **********************************************************************/
+
+static int net_conf_list(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ sbcErr err;
+ int ret = -1;
+ TALLOC_CTX *mem_ctx;
+ uint32_t num_shares;
+ uint32_t share_count, param_count;
+ struct smbconf_service **shares = NULL;
+
+ mem_ctx = talloc_stackframe();
+
+ if (argc != 0 || c->display_usage) {
+ net_conf_list_usage(c, argc, argv);
+ goto done;
+ }
+
+ err = smbconf_get_config(conf_ctx, mem_ctx, &num_shares, &shares);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error getting config: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ for (share_count = 0; share_count < num_shares; share_count++) {
+ const char *indent = "";
+ if (shares[share_count]->name != NULL) {
+ d_printf("[%s]\n", shares[share_count]->name);
+ indent = "\t";
+ }
+ for (param_count = 0;
+ param_count < shares[share_count]->num_params;
+ param_count++)
+ {
+ d_printf("%s%s = %s\n",
+ indent,
+ shares[share_count]->param_names[param_count],
+ shares[share_count]->param_values[param_count]);
+ }
+ d_printf("\n");
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_import(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ const char *filename = NULL;
+ const char *servicename = NULL;
+ char *conf_source = NULL;
+ TALLOC_CTX *mem_ctx;
+ struct smbconf_ctx *txt_ctx;
+ sbcErr err;
+
+ if (c->display_usage)
+ return net_conf_import_usage(c, argc, argv);
+
+ mem_ctx = talloc_stackframe();
+
+ switch (argc) {
+ case 0:
+ default:
+ net_conf_import_usage(c, argc, argv);
+ goto done;
+ case 2:
+ servicename = talloc_strdup(mem_ctx, argv[1]);
+ if (servicename == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ FALL_THROUGH;
+ case 1:
+ filename = argv[0];
+ break;
+ }
+
+ DEBUG(3,("net_conf_import: reading configuration from file %s.\n",
+ filename));
+
+ conf_source = talloc_asprintf(mem_ctx, "file:%s", filename);
+ if (conf_source == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ err = smbconf_init(mem_ctx, &txt_ctx, conf_source);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error loading file '%s': %s\n"), filename,
+ sbcErrorString(err));
+ goto done;
+ }
+
+ if (c->opt_testmode) {
+ d_printf(_("\nTEST MODE - "
+ "would import the following configuration:\n\n"));
+ }
+
+ if (servicename != NULL) {
+ struct smbconf_service *service = NULL;
+
+ err = smbconf_get_share(txt_ctx, mem_ctx,
+ servicename,
+ &service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto cancel;
+ }
+
+ err = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error starting transaction: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ err = import_process_service(c, conf_ctx, service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error importing service %s: %s\n"),
+ servicename, sbcErrorString(err));
+ goto cancel;
+ }
+ } else {
+ struct smbconf_service **services = NULL;
+ uint32_t num_shares, sidx;
+
+ err = smbconf_get_config(txt_ctx, mem_ctx,
+ &num_shares,
+ &services);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto cancel;
+ }
+ if (!c->opt_testmode) {
+ if (!SBC_ERROR_IS_OK(smbconf_drop(conf_ctx))) {
+ goto cancel;
+ }
+ }
+
+ /*
+ * Wrap the importing of shares into a transaction,
+ * but only 100 at a time, in order to save memory.
+ * The allocated memory accumulates across the actions
+ * within the transaction, and for me, some 1500
+ * imported shares, the MAX_TALLOC_SIZE of 256 MB
+ * was exceeded.
+ */
+ err = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error starting transaction: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ for (sidx = 0; sidx < num_shares; sidx++) {
+ err = import_process_service(c, conf_ctx,
+ services[sidx]);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error importing service %s: %s\n"),
+ services[sidx]->name,
+ sbcErrorString(err));
+ goto cancel;
+ }
+
+ if (sidx % 100) {
+ continue;
+ }
+
+ err = smbconf_transaction_commit(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error committing transaction: "
+ "%s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+ err = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error starting transaction: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+ }
+ }
+
+ err = smbconf_transaction_commit(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error committing transaction: %s\n"),
+ sbcErrorString(err));
+ } else {
+ ret = 0;
+ }
+
+ goto done;
+
+cancel:
+ err = smbconf_transaction_cancel(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error cancelling transaction: %s\n"),
+ sbcErrorString(err));
+ }
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_listshares(struct net_context *c,
+ struct smbconf_ctx *conf_ctx, int argc,
+ const char **argv)
+{
+ sbcErr err;
+ int ret = -1;
+ uint32_t count, num_shares = 0;
+ char **share_names = NULL;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_stackframe();
+
+ if (argc != 0 || c->display_usage) {
+ net_conf_listshares_usage(c, argc, argv);
+ goto done;
+ }
+
+ err = smbconf_get_share_names(conf_ctx, mem_ctx, &num_shares,
+ &share_names);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto done;
+ }
+
+ for (count = 0; count < num_shares; count++)
+ {
+ d_printf("%s\n", share_names[count]);
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_drop(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+
+ if (argc != 0 || c->display_usage) {
+ net_conf_drop_usage(c, argc, argv);
+ goto done;
+ }
+
+ err = smbconf_drop(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error deleting configuration: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ return ret;
+}
+
+static int net_conf_showshare(struct net_context *c,
+ struct smbconf_ctx *conf_ctx, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+ const char *sharename = NULL;
+ TALLOC_CTX *mem_ctx;
+ uint32_t count;
+ struct smbconf_service *service = NULL;
+
+ mem_ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ net_conf_showshare_usage(c, argc, argv);
+ goto done;
+ }
+
+ sharename = talloc_strdup(mem_ctx, argv[0]);
+ if (sharename == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ err = smbconf_get_share(conf_ctx, mem_ctx, sharename, &service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error getting share parameters: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ d_printf("[%s]\n", service->name);
+
+ for (count = 0; count < service->num_params; count++) {
+ d_printf("\t%s = %s\n", service->param_names[count],
+ service->param_values[count]);
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+/**
+ * Add a share, with a couple of standard parameters, partly optional.
+ *
+ * This is a high level utility function of the net conf utility,
+ * not a direct frontend to the smbconf API.
+ */
+static int net_conf_addshare(struct net_context *c,
+ struct smbconf_ctx *conf_ctx, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+ char *sharename = NULL;
+ const char *path = NULL;
+ const char *comment = NULL;
+ const char *guest_ok = "no";
+ const char *writeable = "no";
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (c->display_usage) {
+ net_conf_addshare_usage(c, argc, argv);
+ ret = 0;
+ goto done;
+ }
+
+ switch (argc) {
+ case 0:
+ case 1:
+ default:
+ net_conf_addshare_usage(c, argc, argv);
+ goto done;
+ case 5:
+ comment = argv[4];
+
+ FALL_THROUGH;
+ case 4:
+ if (!strnequal(argv[3], "guest_ok=", 9)) {
+ net_conf_addshare_usage(c, argc, argv);
+ goto done;
+ }
+ switch (argv[3][9]) {
+ case 'y':
+ case 'Y':
+ guest_ok = "yes";
+ break;
+ case 'n':
+ case 'N':
+ guest_ok = "no";
+ break;
+ default:
+ net_conf_addshare_usage(c, argc, argv);
+ goto done;
+ }
+
+ FALL_THROUGH;
+ case 3:
+ if (!strnequal(argv[2], "writeable=", 10)) {
+ net_conf_addshare_usage(c, argc, argv);
+ goto done;
+ }
+ switch (argv[2][10]) {
+ case 'y':
+ case 'Y':
+ writeable = "yes";
+ break;
+ case 'n':
+ case 'N':
+ writeable = "no";
+ break;
+ default:
+ net_conf_addshare_usage(c, argc, argv);
+ goto done;
+ }
+
+ FALL_THROUGH;
+ case 2:
+ path = argv[1];
+ sharename = talloc_strdup(mem_ctx, argv[0]);
+ if (sharename == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ break;
+ }
+
+ /*
+ * validate arguments
+ */
+
+ /* validate share name */
+
+ if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS,
+ strlen(sharename)))
+ {
+ d_fprintf(stderr, _("ERROR: share name %s contains "
+ "invalid characters (any of %s)\n"),
+ sharename, INVALID_SHARENAME_CHARS);
+ goto done;
+ }
+
+ if (strequal(sharename, GLOBAL_NAME)) {
+ d_fprintf(stderr,
+ _("ERROR: 'global' is not a valid share name.\n"));
+ goto done;
+ }
+
+ if (smbconf_share_exists(conf_ctx, sharename)) {
+ d_fprintf(stderr, _("ERROR: share %s already exists.\n"),
+ sharename);
+ goto done;
+ }
+
+ /* validate path */
+
+ if (path[0] != '/') {
+ bool ok = false;
+
+ if (strequal(sharename, HOMES_NAME) && path[0] == '\0') {
+ /* The homes share can be an empty path. */
+ ok = true;
+ }
+ if (!ok) {
+ d_fprintf(stderr,
+ _("Error: path '%s' is not an absolute path.\n"),
+ path);
+ goto done;
+ }
+ }
+
+ /*
+ * start a transaction
+ */
+
+ err = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf("error starting transaction: %s\n",
+ sbcErrorString(err));
+ goto done;
+ }
+
+ /*
+ * create the share
+ */
+
+ err = smbconf_create_share(conf_ctx, sharename);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error creating share %s: %s\n"),
+ sharename, sbcErrorString(err));
+ goto cancel;
+ }
+
+ /*
+ * fill the share with parameters
+ */
+
+ err = smbconf_set_parameter(conf_ctx, sharename, "path", path);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error setting parameter %s: %s\n"),
+ "path", sbcErrorString(err));
+ goto cancel;
+ }
+
+ if (comment != NULL) {
+ err = smbconf_set_parameter(conf_ctx, sharename, "comment",
+ comment);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error setting parameter %s: %s\n"),
+ "comment", sbcErrorString(err));
+ goto cancel;
+ }
+ }
+
+ err = smbconf_set_parameter(conf_ctx, sharename, "guest ok", guest_ok);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error setting parameter %s: %s\n"),
+ "'guest ok'", sbcErrorString(err));
+ goto cancel;
+ }
+
+ err = smbconf_set_parameter(conf_ctx, sharename, "writeable",
+ writeable);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error setting parameter %s: %s\n"),
+ "writeable", sbcErrorString(err));
+ goto cancel;
+ }
+
+ /*
+ * commit the whole thing
+ */
+
+ err = smbconf_transaction_commit(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf("error committing transaction: %s\n",
+ sbcErrorString(err));
+ } else {
+ ret = 0;
+ }
+
+ goto done;
+
+cancel:
+ err = smbconf_transaction_cancel(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf("error cancelling transaction: %s\n",
+ sbcErrorString(err));
+ }
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_delshare(struct net_context *c,
+ struct smbconf_ctx *conf_ctx, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ const char *sharename = NULL;
+ sbcErr err;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ net_conf_delshare_usage(c, argc, argv);
+ goto done;
+ }
+ sharename = talloc_strdup(mem_ctx, argv[0]);
+ if (sharename == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ err = smbconf_delete_share(conf_ctx, sharename);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error deleting share %s: %s\n"),
+ sharename, sbcErrorString(err));
+ goto done;
+ }
+
+ status = delete_share_security(sharename);
+ if (!NT_STATUS_IS_OK(status) &&
+ !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ d_fprintf(stderr, _("deleting share acl failed for %s: %s\n"),
+ sharename, nt_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_setparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+ char *service = NULL;
+ char *param = NULL;
+ const char *value_str = NULL;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 3 || c->display_usage) {
+ net_conf_setparm_usage(c, argc, argv);
+ goto done;
+ }
+ /*
+ * NULL service name means "dangling parameters" to libsmbconf.
+ * We use the empty string from the command line for this purpose.
+ */
+ if (strlen(argv[0]) != 0) {
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+ }
+ param = strlower_talloc(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+ value_str = argv[2];
+
+ if (!net_conf_param_valid(service,param, value_str)) {
+ goto done;
+ }
+
+ err = smbconf_transaction_start(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error starting transaction: %s\n"),
+ sbcErrorString(err));
+ goto done;
+ }
+
+ if (!smbconf_share_exists(conf_ctx, service)) {
+ err = smbconf_create_share(conf_ctx, service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error creating share '%s': %s\n"),
+ service, sbcErrorString(err));
+ goto cancel;
+ }
+ }
+
+ err = smbconf_set_parameter(conf_ctx, service, param, value_str);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error setting value '%s': %s\n"),
+ param, sbcErrorString(err));
+ goto cancel;
+ }
+
+ err = smbconf_transaction_commit(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error committing transaction: %s\n"),
+ sbcErrorString(err));
+ } else {
+ ret = 0;
+ }
+
+ goto done;
+
+cancel:
+ err = smbconf_transaction_cancel(conf_ctx);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error cancelling transaction: %s\n"),
+ sbcErrorString(err));
+ }
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_getparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+ char *service = NULL;
+ char *param = NULL;
+ char *valstr = NULL;
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_stackframe();
+
+ if (argc != 2 || c->display_usage) {
+ net_conf_getparm_usage(c, argc, argv);
+ goto done;
+ }
+ /*
+ * NULL service name means "dangling parameters" to libsmbconf.
+ * We use the empty string from the command line for this purpose.
+ */
+ if (strlen(argv[0]) != 0) {
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+ }
+ param = strlower_talloc(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ err = smbconf_get_parameter(conf_ctx, mem_ctx, service, param, &valstr);
+ if (SBC_ERROR_EQUAL(err, SBC_ERR_NO_SUCH_SERVICE)) {
+ d_fprintf(stderr,
+ _("Error: given service '%s' does not exist.\n"),
+ service);
+ goto done;
+ } else if (SBC_ERROR_EQUAL(err, SBC_ERR_INVALID_PARAM)) {
+ d_fprintf(stderr,
+ _("Error: given parameter '%s' is not set.\n"),
+ param);
+ goto done;
+ } else if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error getting value '%s': %s.\n"),
+ param, sbcErrorString(err));
+ goto done;
+ }
+
+ d_printf("%s\n", valstr);
+
+ ret = 0;
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_delparm(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ sbcErr err;
+ char *service = NULL;
+ char *param = NULL;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 2 || c->display_usage) {
+ net_conf_delparm_usage(c, argc, argv);
+ goto done;
+ }
+ /*
+ * NULL service name means "dangling parameters" to libsmbconf.
+ * We use the empty string from the command line for this purpose.
+ */
+ if (strlen(argv[0]) != 0) {
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+ }
+ param = strlower_talloc(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ err = smbconf_delete_parameter(conf_ctx, service, param);
+ if (SBC_ERROR_EQUAL(err, SBC_ERR_NO_SUCH_SERVICE)) {
+ d_fprintf(stderr,
+ _("Error: given service '%s' does not exist.\n"),
+ service);
+ goto done;
+ } else if (SBC_ERROR_EQUAL(err, SBC_ERR_INVALID_PARAM)) {
+ d_fprintf(stderr,
+ _("Error: given parameter '%s' is not set.\n"),
+ param);
+ goto done;
+ } else if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("Error deleting value '%s': %s.\n"),
+ param, sbcErrorString(err));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_getincludes(struct net_context *c,
+ struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ sbcErr err;
+ uint32_t num_includes;
+ uint32_t count;
+ char *service;
+ char **includes = NULL;
+ int ret = -1;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ net_conf_getincludes_usage(c, argc, argv);
+ goto done;
+ }
+
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ err = smbconf_get_includes(conf_ctx, mem_ctx, service,
+ &num_includes, &includes);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error getting includes: %s\n"), sbcErrorString(err));
+ goto done;
+ }
+
+ for (count = 0; count < num_includes; count++) {
+ d_printf("include = %s\n", includes[count]);
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_setincludes(struct net_context *c,
+ struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ sbcErr err;
+ char *service;
+ uint32_t num_includes;
+ const char **includes;
+ int ret = -1;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc < 1 || c->display_usage) {
+ net_conf_setincludes_usage(c, argc, argv);
+ goto done;
+ }
+
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ num_includes = argc - 1;
+ if (num_includes == 0) {
+ includes = NULL;
+ } else {
+ includes = argv + 1;
+ }
+
+ err = smbconf_set_includes(conf_ctx, service, num_includes, includes);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error setting includes: %s\n"), sbcErrorString(err));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_conf_delincludes(struct net_context *c,
+ struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ sbcErr err;
+ char *service;
+ int ret = -1;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ net_conf_delincludes_usage(c, argc, argv);
+ goto done;
+ }
+
+ service = talloc_strdup(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto done;
+ }
+
+ err = smbconf_delete_includes(conf_ctx, service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_printf(_("error deleting includes: %s\n"), sbcErrorString(err));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+
+/**********************************************************************
+ *
+ * Wrapper and net_conf_run_function mechanism.
+ *
+ **********************************************************************/
+
+/**
+ * Wrapper function to call the main conf functions.
+ * The wrapper calls handles opening and closing of the
+ * configuration.
+ */
+static int net_conf_wrap_function(struct net_context *c,
+ int (*fn)(struct net_context *,
+ struct smbconf_ctx *,
+ int, const char **),
+ int argc, const char **argv)
+{
+ sbcErr err;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct smbconf_ctx *conf_ctx;
+ int ret = -1;
+
+ err = smbconf_init(mem_ctx, &conf_ctx, "registry:");
+ if (!SBC_ERROR_IS_OK(err)) {
+ talloc_free(mem_ctx);
+ return -1;
+ }
+
+ ret = fn(c, conf_ctx, argc, argv);
+
+ smbconf_shutdown(conf_ctx);
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/*
+ * We need a functable struct of our own, because the
+ * functions are called through a wrapper that handles
+ * the opening and closing of the configuration, and so on.
+ */
+struct conf_functable {
+ const char *funcname;
+ int (*fn)(struct net_context *c, struct smbconf_ctx *ctx, int argc,
+ const char **argv);
+ int valid_transports;
+ const char *description;
+ const char *usage;
+};
+
+/**
+ * This imitates net_run_function but calls the main functions
+ * through the wrapper net_conf_wrap_function().
+ */
+static int net_conf_run_function(struct net_context *c, int argc,
+ const char **argv, const char *whoami,
+ struct conf_functable *table)
+{
+ int i;
+
+ if (argc != 0) {
+ for (i=0; table[i].funcname; i++) {
+ if (strcasecmp_m(argv[0], table[i].funcname) == 0)
+ return net_conf_wrap_function(c, table[i].fn,
+ argc-1,
+ argv+1);
+ }
+ }
+
+ d_printf(_("Usage:\n"));
+ for (i=0; table[i].funcname; i++) {
+ if (c->display_usage == false)
+ d_printf("%s %-15s %s\n", whoami, table[i].funcname,
+ table[i].description);
+ else
+ d_printf("%s\n", table[i].usage);
+ }
+
+ return c->display_usage?0:-1;
+}
+
+/*
+ * Entry-point for all the CONF functions.
+ */
+
+int net_conf(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+ struct conf_functable func_table[] = {
+ {
+ "list",
+ net_conf_list,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump the complete configuration in smb.conf like "
+ "format."),
+ N_("net conf list\n"
+ " Dump the complete configuration in smb.conf "
+ "like format.")
+
+ },
+ {
+ "import",
+ net_conf_import,
+ NET_TRANSPORT_LOCAL,
+ N_("Import configuration from file in smb.conf "
+ "format."),
+ N_("net conf import\n"
+ " Import configuration from file in smb.conf "
+ "format.")
+ },
+ {
+ "listshares",
+ net_conf_listshares,
+ NET_TRANSPORT_LOCAL,
+ N_("List the share names."),
+ N_("net conf listshares\n"
+ " List the share names.")
+ },
+ {
+ "drop",
+ net_conf_drop,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete the complete configuration."),
+ N_("net conf drop\n"
+ " Delete the complete configuration.")
+ },
+ {
+ "showshare",
+ net_conf_showshare,
+ NET_TRANSPORT_LOCAL,
+ N_("Show the definition of a share."),
+ N_("net conf showshare\n"
+ " Show the definition of a share.")
+ },
+ {
+ "addshare",
+ net_conf_addshare,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new share."),
+ N_("net conf addshare\n"
+ " Create a new share.")
+ },
+ {
+ "delshare",
+ net_conf_delshare,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a share."),
+ N_("net conf delshare\n"
+ " Delete a share.")
+ },
+ {
+ "setparm",
+ net_conf_setparm,
+ NET_TRANSPORT_LOCAL,
+ N_("Store a parameter."),
+ N_("net conf setparm\n"
+ " Store a parameter.")
+ },
+ {
+ "getparm",
+ net_conf_getparm,
+ NET_TRANSPORT_LOCAL,
+ N_("Retrieve the value of a parameter."),
+ N_("net conf getparm\n"
+ " Retrieve the value of a parameter.")
+ },
+ {
+ "delparm",
+ net_conf_delparm,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a parameter."),
+ N_("net conf delparm\n"
+ " Delete a parameter.")
+ },
+ {
+ "getincludes",
+ net_conf_getincludes,
+ NET_TRANSPORT_LOCAL,
+ N_("Show the includes of a share definition."),
+ N_("net conf getincludes\n"
+ " Show the includes of a share definition.")
+ },
+ {
+ "setincludes",
+ net_conf_setincludes,
+ NET_TRANSPORT_LOCAL,
+ N_("Set includes for a share."),
+ N_("net conf setincludes\n"
+ " Set includes for a share.")
+ },
+ {
+ "delincludes",
+ net_conf_delincludes,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete includes from a share definition."),
+ N_("net conf delincludes\n"
+ " Delete includes from a share definition.")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ ret = net_conf_run_function(c, argc, argv, "net conf", func_table);
+
+ return ret;
+}
+
diff --git a/source3/utils/net_conf_util.c b/source3/utils/net_conf_util.c
new file mode 100644
index 0000000..ec6a479
--- /dev/null
+++ b/source3/utils/net_conf_util.c
@@ -0,0 +1,69 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Configuration interface
+ *
+ * Copyright (C) Michael Adam 2013
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Utility functions for net conf and net rpc conf.
+ */
+
+#include "includes.h"
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_reg.h"
+#include "lib/param/loadparm.h"
+#include "net_conf_util.h"
+
+bool net_conf_param_valid(const char *service,
+ const char *param,
+ const char *valstr)
+{
+ const char *canon_param, *canon_valstr;
+
+ if (!lp_parameter_is_valid(param)) {
+ d_fprintf(stderr, "Invalid parameter '%s' given.\n", param);
+ return false;
+ }
+
+ if (!smbconf_reg_parameter_is_valid(param)) {
+ d_fprintf(stderr, "Parameter '%s' not allowed in registry.\n",
+ param);
+ return false;
+ }
+
+ if (!strequal(service, GLOBAL_NAME) && lp_parameter_is_global(param)) {
+ d_fprintf(stderr, "Global parameter '%s' not allowed in "
+ "service definition ('%s').\n", param, service);
+ return false;
+ }
+
+ if (!lp_canonicalize_parameter_with_value(param, valstr,
+ &canon_param,
+ &canon_valstr))
+ {
+ /*
+ * We already know the parameter name is valid.
+ * So the value must be invalid.
+ */
+ d_fprintf(stderr, "invalid value '%s' given for "
+ "parameter '%s'\n", valstr, param);
+ return false;
+ }
+
+ return true;
+}
diff --git a/source3/utils/net_conf_util.h b/source3/utils/net_conf_util.h
new file mode 100644
index 0000000..798b399
--- /dev/null
+++ b/source3/utils/net_conf_util.h
@@ -0,0 +1,33 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Configuration interface
+ *
+ * Copyright (C) Michael Adam 2013
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NET_CONF_UTIL_H__
+#define __NET_CONF_UTIL_H__
+
+/*
+ * Utility functions for net conf and net rpc conf.
+ */
+
+bool net_conf_param_valid(const char *service,
+ const char *param,
+ const char *valstr);
+
+#endif /* __NET_CONF_UTIL_H__ */
diff --git a/source3/utils/net_dns.c b/source3/utils/net_dns.c
new file mode 100644
index 0000000..9850ba4
--- /dev/null
+++ b/source3/utils/net_dns.c
@@ -0,0 +1,224 @@
+/*
+ Samba Unix/Linux Dynamic DNS Update
+ net ads commands
+
+ Copyright (C) Krishna Ganugapati (krishnag@centeris.com) 2006
+ Copyright (C) Gerald Carter 2006
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "../lib/addns/dns.h"
+#include "utils/net_dns.h"
+
+#if defined(HAVE_KRB5)
+
+/*********************************************************************
+*********************************************************************/
+
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName,
+ const char *pszHostName,
+ const struct sockaddr_storage *sslist,
+ size_t num_addrs,
+ uint32_t flags,
+ uint32_t ttl,
+ bool remove_host)
+{
+ DNS_ERROR err;
+ struct dns_connection *conn;
+ TALLOC_CTX *mem_ctx;
+ OM_uint32 minor;
+ struct dns_update_request *req, *resp;
+
+ DEBUG(10,("DoDNSUpdate called with flags: 0x%08x\n", flags));
+
+ if (!(flags & DNS_UPDATE_SIGNED) &&
+ !(flags & DNS_UPDATE_UNSIGNED) &&
+ !(flags & DNS_UPDATE_PROBE)) {
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+
+ if ( !remove_host && ((num_addrs <= 0) || !sslist) ) {
+ return ERROR_DNS_INVALID_PARAMETER;
+ }
+
+ if (!(mem_ctx = talloc_init("DoDNSUpdate"))) {
+ return ERROR_DNS_NO_MEMORY;
+ }
+
+ err = dns_open_connection( pszServerName, DNS_TCP, mem_ctx, &conn );
+ if (!ERR_DNS_IS_OK(err)) {
+ goto error;
+ }
+
+ if (flags & DNS_UPDATE_PROBE) {
+
+ /*
+ * Probe if everything's fine
+ */
+
+ err = dns_create_probe(mem_ctx, pszDomainName, pszHostName,
+ num_addrs, sslist, &req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_update_transaction(mem_ctx, conn, req, &resp);
+
+ if (!ERR_DNS_IS_OK(err)) {
+ DEBUG(3,("DoDNSUpdate: failed to probe DNS\n"));
+ goto error;
+ }
+
+ if ((dns_response_code(resp->flags) == DNS_NO_ERROR) &&
+ (flags & DNS_UPDATE_PROBE_SUFFICIENT)) {
+ TALLOC_FREE(mem_ctx);
+ return ERROR_DNS_SUCCESS;
+ }
+ }
+
+ if (flags & DNS_UPDATE_UNSIGNED) {
+
+ /*
+ * First try without signing
+ */
+
+ err = dns_create_update_request(mem_ctx,
+ pszDomainName,
+ pszHostName,
+ sslist,
+ num_addrs,
+ ttl,
+ &req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_update_transaction(mem_ctx, conn, req, &resp);
+ if (!ERR_DNS_IS_OK(err)) {
+ DEBUG(3,("DoDNSUpdate: unsigned update failed\n"));
+ goto error;
+ }
+
+ if ((dns_response_code(resp->flags) == DNS_NO_ERROR) &&
+ (flags & DNS_UPDATE_UNSIGNED_SUFFICIENT)) {
+ TALLOC_FREE(mem_ctx);
+ return ERROR_DNS_SUCCESS;
+ }
+ }
+
+ /*
+ * Okay, we have to try with signing
+ */
+ if (flags & DNS_UPDATE_SIGNED) {
+ gss_ctx_id_t gss_context;
+ char *keyname;
+
+ err = dns_create_update_request(mem_ctx,
+ pszDomainName,
+ pszHostName,
+ sslist,
+ num_addrs,
+ ttl,
+ &req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ if (!(keyname = dns_generate_keyname( mem_ctx ))) {
+ err = ERROR_DNS_NO_MEMORY;
+ goto error;
+ }
+
+ err = dns_negotiate_sec_ctx( pszDomainName, pszServerName,
+ keyname, &gss_context, DNS_SRV_ANY );
+
+ /* retry using the Windows 2000 DNS hack */
+ if (!ERR_DNS_IS_OK(err)) {
+ err = dns_negotiate_sec_ctx( pszDomainName, pszServerName,
+ keyname, &gss_context,
+ DNS_SRV_WIN2000 );
+ }
+
+ if (!ERR_DNS_IS_OK(err))
+ goto error;
+
+ err = dns_sign_update(req, gss_context, keyname,
+ "gss.microsoft.com", time(NULL), 3600);
+
+ gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER);
+
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_update_transaction(mem_ctx, conn, req, &resp);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = (dns_response_code(resp->flags) == DNS_NO_ERROR) ?
+ ERROR_DNS_SUCCESS : ERROR_DNS_UPDATE_FAILED;
+
+ if (!ERR_DNS_IS_OK(err)) {
+ DEBUG(3,("DoDNSUpdate: signed update failed\n"));
+ }
+ }
+
+
+error:
+ TALLOC_FREE(mem_ctx);
+ return err;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+int get_my_ip_address( struct sockaddr_storage **pp_ss )
+
+{
+ int i, n;
+ struct sockaddr_storage *list = NULL;
+ int count = 0;
+
+ /* Honor the configured list of interfaces to register */
+
+ load_interfaces();
+ n = iface_count();
+
+ if (n <= 0) {
+ return -1;
+ }
+
+ if ( (list = SMB_MALLOC_ARRAY( struct sockaddr_storage, n )) == NULL ) {
+ return -1;
+ }
+
+ for ( i=0; i<n; i++ ) {
+ const struct sockaddr_storage *nic_sa_storage = NULL;
+
+ if ((nic_sa_storage = iface_n_sockaddr_storage(i)) == NULL)
+ continue;
+
+ /* Don't register loopback addresses */
+ if (is_loopback_addr((const struct sockaddr *)nic_sa_storage)) {
+ continue;
+ }
+
+ /* Don't register link-local addresses */
+ if (is_linklocal_addr(nic_sa_storage)) {
+ continue;
+ }
+
+ memcpy(&list[count++], nic_sa_storage, sizeof(struct sockaddr_storage));
+ }
+ *pp_ss = list;
+
+ return count;
+}
+
+#endif /* defined(HAVE_KRB5) */
diff --git a/source3/utils/net_dns.h b/source3/utils/net_dns.h
new file mode 100644
index 0000000..4569e1c
--- /dev/null
+++ b/source3/utils/net_dns.h
@@ -0,0 +1,44 @@
+/*
+ Samba Unix/Linux Dynamic DNS Update
+ net ads commands
+
+ Copyright (C) Krishna Ganugapati (krishnag@centeris.com) 2006
+ Copyright (C) Gerald Carter 2006
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/* flags for DoDNSUpdate */
+
+#define DNS_UPDATE_SIGNED 0x01
+#define DNS_UPDATE_SIGNED_SUFFICIENT 0x02
+#define DNS_UPDATE_UNSIGNED 0x04
+#define DNS_UPDATE_UNSIGNED_SUFFICIENT 0x08
+#define DNS_UPDATE_PROBE 0x10
+#define DNS_UPDATE_PROBE_SUFFICIENT 0x20
+
+#if defined(HAVE_KRB5)
+
+#include "../lib/addns/dns.h"
+
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName,
+ const char *pszHostName,
+ const struct sockaddr_storage *sslist,
+ size_t num_addrs,
+ uint32_t flags,
+ uint32_t ttl,
+ bool remove_host);
+
+#endif /* defined(HAVE_KRB5) */
diff --git a/source3/utils/net_dom.c b/source3/utils/net_dom.c
new file mode 100644
index 0000000..4342990
--- /dev/null
+++ b/source3/utils/net_dom.c
@@ -0,0 +1,385 @@
+/*
+ Samba Unix/Linux SMB client library
+ net dom commands for remote join/unjoin
+ Copyright (C) 2007,2009 Günther Deschner
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "../librpc/gen_ndr/ndr_initshutdown.h"
+#include "../librpc/gen_ndr/ndr_winreg.h"
+#include "lib/netapi/netapi.h"
+#include "lib/netapi/netapi_net.h"
+#include "libsmb/libsmb.h"
+
+int net_dom_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net dom join "
+ "<domain=DOMAIN> <ou=OU> <account=ACCOUNT> "
+ "<password=PASSWORD> <reboot>\n Join a remote machine\n"));
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net dom unjoin "
+ "<account=ACCOUNT> <password=PASSWORD> <reboot>\n"
+ " Unjoin a remote machine\n"));
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net dom renamecomputer "
+ "<newname=NEWNAME> "
+ "<account=ACCOUNT> <password=PASSWORD> <reboot>\n"
+ " Rename joined computer\n"));
+
+ return -1;
+}
+
+static int net_dom_unjoin(struct net_context *c, int argc, const char **argv)
+{
+ const char *server_name = NULL;
+ const char *account = NULL;
+ const char *password = NULL;
+ uint32_t unjoin_flags = NETSETUP_ACCT_DELETE |
+ NETSETUP_JOIN_DOMAIN |
+ NETSETUP_IGNORE_UNSUPPORTED_FLAGS;
+ struct cli_state *cli = NULL;
+ bool do_reboot = false;
+ NTSTATUS ntstatus;
+ NET_API_STATUS status;
+ int ret = -1;
+ int i;
+
+ if (argc < 1 || c->display_usage) {
+ return net_dom_usage(c, argc, argv);
+ }
+
+ if (c->opt_host) {
+ server_name = c->opt_host;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "account", strlen("account"))) {
+ account = get_string_param(argv[i]);
+ if (!account) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "password", strlen("password"))) {
+ password = get_string_param(argv[i]);
+ if (!password) {
+ return -1;
+ }
+ }
+ if (strequal(argv[i], "reboot")) {
+ do_reboot = true;
+ }
+ }
+
+ if (do_reboot) {
+ ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup,
+ server_name, NULL, 0,
+ &cli);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return -1;
+ }
+ }
+
+ status = NetUnjoinDomain(server_name, account, password, unjoin_flags);
+ if (status != 0) {
+ printf(_("Failed to unjoin domain: %s\n"),
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ goto done;
+ }
+
+ if (do_reboot) {
+ c->opt_comment = _("Shutting down due to a domain membership "
+ "change");
+ c->opt_reboot = true;
+ c->opt_timeout = 30;
+
+ ret = run_rpc_command(c, cli,
+ &ndr_table_initshutdown,
+ 0, rpc_init_shutdown_internals,
+ argc, argv);
+ if (ret == 0) {
+ goto done;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_winreg, 0,
+ rpc_reg_shutdown_internals,
+ argc, argv);
+ goto done;
+ }
+
+ ret = 0;
+
+ done:
+ if (cli) {
+ cli_shutdown(cli);
+ }
+
+ return ret;
+}
+
+static int net_dom_join(struct net_context *c, int argc, const char **argv)
+{
+ const char *server_name = NULL;
+ const char *domain_name = NULL;
+ const char *account_ou = NULL;
+ const char *Account = NULL;
+ const char *password = NULL;
+ uint32_t join_flags = NETSETUP_ACCT_CREATE |
+ NETSETUP_JOIN_DOMAIN;
+ struct cli_state *cli = NULL;
+ bool do_reboot = false;
+ NTSTATUS ntstatus;
+ NET_API_STATUS status;
+ int ret = -1;
+ int i;
+
+ if (argc < 1 || c->display_usage) {
+ return net_dom_usage(c, argc, argv);
+ }
+
+ net_warn_member_options();
+
+ if (c->opt_host) {
+ server_name = c->opt_host;
+ }
+
+ if (c->opt_force) {
+ join_flags |= NETSETUP_DOMAIN_JOIN_IF_JOINED;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "ou", strlen("ou"))) {
+ account_ou = get_string_param(argv[i]);
+ if (!account_ou) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "domain", strlen("domain"))) {
+ domain_name = get_string_param(argv[i]);
+ if (!domain_name) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "account", strlen("account"))) {
+ Account = get_string_param(argv[i]);
+ if (!Account) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "password", strlen("password"))) {
+ password = get_string_param(argv[i]);
+ if (!password) {
+ return -1;
+ }
+ }
+ if (strequal(argv[i], "reboot")) {
+ do_reboot = true;
+ }
+ }
+
+ if (do_reboot) {
+ ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup,
+ server_name, NULL, 0,
+ &cli);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return -1;
+ }
+ }
+
+ /* check if domain is a domain or a workgroup */
+
+ status = NetJoinDomain(server_name, domain_name, account_ou,
+ Account, password, join_flags);
+ if (status != 0) {
+ printf(_("Failed to join domain: %s\n"),
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ goto done;
+ }
+
+ if (do_reboot) {
+ c->opt_comment = _("Shutting down due to a domain membership "
+ "change");
+ c->opt_reboot = true;
+ c->opt_timeout = 30;
+
+ ret = run_rpc_command(c, cli, &ndr_table_initshutdown, 0,
+ rpc_init_shutdown_internals,
+ argc, argv);
+ if (ret == 0) {
+ goto done;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_winreg, 0,
+ rpc_reg_shutdown_internals,
+ argc, argv);
+ goto done;
+ }
+
+ ret = 0;
+
+ done:
+ if (cli) {
+ cli_shutdown(cli);
+ }
+
+ return ret;
+}
+
+static int net_dom_renamecomputer(struct net_context *c, int argc, const char **argv)
+{
+ const char *server_name = NULL;
+ const char *account = NULL;
+ const char *password = NULL;
+ const char *newname = NULL;
+ uint32_t rename_options = NETSETUP_ACCT_CREATE;
+ struct cli_state *cli = NULL;
+ bool do_reboot = false;
+ NTSTATUS ntstatus;
+ NET_API_STATUS status;
+ int ret = -1;
+ int i;
+
+ if (argc < 1 || c->display_usage) {
+ return net_dom_usage(c, argc, argv);
+ }
+
+ if (c->opt_host) {
+ server_name = c->opt_host;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strnequal(argv[i], "account", strlen("account"))) {
+ account = get_string_param(argv[i]);
+ if (!account) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "password", strlen("password"))) {
+ password = get_string_param(argv[i]);
+ if (!password) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "newname", strlen("newname"))) {
+ newname = get_string_param(argv[i]);
+ if (!newname) {
+ return -1;
+ }
+ }
+ if (strequal(argv[i], "reboot")) {
+ do_reboot = true;
+ }
+ }
+
+ if (do_reboot) {
+ ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup,
+ server_name, NULL, 0,
+ &cli);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ return -1;
+ }
+ }
+
+ status = NetRenameMachineInDomain(server_name, newname,
+ account, password, rename_options);
+ if (status != 0) {
+ printf(_("Failed to rename machine: "));
+ if (status == W_ERROR_V(WERR_NERR_SETUPNOTJOINED)) {
+ printf(_("Computer is not joined to a Domain\n"));
+ goto done;
+ }
+ printf("%s\n",
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ goto done;
+ }
+
+ if (do_reboot) {
+ c->opt_comment = _("Shutting down due to a computer rename");
+ c->opt_reboot = true;
+ c->opt_timeout = 30;
+
+ ret = run_rpc_command(c, cli,
+ &ndr_table_initshutdown,
+ 0, rpc_init_shutdown_internals,
+ argc, argv);
+ if (ret == 0) {
+ goto done;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_winreg, 0,
+ rpc_reg_shutdown_internals,
+ argc, argv);
+ goto done;
+ }
+
+ ret = 0;
+
+ done:
+ if (cli) {
+ cli_shutdown(cli);
+ }
+
+ return ret;
+}
+
+int net_dom(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "join",
+ net_dom_join,
+ NET_TRANSPORT_LOCAL,
+ N_("Join a remote machine"),
+ N_("net dom join <domain=DOMAIN> <ou=OU> "
+ "<account=ACCOUNT> <password=PASSWORD> <reboot>\n"
+ " Join a remote machine")
+ },
+ {
+ "unjoin",
+ net_dom_unjoin,
+ NET_TRANSPORT_LOCAL,
+ N_("Unjoin a remote machine"),
+ N_("net dom unjoin <account=ACCOUNT> "
+ "<password=PASSWORD> <reboot>\n"
+ " Unjoin a remote machine")
+ },
+ {
+ "renamecomputer",
+ net_dom_renamecomputer,
+ NET_TRANSPORT_LOCAL,
+ N_("Rename a computer that is joined to a domain"),
+ N_("net dom renamecomputer <newname=NEWNAME> "
+ "<account=ACCOUNT> <password=PASSWORD> "
+ "<reboot>\n"
+ " Rename joined computer")
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ return net_run_function(c, argc, argv, "net dom", func);
+}
diff --git a/source3/utils/net_eventlog.c b/source3/utils/net_eventlog.c
new file mode 100644
index 0000000..24dbab9
--- /dev/null
+++ b/source3/utils/net_eventlog.c
@@ -0,0 +1,275 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Local win32 eventlog interface
+ *
+ * Copyright (C) Guenther Deschner 2009
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "lib/eventlog/eventlog.h"
+
+/**
+ * Dump an *evt win32 eventlog file
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int net_eventlog_dump(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct EVENTLOG_EVT_FILE evt;
+ char *s;
+
+ if (argc < 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\nnet eventlog dump <file.evt>\n",
+ _("Usage:"));
+ goto done;
+ }
+
+ blob.data = (uint8_t *)file_load(argv[0], &blob.length, 0, ctx);
+ if (!blob.data) {
+ d_fprintf(stderr, _("failed to load evt file: %s\n"), argv[0]);
+ goto done;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt,
+ (ndr_pull_flags_fn_t)ndr_pull_EVENTLOG_EVT_FILE);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("evt pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ goto done;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(ctx, EVENTLOG_EVT_FILE, &evt);
+ if (s) {
+ printf("%s\n", s);
+ }
+
+ ret = 0;
+ done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/**
+ * Import an *evt win32 eventlog file to internal tdb representation
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int net_eventlog_import(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ uint32_t num_records = 0;
+ uint32_t i;
+ ELOG_TDB *etdb = NULL;
+
+ struct EVENTLOGHEADER evt_header;
+ struct EVENTLOG_EVT_FILE evt;
+
+ if (argc < 2 || c->display_usage) {
+ d_fprintf(stderr,
+ "%s\nnet eventlog import <file> <eventlog>\n",
+ _("Usage:"));
+ goto done;
+ }
+
+ blob.data = (uint8_t *)file_load(argv[0], &blob.length, 0, ctx);
+ if (!blob.data) {
+ d_fprintf(stderr, _("failed to load evt file: %s\n"), argv[0]);
+ goto done;
+ }
+
+ /* dump_data(0, blob.data, blob.length); */
+ ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt_header,
+ (ndr_pull_flags_fn_t)ndr_pull_EVENTLOGHEADER);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("evt header pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ goto done;
+ }
+
+ if (evt_header.Flags & ELF_LOGFILE_HEADER_WRAP) {
+ d_fprintf(stderr, _("input file is wrapped, cannot proceed\n"));
+ goto done;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt,
+ (ndr_pull_flags_fn_t)ndr_pull_EVENTLOG_EVT_FILE);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("evt pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ goto done;
+ }
+
+ /* NDR_PRINT_DEBUG(EVENTLOG_EVT_FILE, &evt); */
+
+ etdb = elog_open_tdb(argv[1], false, false);
+ if (!etdb) {
+ d_fprintf(stderr, _("can't open the eventlog TDB (%s)\n"),
+ argv[1]);
+ goto done;
+ }
+
+ num_records = evt.hdr.CurrentRecordNumber - evt.hdr.OldestRecordNumber;
+
+ for (i=0; i<num_records; i++) {
+ uint32_t record_number;
+ struct eventlog_Record_tdb e;
+
+ status = evlog_evt_entry_to_tdb_entry(ctx, &evt.records[i], &e);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = evlog_push_record_tdb(ctx, ELOG_TDB_CTX(etdb),
+ &e, &record_number);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("can't write to the eventlog: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ }
+
+ printf(_("wrote %d entries to tdb\n"), i);
+
+ ret = 0;
+ done:
+
+ elog_close_tdb(etdb, false);
+
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/**
+ * Export internal eventlog tdb representation to an *evt win32 eventlog file
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int net_eventlog_export(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ NTSTATUS status;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ DATA_BLOB blob;
+ uint32_t num_records = 0;
+ ELOG_TDB *etdb = NULL;
+
+ if (argc < 2 || c->display_usage) {
+ d_fprintf(stderr,
+ "%s\nnet eventlog export <file> <eventlog>\n",
+ _("Usage:"));
+ goto done;
+ }
+
+ etdb = elog_open_tdb(argv[1], false, true);
+ if (!etdb) {
+ d_fprintf(stderr, _("can't open the eventlog TDB (%s)\n"),
+ argv[1]);
+ goto done;
+ }
+
+ status = evlog_convert_tdb_to_evt(ctx, etdb, &blob, &num_records);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (!file_save(argv[0], blob.data, blob.length)) {
+ d_fprintf(stderr, _("failed to save evt file: %s\n"), argv[0]);
+ goto done;
+ }
+
+ ret = 0;
+ done:
+
+ elog_close_tdb(etdb, false);
+
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/**
+ * 'net rpc eventlog' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_eventlog(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+
+ struct functable func[] = {
+ {
+ "dump",
+ net_eventlog_dump,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump eventlog"),
+ N_("net eventlog dump\n"
+ " Dump win32 *.evt eventlog file")
+ },
+ {
+ "import",
+ net_eventlog_import,
+ NET_TRANSPORT_LOCAL,
+ N_("Import eventlog"),
+ N_("net eventlog import\n"
+ " Import win32 *.evt eventlog file")
+ },
+ {
+ "export",
+ net_eventlog_export,
+ NET_TRANSPORT_LOCAL,
+ N_("Export eventlog"),
+ N_("net eventlog export\n"
+ " Export win32 *.evt eventlog file")
+ },
+
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ ret = net_run_function(c, argc, argv, "net eventlog", func);
+
+ return ret;
+}
diff --git a/source3/utils/net_file.c b/source3/utils/net_file.c
new file mode 100644
index 0000000..71a7e05
--- /dev/null
+++ b/source3/utils/net_file.c
@@ -0,0 +1,57 @@
+/*
+ Samba Unix/Linux SMB client library
+ net file commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2002 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_file_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net [<method>] file [misc. options] [targets]\n"
+ "\tlists all open files on file server\n"));
+ d_printf(_("net [<method>] file USER <username> "
+ "[misc. options] [targets]"
+ "\n\tlists all files opened by username on file server\n"));
+ d_printf(_("net [<method>] file CLOSE <id> [misc. options] [targets]\n"
+ "\tcloses specified file on target server\n"));
+ d_printf(_("net [rap] file INFO <id> [misc. options] [targets]\n"
+ "\tdisplays information about the specified open file\n"));
+
+ net_common_methods_usage(c, argc, argv);
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_file(struct net_context *c, int argc, const char **argv)
+{
+ if (argc < 1)
+ return net_file_usage(c, argc, argv);
+
+ if (strcasecmp_m(argv[0], "HELP") == 0) {
+ net_file_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (net_rpc_check(c, 0))
+ return net_rpc_file(c, argc, argv);
+ return net_rap_file(c, argc, argv);
+}
+
+
diff --git a/source3/utils/net_g_lock.c b/source3/utils/net_g_lock.c
new file mode 100644
index 0000000..a100028
--- /dev/null
+++ b/source3/utils/net_g_lock.c
@@ -0,0 +1,267 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Interface to the g_lock facility
+ * Copyright (C) Volker Lendecke 2009
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "net.h"
+#include "lib/util/server_id.h"
+#include "g_lock.h"
+#include "messages.h"
+#include "lib/util/util_tdb.h"
+
+static bool net_g_lock_init(TALLOC_CTX *mem_ctx,
+ struct tevent_context **pev,
+ struct messaging_context **pmsg,
+ struct g_lock_ctx **pg_ctx)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *g_ctx = NULL;
+
+ ev = samba_tevent_context_init(mem_ctx);
+ if (ev == NULL) {
+ d_fprintf(stderr, "ERROR: could not init event context\n");
+ goto fail;
+ }
+ msg = messaging_init(mem_ctx, ev);
+ if (msg == NULL) {
+ d_fprintf(stderr, "ERROR: could not init messaging context\n");
+ goto fail;
+ }
+ g_ctx = g_lock_ctx_init(mem_ctx, msg);
+ if (g_ctx == NULL) {
+ d_fprintf(stderr, "ERROR: could not init g_lock context\n");
+ goto fail;
+ }
+
+ *pev = ev;
+ *pmsg = msg;
+ *pg_ctx = g_ctx;
+ return true;
+fail:
+ TALLOC_FREE(g_ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return false;
+}
+
+static int net_g_lock_do(struct net_context *c, int argc, const char **argv)
+{
+ struct g_lock_ctx *ctx = NULL;
+ TDB_DATA key = {0};
+ const char *cmd = NULL;
+ int timeout;
+ NTSTATUS status;
+ int result = -1;
+
+ if (argc != 3) {
+ d_printf("Usage: net g_lock do <lockname> <timeout> "
+ "<command>\n");
+ return -1;
+ }
+ key = string_term_tdb_data(argv[0]);
+ timeout = atoi(argv[1]);
+ cmd = argv[2];
+
+ ctx = g_lock_ctx_init(c, c->msg_ctx);
+ if (ctx == NULL) {
+ d_fprintf(stderr, _("g_lock_ctx_init failed\n"));
+ return -1;
+ }
+ status = g_lock_lock(
+ ctx,
+ key,
+ G_LOCK_WRITE,
+ timeval_set(timeout / 1000, timeout % 1000),
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("g_lock_lock failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ result = system(cmd);
+
+ g_lock_unlock(ctx, key);
+
+ if (result == -1) {
+ d_fprintf(stderr, "ERROR: system() returned %s\n",
+ strerror(errno));
+ goto done;
+ }
+ d_fprintf(stderr, "command returned %d\n", result);
+
+done:
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+static void net_g_lock_dump_fn(struct server_id exclusive,
+ size_t num_shared,
+ const struct server_id *shared,
+ const uint8_t *data,
+ size_t datalen,
+ void *private_data)
+{
+ struct server_id_buf idbuf;
+
+ if (exclusive.pid != 0) {
+ d_printf("%s: WRITE\n",
+ server_id_str_buf(exclusive, &idbuf));
+ } else {
+ size_t i;
+ for (i=0; i<num_shared; i++) {
+ d_printf("%s: READ\n",
+ server_id_str_buf(shared[i], &idbuf));
+ }
+ }
+ dump_data_file(data, datalen, true, stdout);
+}
+
+static int net_g_lock_dump(struct net_context *c, int argc, const char **argv)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *g_ctx = NULL;
+ int ret = -1;
+
+ if (argc != 1) {
+ d_printf("Usage: net g_lock dump <lockname>\n");
+ return -1;
+ }
+
+ if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) {
+ goto done;
+ }
+
+ (void)g_lock_dump(g_ctx, string_term_tdb_data(argv[0]),
+ net_g_lock_dump_fn, NULL);
+
+ ret = 0;
+done:
+ TALLOC_FREE(g_ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret;
+}
+
+static int net_g_lock_dumpall_fn(TDB_DATA key, void *private_data)
+{
+ struct g_lock_ctx *g_ctx = talloc_get_type_abort(
+ private_data, struct g_lock_ctx);
+
+ dump_data_file(key.dptr, key.dsize, true, stdout);
+ g_lock_dump(g_ctx, key, net_g_lock_dump_fn, NULL);
+ printf("\n");
+
+ return 0;
+}
+
+static int net_g_lock_dumpall(
+ struct net_context *c, int argc, const char **argv)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *g_ctx = NULL;
+ int ret = -1;
+
+ if (argc != 0) {
+ d_printf("Usage: net g_lock locks\n");
+ return -1;
+ }
+
+ if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) {
+ goto done;
+ }
+
+ ret = g_lock_locks(g_ctx, net_g_lock_dumpall_fn, g_ctx);
+done:
+ TALLOC_FREE(g_ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret < 0 ? -1 : ret;
+}
+
+static int net_g_lock_locks_fn(TDB_DATA key, void *private_data)
+{
+ dump_data_file(key.dptr, key.dsize, true, stdout);
+ return 0;
+}
+
+static int net_g_lock_locks(struct net_context *c, int argc, const char **argv)
+{
+ struct tevent_context *ev = NULL;
+ struct messaging_context *msg = NULL;
+ struct g_lock_ctx *g_ctx = NULL;
+ int ret = -1;
+
+ if (argc != 0) {
+ d_printf("Usage: net g_lock locks\n");
+ return -1;
+ }
+
+ if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) {
+ goto done;
+ }
+
+ ret = g_lock_locks(g_ctx, net_g_lock_locks_fn, NULL);
+done:
+ TALLOC_FREE(g_ctx);
+ TALLOC_FREE(msg);
+ TALLOC_FREE(ev);
+ return ret < 0 ? -1 : ret;
+}
+
+int net_g_lock(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "do",
+ net_g_lock_do,
+ NET_TRANSPORT_LOCAL,
+ N_("Execute a shell command under a lock"),
+ N_("net g_lock do <lock name> <timeout> <command>\n")
+ },
+ {
+ "locks",
+ net_g_lock_locks,
+ NET_TRANSPORT_LOCAL,
+ N_("List all locknames"),
+ N_("net g_lock locks\n")
+ },
+ {
+ "dump",
+ net_g_lock_dump,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump a g_lock locking table"),
+ N_("net g_lock dump <lock name>\n")
+ },
+ {
+ "dumpall",
+ net_g_lock_dumpall,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump all g_lock locking tables"),
+ N_("net g_lock dumpall\n")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net g_lock", func);
+}
diff --git a/source3/utils/net_group.c b/source3/utils/net_group.c
new file mode 100644
index 0000000..505856a
--- /dev/null
+++ b/source3/utils/net_group.c
@@ -0,0 +1,68 @@
+/*
+ Samba Unix/Linux SMB client library
+ net group commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2002 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_group_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net [<method>] group [misc. options] [targets]"
+ "\n\tList user groups\n\n"));
+ d_printf(_("net rpc group LIST [global|local|builtin]* [misc. options]"
+ "\n\tList specific user groups\n\n"));
+ d_printf(_("net [<method>] group DELETE <name> "
+ "[misc. options] [targets]"
+ "\n\tDelete specified group\n"));
+ d_printf(_("\nnet [<method>] group ADD <name> [-C comment] "
+ "[-c container] [misc. options] [targets]\n"
+ "\tCreate specified group\n"));
+ d_printf(_("\nnet rpc group MEMBERS <name>\n\tList Group Members\n\n"));
+ d_printf(_("\nnet rpc group ADDMEM <group> <member>\n"
+ "\tAdd Group Members\n\n"));
+ d_printf(_("\nnet rpc group DELMEM <group> <member>\n"
+ "\tDelete Group Members\n\n"));
+ net_common_methods_usage(c, argc, argv);
+ net_common_flags_usage(c, argc, argv);
+ d_printf(_("\t-C or --comment=<comment>\tdescriptive comment "
+ "(for add only)\n"));
+ d_printf(_("\t-c or --container=<container>\tLDAP container, "
+ "defaults to cn=Users (for add in ADS only)\n"));
+ d_printf(_("\t-L or --localgroup\t\tWhen adding groups, "
+ "create a local group (alias)\n"));
+ return -1;
+}
+
+int net_group(struct net_context *c, int argc, const char **argv)
+{
+ if (argc < 1)
+ return net_group_usage(c, argc, argv);
+
+ if (strcasecmp_m(argv[0], "HELP") == 0) {
+ net_group_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (net_ads_check(c) == 0)
+ return net_ads_group(c, argc, argv);
+
+ return net_rap_group(c, argc, argv);
+}
+
diff --git a/source3/utils/net_groupmap.c b/source3/utils/net_groupmap.c
new file mode 100644
index 0000000..4f36d45
--- /dev/null
+++ b/source3/utils/net_groupmap.c
@@ -0,0 +1,1023 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * RPC Pipe client / server routines
+ * Copyright (C) Andrew Tridgell 1992-2000,
+ * Copyright (C) Jean François Micouleau 1998-2001.
+ * Copyright (C) Gerald Carter 2003,
+ * Copyright (C) Volker Lendecke 2004
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "utils/net.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "lib/util/string_wrappers.h"
+
+/*********************************************************
+ Figure out if the input was an NT group or a SID string.
+ Return the SID.
+**********************************************************/
+static bool get_sid_from_input(struct dom_sid *sid, char *input)
+{
+ GROUP_MAP *map;
+
+ map = talloc_zero(NULL, GROUP_MAP);
+ if (!map) {
+ return false;
+ }
+
+ if (strncasecmp_m( input, "S-", 2)) {
+ /* Perhaps its the NT group name? */
+ if (!pdb_getgrnam(map, input)) {
+ printf(_("NT Group %s doesn't exist in mapping DB\n"),
+ input);
+ TALLOC_FREE(map);
+ return false;
+ } else {
+ *sid = map->sid;
+ }
+ } else {
+ if (!string_to_sid(sid, input)) {
+ printf(_("converting sid %s from a string failed!\n"),
+ input);
+ TALLOC_FREE(map);
+ return false;
+ }
+ }
+ TALLOC_FREE(map);
+ return true;
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_map_entry (const GROUP_MAP *map, bool long_list)
+{
+ struct dom_sid_buf buf;
+
+ if (!long_list)
+ d_printf("%s (%s) -> %s\n", map->nt_name,
+ dom_sid_str_buf(&map->sid, &buf),
+ gidtoname(map->gid));
+ else {
+ d_printf("%s\n", map->nt_name);
+ d_printf(_("\tSID : %s\n"),
+ dom_sid_str_buf(&map->sid, &buf));
+ d_printf(_("\tUnix gid : %u\n"), (unsigned int)map->gid);
+ d_printf(_("\tUnix group: %s\n"), gidtoname(map->gid));
+ d_printf(_("\tGroup type: %s\n"),
+ sid_type_lookup(map->sid_name_use));
+ d_printf(_("\tComment : %s\n"), map->comment);
+ }
+
+}
+/*********************************************************
+ List the groups.
+**********************************************************/
+static int net_groupmap_list(struct net_context *c, int argc, const char **argv)
+{
+ size_t entries;
+ bool long_list = false;
+ size_t i;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+ const char list_usage_str[] = N_("net groupmap list [verbose] "
+ "[ntgroup=NT group] [sid=SID]\n"
+ " verbose\tPrint verbose list\n"
+ " ntgroup\tNT group to list\n"
+ " sid\tSID of group to list");
+
+ if (c->display_usage) {
+ d_printf("%s\n%s\n", _("Usage: "), list_usage_str);
+ return 0;
+ }
+
+ if (c->opt_verbose || c->opt_long_list_entries)
+ long_list = true;
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !strcasecmp_m(argv[i], "verbose")) {
+ long_list = true;
+ }
+ else if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_fprintf(stderr, _("must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_fprintf(stderr, _("must supply a SID\n"));
+ return -1;
+ }
+ }
+ else {
+ d_fprintf(stderr, _("Bad option: %s\n"), argv[i]);
+ d_printf("%s\n%s\n", _("Usage:"), list_usage_str);
+ return -1;
+ }
+ }
+
+ /* list a single group is given a name */
+ if ( ntgroup[0] || sid_string[0] ) {
+ struct dom_sid sid;
+ GROUP_MAP *map;
+
+ if ( sid_string[0] )
+ strlcpy(ntgroup, sid_string, sizeof(ntgroup));
+
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+
+ map = talloc_zero(NULL, GROUP_MAP);
+ if (!map) {
+ return -1;
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(map, sid)) {
+ d_fprintf(stderr,
+ _("Failure to find local group SID in the "
+ "database\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ print_map_entry(map, long_list );
+ TALLOC_FREE(map);
+ }
+ else {
+ GROUP_MAP **maps = NULL;
+ bool ok = false;
+ /* enumerate all group mappings */
+ ok = pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN,
+ &maps, &entries,
+ ENUM_ALL_MAPPED);
+ if (!ok) {
+ return -1;
+ }
+
+ for (i=0; i<entries; i++) {
+ print_map_entry(maps[i], long_list);
+ }
+
+ TALLOC_FREE(maps);
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a new group mapping entry
+**********************************************************/
+
+static int net_groupmap_add(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring string_sid = "";
+ fstring type = "";
+ fstring ntcomment = "";
+ enum lsa_SidType sid_type = SID_NAME_DOM_GRP;
+ uint32_t rid = 0;
+ gid_t gid;
+ int i;
+ GROUP_MAP *map;
+
+ const char *name_type;
+ const char add_usage_str[] = N_("net groupmap add "
+ "{rid=<int>|sid=<string>}"
+ " unixgroup=<string> "
+ "[type=<domain|local|builtin>] "
+ "[ntgroup=<string>] "
+ "[comment=<string>]");
+
+ name_type = "domain group";
+
+ if (c->display_usage) {
+ d_printf("%s\n%s\n", _("Usage:\n"), add_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !strncasecmp_m(argv[i], "rid", strlen("rid")) ) {
+ rid = get_int_param(argv[i]);
+ if ( rid < DOMAIN_RID_ADMINS ) {
+ d_fprintf(stderr,
+ _("RID must be greater than %d\n"),
+ (uint32_t)DOMAIN_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_fprintf(stderr,_( "must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_fprintf(stderr, _("must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( string_sid, get_string_param( argv[i] ) );
+ if ( !string_sid[0] ) {
+ d_fprintf(stderr, _("must supply a SID\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_fprintf(stderr,
+ _("must supply a comment string\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'b':
+ case 'B':
+ sid_type = SID_NAME_WKN_GRP;
+ name_type = "wellknown group";
+ break;
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ name_type = "domain group";
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ name_type = "alias (local) group";
+ break;
+ default:
+ d_fprintf(stderr,
+ _("unknown group type %s\n"),
+ type);
+ return -1;
+ }
+ }
+ else {
+ d_fprintf(stderr, _("Bad option: %s\n"), argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !unixgrp[0] ) {
+ d_printf("%s\n%s\n", _("Usage:\n"), add_usage_str);
+ return -1;
+ }
+
+ if ( (gid = nametogid(unixgrp)) == (gid_t)-1 ) {
+ d_fprintf(stderr, _("Can't lookup UNIX group %s\n"), unixgrp);
+ return -1;
+ }
+
+ map = talloc_zero(NULL, GROUP_MAP);
+ if (!map) {
+ return -1;
+ }
+ /* Default is domain group. */
+ map->sid_name_use = SID_NAME_DOM_GRP;
+ if (pdb_getgrgid(map, gid)) {
+ struct dom_sid_buf buf;
+ d_printf(_("Unix group %s already mapped to SID %s\n"),
+ unixgrp, dom_sid_str_buf(&map->sid, &buf));
+ TALLOC_FREE(map);
+ return -1;
+ }
+ TALLOC_FREE(map);
+
+ if ( (rid == 0) && (string_sid[0] == '\0') ) {
+ d_printf(_("No rid or sid specified, choosing a RID\n"));
+ if (pdb_capabilities() & PDB_CAP_STORE_RIDS) {
+ if (!pdb_new_rid(&rid)) {
+ d_printf(_("Could not get new RID\n"));
+ }
+ } else {
+ rid = algorithmic_pdb_gid_to_group_rid(gid);
+ }
+ d_printf(_("Got RID %d\n"), rid);
+ }
+
+ /* append the rid to our own domain/machine SID if we don't have a full SID */
+ if ( !string_sid[0] ) {
+ sid_compose(&sid, get_global_sam_sid(), rid);
+ sid_to_fstring(string_sid, &sid);
+ }
+
+ if (!ntcomment[0]) {
+ switch (sid_type) {
+ case SID_NAME_WKN_GRP:
+ fstrcpy(ntcomment, "Wellknown Unix group");
+ break;
+ case SID_NAME_DOM_GRP:
+ fstrcpy(ntcomment, "Domain Unix group");
+ break;
+ case SID_NAME_ALIAS:
+ fstrcpy(ntcomment, "Local Unix group");
+ break;
+ default:
+ fstrcpy(ntcomment, "Unix group");
+ break;
+ }
+ }
+
+ if (!ntgroup[0] )
+ strlcpy(ntgroup, unixgrp, sizeof(ntgroup));
+
+ if (!NT_STATUS_IS_OK(add_initial_entry(gid, string_sid, sid_type, ntgroup, ntcomment))) {
+ d_fprintf(stderr, _("adding entry for group %s failed!\n"), ntgroup);
+ return -1;
+ }
+
+ d_printf(_("Successfully added group %s to the mapping db as a %s\n"),
+ ntgroup, name_type);
+ return 0;
+}
+
+static int net_groupmap_modify(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ GROUP_MAP *map = NULL;
+ fstring ntcomment = "";
+ fstring type = "";
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring sid_string = "";
+ enum lsa_SidType sid_type = SID_NAME_UNKNOWN;
+ int i;
+ gid_t gid;
+ const char modify_usage_str[] = N_("net groupmap modify "
+ "{ntgroup=<string>|sid=<SID>} "
+ "[comment=<string>] "
+ "[unixgroup=<string>] "
+ "[type=<domain|local>]");
+
+ if (c->display_usage) {
+ d_printf("%s\n%s\n", _("Usage:\n"), modify_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_fprintf(stderr, _("must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_fprintf(stderr, _("must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "comment", strlen("comment")) ) {
+ fstrcpy( ntcomment, get_string_param( argv[i] ) );
+ if ( !ntcomment[0] ) {
+ d_fprintf(stderr,
+ _("must supply a comment string\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "unixgroup", strlen("unixgroup")) ) {
+ fstrcpy( unixgrp, get_string_param( argv[i] ) );
+ if ( !unixgrp[0] ) {
+ d_fprintf(stderr,
+ _("must supply a group name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "type", strlen("type")) ) {
+ fstrcpy( type, get_string_param( argv[i] ) );
+ switch ( type[0] ) {
+ case 'd':
+ case 'D':
+ sid_type = SID_NAME_DOM_GRP;
+ break;
+ case 'l':
+ case 'L':
+ sid_type = SID_NAME_ALIAS;
+ break;
+ }
+ }
+ else {
+ d_fprintf(stderr, _("Bad option: %s\n"), argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0] ) {
+ d_printf("%s\n%s\n", _("Usage:\n"), modify_usage_str);
+ return -1;
+ }
+
+ /* give preference to the SID; if both the ntgroup name and SID
+ are defined, use the SID and assume that the group name could be a
+ new name */
+
+ if ( sid_string[0] ) {
+ if (!get_sid_from_input(&sid, sid_string)) {
+ return -1;
+ }
+ }
+ else {
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+ }
+
+ map = talloc_zero(NULL, GROUP_MAP);
+ if (!map) {
+ return -1;
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(map, sid)) {
+ d_fprintf(stderr,
+ _("Failed to find local group SID in the database\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ /*
+ * Allow changing of group type only between domain and local
+ * We disallow changing Builtin groups !!! (SID problem)
+ */
+ if (sid_type == SID_NAME_UNKNOWN) {
+ d_fprintf(stderr, _("Can't map to an unknown group type.\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ if (map->sid_name_use == SID_NAME_WKN_GRP) {
+ d_fprintf(stderr,
+ _("You can only change between domain and local "
+ "groups.\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ map->sid_name_use = sid_type;
+
+ /* Change comment if new one */
+ if (ntcomment[0]) {
+ map->comment = talloc_strdup(map, ntcomment);
+ if (!map->comment) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ return -1;
+ }
+ }
+
+ if (ntgroup[0]) {
+ map->nt_name = talloc_strdup(map, ntgroup);
+ if (!map->nt_name) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ return -1;
+ }
+ }
+
+ if ( unixgrp[0] ) {
+ gid = nametogid( unixgrp );
+ if ( gid == -1 ) {
+ d_fprintf(stderr, _("Unable to lookup UNIX group %s. "
+ "Make sure the group exists.\n"),
+ unixgrp);
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ map->gid = gid;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_update_group_mapping_entry(map))) {
+ d_fprintf(stderr, _("Could not update group database\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ d_printf(_("Updated mapping entry for %s\n"), map->nt_name);
+
+ TALLOC_FREE(map);
+ return 0;
+}
+
+static int net_groupmap_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+ int i;
+ const char delete_usage_str[] = N_("net groupmap delete "
+ "{ntgroup=<string>|sid=<SID>}");
+
+ if (c->display_usage) {
+ d_printf("%s\n%s\n", _("Usage:\n"), delete_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) {
+ fstrcpy( ntgroup, get_string_param( argv[i] ) );
+ if ( !ntgroup[0] ) {
+ d_fprintf(stderr, _("must supply a name\n"));
+ return -1;
+ }
+ }
+ else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) {
+ fstrcpy( sid_string, get_string_param( argv[i] ) );
+ if ( !sid_string[0] ) {
+ d_fprintf(stderr, _("must supply a SID\n"));
+ return -1;
+ }
+ }
+ else {
+ d_fprintf(stderr, _("Bad option: %s\n"), argv[i]);
+ return -1;
+ }
+ }
+
+ if ( !ntgroup[0] && !sid_string[0]) {
+ d_printf("%s\n%s\n", _("Usage:\n"), delete_usage_str);
+ return -1;
+ }
+
+ /* give preference to the SID if we have that */
+
+ if ( sid_string[0] )
+ strlcpy(ntgroup, sid_string, sizeof(ntgroup));
+
+ if ( !get_sid_from_input(&sid, ntgroup) ) {
+ d_fprintf(stderr, _("Unable to resolve group %s to a SID\n"),
+ ntgroup);
+ return -1;
+ }
+
+ if ( !NT_STATUS_IS_OK(pdb_delete_group_mapping_entry(sid)) ) {
+ d_fprintf(stderr,
+ _("Failed to remove group %s from the mapping db!\n"),
+ ntgroup);
+ return -1;
+ }
+
+ d_printf(_("Successfully removed %s from the mapping db\n"), ntgroup);
+
+ return 0;
+}
+
+static int net_groupmap_set(struct net_context *c, int argc, const char **argv)
+{
+ const char *ntgroup = NULL;
+ struct group *grp = NULL;
+ GROUP_MAP *map;
+ bool have_map = false;
+
+ if ((argc < 1) || (argc > 2) || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net groupmap set \"NT Group\" "
+ "[\"unix group\"] [-C \"comment\"] [-L] [-D]\n"));
+ return -1;
+ }
+
+ if ( c->opt_localgroup && c->opt_domaingroup ) {
+ d_printf(_("Can only specify -L or -D, not both\n"));
+ return -1;
+ }
+
+ ntgroup = argv[0];
+
+ if (argc == 2) {
+ grp = getgrnam(argv[1]);
+
+ if (grp == NULL) {
+ d_fprintf(stderr, _("Could not find unix group %s\n"),
+ argv[1]);
+ return -1;
+ }
+ }
+
+ map = talloc_zero(NULL, GROUP_MAP);
+ if (!map) {
+ d_printf(_("Out of memory!\n"));
+ return -1;
+ }
+
+ have_map = pdb_getgrnam(map, ntgroup);
+
+ if (!have_map) {
+ struct dom_sid sid;
+ have_map = ( (strncmp(ntgroup, "S-", 2) == 0) &&
+ string_to_sid(&sid, ntgroup) &&
+ pdb_getgrsid(map, sid) );
+ }
+
+ if (!have_map) {
+
+ /* Ok, add it */
+
+ if (grp == NULL) {
+ d_fprintf(stderr,
+ _("Could not find group mapping for %s\n"),
+ ntgroup);
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ map->gid = grp->gr_gid;
+
+ if (c->opt_rid == 0) {
+ if ( pdb_capabilities() & PDB_CAP_STORE_RIDS ) {
+ if ( !pdb_new_rid((uint32_t *)&c->opt_rid) ) {
+ d_fprintf( stderr,
+ _("Could not allocate new RID\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+ } else {
+ c->opt_rid = algorithmic_pdb_gid_to_group_rid(map->gid);
+ }
+ }
+
+ sid_compose(&map->sid, get_global_sam_sid(), c->opt_rid);
+
+ map->sid_name_use = SID_NAME_DOM_GRP;
+ map->nt_name = talloc_strdup(map, ntgroup);
+ map->comment = talloc_strdup(map, "");
+ if (!map->nt_name || !map->comment) {
+ d_printf(_("Out of memory!\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_add_group_mapping_entry(map))) {
+ d_fprintf(stderr,
+ _("Could not add mapping entry for %s\n"),
+ ntgroup);
+ TALLOC_FREE(map);
+ return -1;
+ }
+ }
+
+ /* Now we have a mapping entry, update that stuff */
+
+ if ( c->opt_localgroup || c->opt_domaingroup ) {
+ if (map->sid_name_use == SID_NAME_WKN_GRP) {
+ d_fprintf(stderr,
+ _("Can't change type of the BUILTIN "
+ "group %s\n"),
+ map->nt_name);
+ TALLOC_FREE(map);
+ return -1;
+ }
+ }
+
+ if (c->opt_localgroup)
+ map->sid_name_use = SID_NAME_ALIAS;
+
+ if (c->opt_domaingroup)
+ map->sid_name_use = SID_NAME_DOM_GRP;
+
+ /* The case (opt_domaingroup && opt_localgroup) was tested for above */
+
+ if ((c->opt_comment != NULL) && (strlen(c->opt_comment) > 0)) {
+ map->comment = talloc_strdup(map, c->opt_comment);
+ if (!map->comment) {
+ d_printf(_("Out of memory!\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+ }
+
+ if ((c->opt_newntname != NULL) && (strlen(c->opt_newntname) > 0)) {
+ map->nt_name = talloc_strdup(map, c->opt_newntname);
+ if (!map->nt_name) {
+ d_printf(_("Out of memory!\n"));
+ TALLOC_FREE(map);
+ return -1;
+ }
+ }
+
+ if (grp != NULL)
+ map->gid = grp->gr_gid;
+
+ if (!NT_STATUS_IS_OK(pdb_update_group_mapping_entry(map))) {
+ d_fprintf(stderr, _("Could not update group mapping for %s\n"),
+ ntgroup);
+ TALLOC_FREE(map);
+ return -1;
+ }
+
+ TALLOC_FREE(map);
+ return 0;
+}
+
+static int net_groupmap_cleanup(struct net_context *c, int argc, const char **argv)
+{
+ GROUP_MAP **maps = NULL;
+ size_t i, entries;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net groupmap cleanup\n"
+ " %s\n",
+ _("Usage:"),
+ _("Delete all group mappings"));
+ return 0;
+ }
+
+ if (!pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN, &maps, &entries,
+ ENUM_ALL_MAPPED)) {
+ d_fprintf(stderr, _("Could not list group mappings\n"));
+ return -1;
+ }
+
+ for (i=0; i<entries; i++) {
+
+ if (maps[i]->gid == -1)
+ printf(_("Group %s is not mapped\n"),
+ maps[i]->nt_name);
+
+ if (!sid_check_is_in_our_sam(&maps[i]->sid) &&
+ !sid_check_is_in_builtin(&maps[i]->sid))
+ {
+ struct dom_sid_buf buf;
+ printf(_("Deleting mapping for NT Group %s, sid %s\n"),
+ maps[i]->nt_name,
+ dom_sid_str_buf(&maps[i]->sid, &buf));
+ pdb_delete_group_mapping_entry(maps[i]->sid);
+ }
+ }
+
+ TALLOC_FREE(maps);
+ return 0;
+}
+
+static int net_groupmap_addmem(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid alias, member;
+
+ if ( (argc != 2) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net groupmap addmem alias-sid member-sid\n"));
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_add_aliasmem(&alias, &member))) {
+ d_fprintf(stderr, _("Could not add sid %s to alias %s\n"),
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_delmem(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid alias, member;
+
+ if ( (argc != 2) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net groupmap delmem alias-sid member-sid\n"));
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_del_aliasmem(&alias, &member))) {
+ d_fprintf(stderr, _("Could not delete sid %s from alias %s\n"),
+ argv[1], argv[0]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_listmem(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid alias;
+ struct dom_sid *members;
+ size_t i, num;
+
+ if ( (argc != 1) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net groupmap listmem alias-sid\n"));
+ return -1;
+ }
+
+ members = NULL;
+ num = 0;
+
+ if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(&alias, talloc_tos(),
+ &members, &num))) {
+ d_fprintf(stderr, _("Could not list members for sid %s\n"),
+ argv[0]);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ struct dom_sid_buf buf;
+ printf("%s\n", dom_sid_str_buf(&(members[i]), &buf));
+ }
+
+ TALLOC_FREE(members);
+
+ return 0;
+}
+
+static bool print_alias_memberships(TALLOC_CTX *mem_ctx,
+ const struct dom_sid *domain_sid,
+ const struct dom_sid *member)
+{
+ uint32_t *alias_rids;
+ size_t i, num_alias_rids;
+ struct dom_sid_buf buf;
+
+ alias_rids = NULL;
+ num_alias_rids = 0;
+
+ if (!NT_STATUS_IS_OK(pdb_enum_alias_memberships(
+ mem_ctx, domain_sid, member, 1,
+ &alias_rids, &num_alias_rids))) {
+ d_fprintf(stderr, _("Could not list memberships for sid %s\n"),
+ dom_sid_str_buf(member, &buf));
+ return false;
+ }
+
+ for (i = 0; i < num_alias_rids; i++) {
+ struct dom_sid alias;
+ sid_compose(&alias, domain_sid, alias_rids[i]);
+ printf("%s\n", dom_sid_str_buf(&alias, &buf));
+ }
+
+ return true;
+}
+
+static int net_groupmap_memberships(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ struct dom_sid *domain_sid, member;
+
+ if ( (argc != 1) ||
+ c->display_usage ||
+ !string_to_sid(&member, argv[0]) ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net groupmap memberships sid\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("net_groupmap_memberships");
+ if (mem_ctx == NULL) {
+ d_fprintf(stderr, _("talloc_init failed\n"));
+ return -1;
+ }
+
+ domain_sid = get_global_sam_sid();
+ if (domain_sid == NULL) {
+ d_fprintf(stderr, _("Could not get domain sid\n"));
+ return -1;
+ }
+
+ if (!print_alias_memberships(mem_ctx, domain_sid, &member) ||
+ !print_alias_memberships(mem_ctx, &global_sid_Builtin, &member))
+ return -1;
+
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_groupmap(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ net_groupmap_add,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new group mapping"),
+ N_("net groupmap add\n"
+ " Create a new group mapping")
+ },
+ {
+ "modify",
+ net_groupmap_modify,
+ NET_TRANSPORT_LOCAL,
+ N_("Update a group mapping"),
+ N_("net groupmap modify\n"
+ " Modify an existing group mapping")
+ },
+ {
+ "delete",
+ net_groupmap_delete,
+ NET_TRANSPORT_LOCAL,
+ N_("Remove a group mapping"),
+ N_("net groupmap delete\n"
+ " Remove a group mapping")
+ },
+ {
+ "set",
+ net_groupmap_set,
+ NET_TRANSPORT_LOCAL,
+ N_("Set group mapping"),
+ N_("net groupmap set\n"
+ " Set a group mapping")
+ },
+ {
+ "cleanup",
+ net_groupmap_cleanup,
+ NET_TRANSPORT_LOCAL,
+ N_("Remove foreign group mapping entries"),
+ N_("net groupmap cleanup\n"
+ " Remove foreign group mapping entries")
+ },
+ {
+ "addmem",
+ net_groupmap_addmem,
+ NET_TRANSPORT_LOCAL,
+ N_("Add a foreign alias member"),
+ N_("net groupmap addmem\n"
+ " Add a foreign alias member")
+ },
+ {
+ "delmem",
+ net_groupmap_delmem,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete foreign alias member"),
+ N_("net groupmap delmem\n"
+ " Delete foreign alias member")
+ },
+ {
+ "listmem",
+ net_groupmap_listmem,
+ NET_TRANSPORT_LOCAL,
+ N_("List foreign group members"),
+ N_("net groupmap listmem\n"
+ " List foreign alias members")
+ },
+ {
+ "memberships",
+ net_groupmap_memberships,
+ NET_TRANSPORT_LOCAL,
+ N_("List foreign group memberships"),
+ N_("net groupmap memberships\n"
+ " List foreign group memberships")
+ },
+ {
+ "list",
+ net_groupmap_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List current group map"),
+ N_("net groupmap list\n"
+ " List current group map")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c,argc, argv, "net groupmap", func);
+}
+
diff --git a/source3/utils/net_help.c b/source3/utils/net_help.c
new file mode 100644
index 0000000..4aba1c5
--- /dev/null
+++ b/source3/utils/net_help.c
@@ -0,0 +1,69 @@
+/*
+ Samba Unix/Linux SMB client library
+ net help commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+static int net_usage(struct net_context *c, int argc, const char **argv);
+
+static int net_help_usage(struct net_context *c, int argc, const char **argv)
+{
+ c->display_usage = true;
+ return net_usage(c, argc, argv);
+}
+
+static int net_usage(struct net_context *c, int argc, const char **argv)
+{
+ struct functable *table = (struct functable*) c->private_data;
+ int i;
+
+ d_printf(_("Usage:\n"));
+ for (i=0; table[i].funcname != NULL; i++) {
+ if (c->display_usage) {
+ d_printf(_("net %s usage:\n"), table[i].funcname);
+ d_printf("\n%s\n\n", _(table[i].usage));
+ } else {
+ d_printf("%s %-15s %s\n", "net", table[i].funcname,
+ _(table[i].description));
+ }
+
+ }
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+/*
+ handle "net help *" subcommands
+*/
+int net_help(struct net_context *c, int argc, const char **argv)
+{
+ struct functable *func = (struct functable *)c->private_data;
+
+ if (argc == 0) {
+ return net_usage(c, argc, argv);
+ }
+
+ if (strcasecmp_m(argv[0], "help") == 0) {
+ return net_help_usage(c, argc, argv);
+ }
+
+ c->display_usage = true;
+ return net_run_function(c, argc, argv, "net help", func);
+}
diff --git a/source3/utils/net_help_common.c b/source3/utils/net_help_common.c
new file mode 100644
index 0000000..a861d3f
--- /dev/null
+++ b/source3/utils/net_help_common.c
@@ -0,0 +1,95 @@
+/*
+ Samba Unix/Linux SMB client library
+ net help commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_common_methods_usage(struct net_context *c, int argc, const char**argv)
+{
+ d_printf(_("Valid methods: (auto-detected if not specified)\n"));
+ d_printf(_("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n"));
+ d_printf(_("\trpc\t\t\t\tDCE-RPC\n"));
+ d_printf(_("\trap\t\t\t\tRAP (older systems)\n"));
+ d_printf("\n");
+ return 0;
+}
+
+int net_common_flags_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("Valid targets: choose one (none defaults to localhost)\n"));
+ d_printf(_("\t-S|--server=<server>\t\t\tserver name\n"));
+ d_printf(_("\t-I|--ipaddress=<ipaddr>\t\t\taddress of target server\n"));
+ d_printf(_("\t-w|--target-workgroup=<wg>\t\ttarget workgroup or domain\n"));
+
+ d_printf("\n");
+ d_printf(_("Valid misc options are:\n")); /* misc options */
+ d_printf(_("\t-p|--port=<port>\t\t\tconnection port on target\n"));
+ d_printf(_("\t--myname=<name>\t\t\t\tclient name\n"));
+ d_printf(_("\t--long\t\t\t\t\tDisplay full information\n"));
+
+ d_printf("\n");
+ d_printf(_("Valid common options are:\n")); /* misc options */
+ d_printf(_("\t-d|--debuglevel=<level>\t\t\tdebug level (0-10)\n"));
+ d_printf(_("\t--debug-stdout\t\t\t\tSend debug output to standard "
+ "output\n"));
+ d_printf(_("\t--configfile=<path>\t\t\tpathname of smb.conf file\n"));
+ d_printf(_("\t--option=name=value\t\t\tSet smb.conf option from "
+ "command line\n"));
+ d_printf(_("\t-l|--log-basename=LOGFILEBASE\t\tBasename for "
+ "log/debug files\n"));
+ d_printf(_("\t--leak-report\t\t\t\tenable talloc leak reporting on "
+ "exit\n"));
+ d_printf(_("\t--leak-report-full\t\t\tenable full talloc leak "
+ "reporting on exit\n"));
+ d_printf(_("\t-V|--version\t\t\t\tPrint samba version information\n"));
+
+ d_printf("\n");
+ d_printf(_("Valid connection options are:\n")); /* misc options */
+ d_printf(_("\t-R|--name-resolve=NAME-RESOLVE-ORDER\tUse these name "
+ "resolution services only\n"));
+ d_printf(_("\t-O|--socket-options=SOCKETOPTIONS\tsocket options to use\n"));
+ d_printf(_("\t-m|--max-protocol=MAXPROTOCOL\t\tSet max protocol level\n"));
+ d_printf(_("\t-n|--netbiosname=NETBIOSNAME\t\tPrimary netbios name\n"));
+ d_printf(_("\t--netbios-scope=SCOPE\t\t\tUse this Netbios scope\n"));
+ d_printf(_("\t-W|--workgroup=WORKGROUP\t\tSet the workgroup name\n"));
+ d_printf(_("\t--realm=REALM\t\t\t\tSet the realm name\n"));
+
+ d_printf("\n");
+ d_printf(_("Valid credential options are:\n")); /* misc options */
+ d_printf(_("\t-U|--user=[DOMAIN/]USERNAME[%%PASSWORD]\tSet the "
+ "network username\n"));
+ d_printf(_("\t-N|--no-pass\t\t\t\tDon't ask for a password\n"));
+ d_printf(_("\t--password=STRING\t\t\tSet a password\n"));
+ d_printf(_("\t--pw-nt-hash\t\t\t\tThe supplied password is the NT hash\n"));
+ d_printf(_("\t-A|--authentication-file=FILE\t\tGet the "
+ "credentials from a file\n"));
+ d_printf(_("\t-P|--machine-pass\t\t\tUse stored machine account password\n"));
+ d_printf(_("\t--simple-bind-dn=DN\t\t\tDN to use for a simple bind\n"));
+ d_printf(_("\t--use-kerberos=desired|required|off\tUse kerberos "
+ "authentication\n"));
+ d_printf(_("\t--use-krb5-ccache=CCACHE\t\tCredentials cache location "
+ "for Kerberos\n"));
+ d_printf(_("\t--use-winbind-ccache\t\t\tUse the winbind ccache for "
+ "authentication\n"));
+ d_printf(_("\t--client-protection=sign|encrypt|off\tConfigure used "
+ "protection for client connections\n"));
+
+ return -1;
+}
+
diff --git a/source3/utils/net_help_common.h b/source3/utils/net_help_common.h
new file mode 100644
index 0000000..ed85993
--- /dev/null
+++ b/source3/utils/net_help_common.h
@@ -0,0 +1,49 @@
+/*
+ Samba Unix/Linux SMB client library
+ net help commands
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _NET_HELP_COMMON_H_
+#define _NET_HELP_COMMON_H_
+
+/**
+ * Get help for common methods.
+ *
+ * This will output some help for using the ADS/RPC/RAP transports.
+ *
+ * @param c A net_context structure
+ * @param argc Normal argc with previous parameters removed
+ * @param argv Normal argv with previous parameters removed
+ * @return 0 on success, nonzero on failure.
+ */
+int net_common_methods_usage(struct net_context *c, int argc, const char**argv);
+
+/**
+ * Get help for common flags.
+ *
+ * This will output some help for using common flags.
+ *
+ * @param c A net_context structure
+ * @param argc Normal argc with previous parameters removed
+ * @param argv Normal argv with previous parameters removed
+ * @return 0 on success, nonzero on failure.
+ */
+int net_common_flags_usage(struct net_context *c, int argc, const char **argv);
+
+
+#endif /* _NET_HELP_COMMON_H_*/
+
diff --git a/source3/utils/net_idmap.c b/source3/utils/net_idmap.c
new file mode 100644
index 0000000..027b741
--- /dev/null
+++ b/source3/utils/net_idmap.c
@@ -0,0 +1,1413 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2003 Andrew Bartlett (abartlet@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "utils/net.h"
+#include "secrets.h"
+#include "idmap.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/security.h"
+#include "net_idmap_check.h"
+#include "util_tdb.h"
+#include "idmap_autorid_tdb.h"
+#include "lib/util/smb_strtox.h"
+
+#define ALLOC_CHECK(mem) do { \
+ if (!mem) { \
+ d_fprintf(stderr, _("Out of memory!\n")); \
+ talloc_free(ctx); \
+ return -1; \
+ } } while(0)
+
+enum idmap_dump_backend {
+ TDB,
+ AUTORID
+};
+
+struct net_idmap_ctx {
+ enum idmap_dump_backend backend;
+};
+
+static int net_idmap_dump_one_autorid_entry(struct db_record *rec,
+ void *unused)
+{
+ TDB_DATA key;
+ TDB_DATA value;
+
+ key = dbwrap_record_get_key(rec);
+ value = dbwrap_record_get_value(rec);
+
+ if (strncmp((char *)key.dptr, "CONFIG", 6) == 0) {
+ char *config = talloc_array(talloc_tos(), char, value.dsize+1);
+ memcpy(config, value.dptr, value.dsize);
+ config[value.dsize] = '\0';
+ printf("CONFIG: %s\n", config);
+ talloc_free(config);
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "NEXT RANGE", 10) == 0) {
+ printf("RANGE HWM: %"PRIu32"\n", IVAL(value.dptr, 0));
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "NEXT ALLOC UID", 14) == 0) {
+ printf("UID HWM: %"PRIu32"\n", IVAL(value.dptr, 0));
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "NEXT ALLOC GID", 14) == 0) {
+ printf("GID HWM: %"PRIu32"\n", IVAL(value.dptr, 0));
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "UID", 3) == 0 ||
+ strncmp((char *)key.dptr, "GID", 3) == 0)
+ {
+ /* mapped entry from allocation pool */
+ printf("%s %s\n", value.dptr, key.dptr);
+ return 0;
+ }
+
+ if ((strncmp((char *)key.dptr, "S-1-5-", 6) == 0 ||
+ strncmp((char *)key.dptr, "ALLOC", 5) == 0) &&
+ value.dsize == sizeof(uint32_t))
+ {
+ /* this is a domain range assignment */
+ uint32_t range = IVAL(value.dptr, 0);
+ printf("RANGE %"PRIu32": %s\n", range, key.dptr);
+ return 0;
+ }
+
+ return 0;
+}
+
+/***********************************************************
+ Helper function for net_idmap_dump. Dump one entry.
+ **********************************************************/
+static int net_idmap_dump_one_tdb_entry(struct db_record *rec,
+ void *unused)
+{
+ TDB_DATA key;
+ TDB_DATA value;
+
+ key = dbwrap_record_get_key(rec);
+ value = dbwrap_record_get_value(rec);
+
+ if (strcmp((char *)key.dptr, "USER HWM") == 0) {
+ printf(_("USER HWM %d\n"), IVAL(value.dptr,0));
+ return 0;
+ }
+
+ if (strcmp((char *)key.dptr, "GROUP HWM") == 0) {
+ printf(_("GROUP HWM %d\n"), IVAL(value.dptr,0));
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "S-", 2) != 0) {
+ return 0;
+ }
+
+ printf("%s %s\n", value.dptr, key.dptr);
+ return 0;
+}
+
+/* returns db path for idmap backend alloced on talloc_tos */
+static char *net_idmap_dbfile(struct net_context *c,
+ struct net_idmap_ctx *ctx)
+{
+ char *dbfile = NULL;
+ const char *backend = NULL;
+
+ backend = lp_idmap_default_backend();
+ if (!backend) {
+ d_printf(_("Internal error: 'idmap config * : backend' is not set!\n"));
+ return NULL;
+ }
+
+ if (c->opt_db != NULL) {
+ dbfile = talloc_strdup(talloc_tos(), c->opt_db);
+ if (dbfile == NULL) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ }
+ } else if (strequal(backend, "tdb")) {
+ dbfile = state_path(talloc_tos(), "winbindd_idmap.tdb");
+ if (dbfile == NULL) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ }
+ ctx->backend = TDB;
+ } else if (strequal(backend, "tdb2")) {
+ dbfile = talloc_asprintf(talloc_tos(), "%s/idmap2.tdb",
+ lp_private_dir());
+ if (dbfile == NULL) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ }
+ ctx->backend = TDB;
+ } else if (strequal(backend, "autorid")) {
+ dbfile = state_path(talloc_tos(), "autorid.tdb");
+ if (dbfile == NULL) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ }
+ ctx->backend = AUTORID;
+ } else {
+ char *_backend = talloc_strdup(talloc_tos(), backend);
+ char* args = strchr(_backend, ':');
+ if (args != NULL) {
+ *args = '\0';
+ }
+
+ d_printf(_("Sorry, 'idmap backend = %s' is currently not supported\n"),
+ _backend);
+
+ talloc_free(_backend);
+ }
+
+ return dbfile;
+}
+
+static bool net_idmap_opendb_autorid(TALLOC_CTX *mem_ctx,
+ struct net_context *c,
+ bool readonly,
+ struct db_context **db)
+{
+ bool ret = false;
+ char *dbfile = NULL;
+ struct net_idmap_ctx ctx = { .backend = AUTORID };
+
+ if (c == NULL) {
+ goto done;
+ }
+
+ dbfile = net_idmap_dbfile(c, &ctx);
+ if (dbfile == NULL) {
+ goto done;
+ }
+
+ if (ctx.backend != AUTORID) {
+ d_fprintf(stderr, _("Unsupported backend\n"));
+ goto done;
+ }
+
+ if (readonly) {
+ *db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDONLY, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (*db == NULL) {
+ d_fprintf(stderr,
+ _("Could not open autorid db (%s): %s\n"),
+ dbfile, strerror(errno));
+ goto done;
+ }
+ } else {
+ NTSTATUS status;
+ status = idmap_autorid_db_init(dbfile, mem_ctx, db);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Error calling idmap_autorid_db_init: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ }
+
+ ret = true;
+
+done:
+ talloc_free(dbfile);
+ return ret;
+}
+
+
+/***********************************************************
+ Dump the current idmap
+ **********************************************************/
+static int net_idmap_dump(struct net_context *c, int argc, const char **argv)
+{
+ struct db_context *db;
+ TALLOC_CTX *mem_ctx;
+ const char* dbfile;
+ NTSTATUS status;
+ int ret = -1;
+ struct net_idmap_ctx ctx = { .backend = TDB };
+
+ if ( argc > 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap dump [[--db=]<inputfile>]\n"
+ " Dump current ID mapping.\n"
+ " inputfile\tTDB file to read mappings from.\n"));
+ return c->display_usage?0:-1;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ dbfile = (argc > 0) ? argv[0] : net_idmap_dbfile(c, &ctx);
+ if (dbfile == NULL) {
+ goto done;
+ }
+ d_fprintf(stderr, _("dumping id mapping from %s\n"), dbfile);
+
+ db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDONLY, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"),
+ dbfile, strerror(errno));
+ goto done;
+ }
+
+ if (ctx.backend == AUTORID) {
+ status = dbwrap_traverse_read(db,
+ net_idmap_dump_one_autorid_entry,
+ NULL, NULL);
+ } else {
+ status = dbwrap_traverse_read(db,
+ net_idmap_dump_one_tdb_entry,
+ NULL, NULL);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("error traversing the database\n"));
+ ret = -1;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+/***********************************************************
+ Write entries from stdin to current local idmap
+ **********************************************************/
+
+static int net_idmap_store_id_mapping(struct db_context *db,
+ enum id_type type,
+ unsigned long idval,
+ const char *sid_string)
+{
+ NTSTATUS status;
+ char *idstr = NULL;
+
+ switch(type) {
+ case ID_TYPE_UID:
+ idstr = talloc_asprintf(talloc_tos(), "UID %lu", idval);
+ break;
+ case ID_TYPE_GID:
+ idstr = talloc_asprintf(talloc_tos(), "GID %lu", idval);
+ break;
+ default:
+ d_fprintf(stderr, "Invalid id mapping type: %d\n", type);
+ return -1;
+ }
+
+ status = dbwrap_store_bystring(db, idstr,
+ string_term_tdb_data(sid_string),
+ TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Error storing ID -> SID: "
+ "%s\n", nt_errstr(status));
+ talloc_free(idstr);
+ return -1;
+ }
+ status = dbwrap_store_bystring(db, sid_string,
+ string_term_tdb_data(idstr),
+ TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Error storing SID -> ID: "
+ "%s\n", nt_errstr(status));
+ talloc_free(idstr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_idmap_restore(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ FILE *input = NULL;
+ struct db_context *db;
+ const char *dbfile = NULL;
+ int ret = 0;
+ struct net_idmap_ctx ctx = { .backend = TDB };
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap restore [--db=<TDB>] [<inputfile>]\n"
+ " Restore ID mappings from file\n"
+ " TDB\tFile to store ID mappings to."
+ " inputfile\tFile to load ID mappings from. If not "
+ "given, load data from stdin.\n"));
+ return 0;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ dbfile = net_idmap_dbfile(c, &ctx);
+
+ if (dbfile == NULL) {
+ ret = -1;
+ goto done;
+ }
+
+ if (ctx.backend != TDB) {
+ d_fprintf(stderr, _("Sorry, restoring of non-TDB databases is "
+ "currently not supported\n"));
+ ret = -1;
+ goto done;
+ }
+
+ d_fprintf(stderr, _("restoring id mapping to %s\n"), dbfile);
+
+ if (argc == 1) {
+ input = fopen(argv[0], "r");
+ if (input == NULL) {
+ d_fprintf(stderr, _("Could not open input file (%s): %s\n"),
+ argv[0], strerror(errno));
+ ret = -1;
+ goto done;
+ }
+ } else {
+ input = stdin;
+ }
+
+ db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"),
+ dbfile, strerror(errno));
+ ret = -1;
+ goto done;
+ }
+
+ if (dbwrap_transaction_start(db) != 0) {
+ d_fprintf(stderr, _("Failed to start transaction.\n"));
+ ret = -1;
+ goto done;
+ }
+
+ while (!feof(input)) {
+ char line[128], sid_string[128];
+ int len;
+ unsigned long idval;
+ NTSTATUS status;
+
+ if (fgets(line, 127, input) == NULL)
+ break;
+
+ len = strlen(line);
+
+ if ( (len > 0) && (line[len-1] == '\n') )
+ line[len-1] = '\0';
+
+ if (sscanf(line, "GID %lu %127s", &idval, sid_string) == 2)
+ {
+ ret = net_idmap_store_id_mapping(db, ID_TYPE_GID,
+ idval, sid_string);
+ if (ret != 0) {
+ break;
+ }
+ } else if (sscanf(line, "UID %lu %127s", &idval, sid_string) == 2)
+ {
+ ret = net_idmap_store_id_mapping(db, ID_TYPE_UID,
+ idval, sid_string);
+ if (ret != 0) {
+ break;
+ }
+ } else if (sscanf(line, "USER HWM %lu", &idval) == 1) {
+ status = dbwrap_store_int32_bystring(
+ db, "USER HWM", idval);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Could not store USER HWM: %s\n"),
+ nt_errstr(status));
+ break;
+ }
+ } else if (sscanf(line, "GROUP HWM %lu", &idval) == 1) {
+ status = dbwrap_store_int32_bystring(
+ db, "GROUP HWM", idval);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Could not store GROUP HWM: %s\n"),
+ nt_errstr(status));
+ break;
+ }
+ } else {
+ d_fprintf(stderr, _("ignoring invalid line [%s]\n"),
+ line);
+ continue;
+ }
+ }
+
+ if (ret == 0) {
+ if(dbwrap_transaction_commit(db) != 0) {
+ d_fprintf(stderr, _("Failed to commit transaction.\n"));
+ ret = -1;
+ }
+ } else {
+ if (dbwrap_transaction_cancel(db) != 0) {
+ d_fprintf(stderr, _("Failed to cancel transaction.\n"));
+ }
+ }
+
+done:
+ if ((input != NULL) && (input != stdin)) {
+ fclose(input);
+ }
+
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static
+NTSTATUS dbwrap_delete_mapping(struct db_context *db, TDB_DATA key1, bool force)
+{
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ bool is_valid_mapping;
+ NTSTATUS status = NT_STATUS_OK;
+ TDB_DATA val1, val2;
+
+ ZERO_STRUCT(val1);
+ ZERO_STRUCT(val2);
+
+ status = dbwrap_fetch(db, mem_ctx, key1, &val1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to fetch: %.*s\n", (int)key1.dsize, key1.dptr));
+ goto done;
+ }
+
+ if (val1.dptr == NULL) {
+ DEBUG(1, ("invalid mapping: %.*s -> empty value\n",
+ (int)key1.dsize, key1.dptr));
+ status = NT_STATUS_FILE_INVALID;
+ goto done;
+ }
+
+ DEBUG(2, ("mapping: %.*s -> %.*s\n",
+ (int)key1.dsize, key1.dptr, (int)val1.dsize, val1.dptr));
+
+ status = dbwrap_fetch(db, mem_ctx, val1, &val2);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to fetch: %.*s\n", (int)val1.dsize, val1.dptr));
+ goto done;
+ }
+
+ is_valid_mapping = tdb_data_equal(key1, val2);
+
+ if (!is_valid_mapping) {
+ DEBUG(1, ("invalid mapping: %.*s -> %.*s -> %.*s\n",
+ (int)key1.dsize, key1.dptr,
+ (int)val1.dsize, val1.dptr,
+ (int)val2.dsize, val2.dptr));
+ if ( !force ) {
+ status = NT_STATUS_FILE_INVALID;
+ goto done;
+ }
+ }
+
+ status = dbwrap_delete(db, key1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to delete: %.*s\n", (int)key1.dsize, key1.dptr));
+ goto done;
+ }
+
+ if (!is_valid_mapping) {
+ goto done;
+ }
+
+ status = dbwrap_delete(db, val1);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("failed to delete: %.*s\n", (int)val1.dsize, val1.dptr));
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static
+NTSTATUS delete_mapping_action(struct db_context *db, void* data)
+{
+ return dbwrap_delete_mapping(db, *(TDB_DATA*)data, false);
+}
+static
+NTSTATUS delete_mapping_action_force(struct db_context *db, void* data)
+{
+ return dbwrap_delete_mapping(db, *(TDB_DATA*)data, true);
+}
+
+/***********************************************************
+ Delete a SID mapping from a winbindd_idmap.tdb
+ **********************************************************/
+static bool delete_args_ok(int argc, const char **argv)
+{
+ if (argc != 1)
+ return false;
+ if (strncmp(argv[0], "S-", 2) == 0)
+ return true;
+ if (strncmp(argv[0], "GID ", 4) == 0)
+ return true;
+ if (strncmp(argv[0], "UID ", 4) == 0)
+ return true;
+ return false;
+}
+
+static int net_idmap_delete_mapping(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ struct db_context *db;
+ TALLOC_CTX *mem_ctx;
+ TDB_DATA key;
+ NTSTATUS status;
+ const char* dbfile;
+ struct net_idmap_ctx ctx = { .backend = TDB };
+
+ if ( !delete_args_ok(argc,argv) || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap delete mapping [-f] [--db=<TDB>] <ID>\n"
+ " Delete mapping of ID from TDB.\n"
+ " -f\tforce\n"
+ " TDB\tidmap database\n"
+ " ID\tSID|GID|UID\n"));
+ return c->display_usage ? 0 : -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ dbfile = net_idmap_dbfile(c, &ctx);
+ if (dbfile == NULL) {
+ goto done;
+ }
+ d_fprintf(stderr, _("deleting id mapping from %s\n"), dbfile);
+
+ db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDWR, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (db == NULL) {
+ d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"),
+ dbfile, strerror(errno));
+ goto done;
+ }
+
+ key = string_term_tdb_data(argv[0]);
+
+ status = dbwrap_trans_do(db, (c->opt_force
+ ? delete_mapping_action_force
+ : delete_mapping_action), &key);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("could not delete mapping: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ ret = 0;
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static bool parse_uint32(const char *str, uint32_t *result)
+{
+ unsigned long val;
+ int error = 0;
+
+ val = smb_strtoul(str, NULL, 10, &error, SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ return false;
+ }
+
+ *result = val; /* Potential crop */
+ return true;
+}
+
+static void net_idmap_autorid_delete_range_usage(void)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap delete range [-f] [--db=<TDB>] <RANGE>|(<SID>[ <INDEX>])\n"
+ " Delete a domain range mapping from the database.\n"
+ " -f\tforce\n"
+ " TDB\tidmap database\n"
+ " RANGE\tthe range number to delete\n"
+ " SID\t\tSID of the domain\n"
+ " INDEX\trange index number do delete for the domain\n"));
+}
+
+static int net_idmap_autorid_delete_range(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ struct db_context *db = NULL;
+ NTSTATUS status;
+ uint32_t rangenum;
+ uint32_t range_index;
+ const char *domsid;
+ TALLOC_CTX *mem_ctx = NULL;
+ bool ok;
+ bool force = (c->opt_force != 0);
+
+ if (c->display_usage) {
+ net_idmap_autorid_delete_range_usage();
+ return 0;
+ }
+
+ if (argc < 1 || argc > 2) {
+ net_idmap_autorid_delete_range_usage();
+ return -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+ if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) {
+ goto done;
+ }
+
+ ok = parse_uint32(argv[0], &rangenum);
+ if (ok) {
+ d_printf("%s: %"PRIu32"\n", _("Deleting range number"),
+ rangenum);
+
+ status = idmap_autorid_delete_range_by_num(db, rangenum,
+ force);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Failed to delete domain range mapping"),
+ nt_errstr(status));
+ } else {
+ ret = 0;
+ }
+
+ goto done;
+ }
+
+ domsid = argv[0];
+ range_index = 0;
+
+ if (argc == 2) {
+ ok = parse_uint32(argv[1], &range_index);
+ if (!ok) {
+ d_printf("%s: %s\n",
+ _("Invalid index specification"), argv[1]);
+ net_idmap_autorid_delete_range_usage();
+ goto done;
+ }
+ }
+
+ status = idmap_autorid_delete_range_by_sid(db, domsid, range_index,
+ force);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Failed to delete domain range mapping"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static void net_idmap_autorid_delete_ranges_usage(void)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap delete ranges [-f] [--db=<TDB>] <SID>\n"
+ " Delete all domain range mappings for a given domain.\n"
+ " -f\tforce\n"
+ " TDB\tidmap database\n"
+ " SID\t\tSID of the domain\n"));
+}
+
+static int net_idmap_autorid_delete_ranges(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ struct db_context *db = NULL;
+ NTSTATUS status;
+ const char *domsid;
+ TALLOC_CTX *mem_ctx = NULL;
+ bool force = (c->opt_force != 0);
+ int count = 0;
+
+ if (c->display_usage) {
+ net_idmap_autorid_delete_ranges_usage();
+ return 0;
+ }
+
+ if (argc != 1) {
+ net_idmap_autorid_delete_ranges_usage();
+ return -1;
+ }
+
+ domsid = argv[0];
+
+ mem_ctx = talloc_stackframe();
+ if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_delete_domain_ranges(db, domsid, force, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s %s: %s\n",
+ _("Failed to delete domain range mappings for "
+ "domain"),
+ domsid,
+ nt_errstr(status));
+ goto done;
+ }
+
+ d_printf(_("deleted %d domain mappings\n"), count);
+
+ ret = 0;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static int net_idmap_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "mapping",
+ net_idmap_delete_mapping,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete ID mapping"),
+ N_("net idmap delete mapping <ID>\n"
+ " Delete ID mapping")
+ },
+ {
+ "range",
+ net_idmap_autorid_delete_range,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a domain range mapping"),
+ N_("net idmap delete range <RANGE>|(<SID>[ <INDEX>])\n"
+ " Delete a domain range mapping")
+ },
+ {
+ "ranges",
+ net_idmap_autorid_delete_ranges,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete all domain range mappings for a given "
+ "domain"),
+ N_("net idmap delete ranges <SID>\n"
+ " Delete a domain range mapping")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net idmap delete", func);
+}
+
+
+static int net_idmap_set_mapping(struct net_context *c,
+ int argc, const char **argv)
+{
+ d_printf("%s\n", _("Not implemented yet"));
+ return -1;
+}
+
+static void net_idmap_autorid_set_range_usage(void)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap set range"
+ " <range> <SID> [<index>] [--db=<inputfile>]\n"
+ " Store a domain-range mapping for a given domain.\n"
+ " range\tRange number to be set for the domain\n"
+ " SID\t\tSID of the domain\n"
+ " index\trange-index number to be set for the domain\n"
+ " inputfile\tTDB file to add mapping to.\n"));
+}
+
+static int net_idmap_autorid_set_range(struct net_context *c,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *mem_ctx;
+ struct db_context *db = NULL;
+ const char *domsid;
+ uint32_t rangenum;
+ uint32_t range_index = 0;
+ NTSTATUS status;
+ bool ok;
+
+ if (c->display_usage) {
+ net_idmap_autorid_set_range_usage();
+ return 0;
+ }
+
+ if (argc < 2 || argc > 3) {
+ net_idmap_autorid_set_range_usage();
+ return -1;
+ }
+
+ ok = parse_uint32(argv[0], &rangenum);
+ if (!ok) {
+ d_printf("%s: %s\n", _("Invalid range specification"),
+ argv[0]);
+ net_idmap_autorid_set_range_usage();
+ return -1;
+ }
+
+ domsid = argv[1];
+
+ if (argc == 3) {
+ ok = parse_uint32(argv[2], &range_index);
+ if (!ok) {
+ d_printf("%s: %s\n",
+ _("Invalid index specification"), argv[2]);
+ net_idmap_autorid_set_range_usage();
+ return -1;
+ }
+ }
+
+ mem_ctx = talloc_stackframe();
+ if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_setrange(db, domsid, range_index, rangenum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Failed to save domain mapping"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static bool idmap_store_secret(const char *backend,
+ const char *domain,
+ const char *identity,
+ const char *secret)
+{
+ char *tmp;
+ int r;
+ bool ret;
+
+ r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain);
+
+ if (r < 0) return false;
+
+ /* make sure the key is case insensitive */
+ if (!strupper_m(tmp)) {
+ free(tmp);
+ return false;
+ }
+ ret = secrets_store_generic(tmp, identity, secret);
+
+ free(tmp);
+ return ret;
+}
+
+
+static int net_idmap_secret(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *ctx;
+ const char *secret;
+ const char *dn;
+ char *domain;
+ char *backend;
+ char *opt = NULL;
+ bool ret;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:\n"),
+ _("net idmap set secret <DOMAIN> <secret>\n"
+ " Set the secret for the specified domain\n"
+ " DOMAIN\tDomain to set secret for.\n"
+ " secret\tNew secret to set.\n"));
+ return c->display_usage?0:-1;
+ }
+
+ secret = argv[1];
+
+ ctx = talloc_new(NULL);
+ ALLOC_CHECK(ctx);
+
+ domain = talloc_strdup(ctx, argv[0]);
+ ALLOC_CHECK(domain);
+
+ opt = talloc_asprintf(ctx, "idmap config %s", domain);
+ ALLOC_CHECK(opt);
+
+ backend = talloc_strdup(ctx, lp_parm_const_string(-1, opt, "backend", "tdb"));
+ ALLOC_CHECK(backend);
+
+ if ((!backend) || (!strequal(backend, "ldap") &&
+ !strequal(backend, "rfc2307"))) {
+ d_fprintf(stderr,
+ _("The only currently supported backend are LDAP "
+ "and rfc2307\n"));
+ talloc_free(ctx);
+ return -1;
+ }
+
+ dn = lp_parm_const_string(-1, opt, "ldap_user_dn", NULL);
+ if ( ! dn) {
+ d_fprintf(stderr,
+ _("Missing ldap_user_dn option for domain %s\n"),
+ domain);
+ talloc_free(ctx);
+ return -1;
+ }
+
+ ret = idmap_store_secret("ldap", domain, dn, secret);
+
+ if ( ! ret) {
+ d_fprintf(stderr, _("Failed to store secret\n"));
+ talloc_free(ctx);
+ return -1;
+ }
+
+ d_printf(_("Secret stored\n"));
+ return 0;
+}
+
+static int net_idmap_autorid_set_config(struct net_context *c,
+ int argc, const char **argv)
+{
+ int ret = -1;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct db_context *db = NULL;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap set config <config>"
+ " [--db=<inputfile>]\n"
+ " Update CONFIG entry in autorid.\n"
+ " config\tConfig string to be stored\n"
+ " inputfile\tTDB file to update config.\n"));
+ return c->display_usage ? 0 : -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_saveconfigstr(db, argv[0]);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Error storing the config in the database: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_idmap_set(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "mapping",
+ net_idmap_set_mapping,
+ NET_TRANSPORT_LOCAL,
+ N_("Not implemented yet"),
+ N_("net idmap set mapping\n"
+ " Not implemented yet")
+ },
+ {
+ "range",
+ net_idmap_autorid_set_range,
+ NET_TRANSPORT_LOCAL,
+ N_("Store a domain-range mapping"),
+ N_("net idmap set range\n"
+ " Store a domain-range mapping")
+ },
+ {
+ "config",
+ net_idmap_autorid_set_config,
+ NET_TRANSPORT_LOCAL,
+ N_("Save the global configuration in the autorid database"),
+ N_("net idmap set config \n"
+ " Save the global configuration in the autorid database ")
+ },
+ {
+ "secret",
+ net_idmap_secret,
+ NET_TRANSPORT_LOCAL,
+ N_("Set secret for specified domain"),
+ N_("net idmap set secret <DOMAIN> <secret>\n"
+ " Set secret for specified domain")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net idmap set", func);
+}
+
+static void net_idmap_autorid_get_range_usage(void)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap get range <SID> [<index>] [--db=<inputfile>]\n"
+ " Get the range for a given domain and index.\n"
+ " SID\t\tSID of the domain\n"
+ " index\trange-index number to be retrieved\n"
+ " inputfile\tTDB file to add mapping to.\n"));
+}
+
+
+static int net_idmap_autorid_get_range(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *mem_ctx;
+ struct db_context *db = NULL;
+ const char *domsid;
+ uint32_t rangenum;
+ uint32_t range_index = 0;
+ uint32_t low_id;
+ NTSTATUS status;
+ char *keystr;
+ bool ok;
+
+ if (c->display_usage) {
+ net_idmap_autorid_get_range_usage();
+ return 0;
+ }
+
+ if (argc < 1 || argc > 2) {
+ net_idmap_autorid_get_range_usage();
+ return -1;
+ }
+
+ domsid = argv[0];
+
+ if (argc == 2) {
+ ok = parse_uint32(argv[1], &range_index);
+ if (!ok) {
+ d_printf("%s: %s\n",
+ _("Invalid index specification"), argv[1]);
+ net_idmap_autorid_get_range_usage();
+ return -1;
+ }
+ }
+
+ mem_ctx = talloc_stackframe();
+ if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_getrange(db, domsid, range_index, &rangenum,
+ &low_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Failed to load domain range"), nt_errstr(status));
+ goto done;
+ }
+
+ if (range_index == 0) {
+ keystr = talloc_strdup(mem_ctx, domsid);
+ } else {
+ keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid,
+ range_index);
+ }
+
+ printf("RANGE %"PRIu32": %s (low id: %"PRIu32")\n",
+ rangenum, keystr, low_id);
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static NTSTATUS net_idmap_autorid_print_range(struct db_context *db,
+ const char *domsid,
+ uint32_t range_index,
+ uint32_t rangenum,
+ void *private_data)
+{
+ if (range_index == 0) {
+ printf("RANGE %"PRIu32": %s\n", rangenum, domsid);
+ } else {
+ printf("RANGE %"PRIu32": %s#%"PRIu32"\n", rangenum, domsid,
+ range_index);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void net_idmap_autorid_get_ranges_usage(void)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap get ranges [<SID>] [--db=<inputfile>]\n"
+ " Get all ranges for a given domain.\n"
+ " SID\t\tSID of the domain - list all ranges if omitted\n"
+ " inputfile\tTDB file to add mapping to.\n"));
+}
+
+static int net_idmap_autorid_get_ranges(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *mem_ctx;
+ struct db_context *db = NULL;
+ const char *domsid;
+ NTSTATUS status;
+
+ if (c->display_usage) {
+ net_idmap_autorid_get_ranges_usage();
+ return 0;
+ }
+
+ if (argc == 0) {
+ domsid = NULL;
+ } else if (argc == 1) {
+ domsid = argv[0];
+ } else {
+ net_idmap_autorid_get_ranges_usage();
+ return -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+ if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_iterate_domain_ranges_read(db,
+ domsid,
+ net_idmap_autorid_print_range,
+ NULL, /* private_data */
+ NULL /* count */);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Error getting domain ranges"), nt_errstr(status));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static int net_idmap_autorid_get_config(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ char *config;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ struct db_context *db = NULL;
+
+ if (argc > 0 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap get config"
+ " [--db=<inputfile>]\n"
+ " Get CONFIG entry from autorid database\n"
+ " inputfile\tTDB file to read config from.\n"));
+ return c->display_usage ? 0 : -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) {
+ goto done;
+ }
+
+ status = idmap_autorid_getconfigstr(db, mem_ctx, &config);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "%s: %s\n",
+ _("Error: unable to read config entry"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ printf("CONFIG: %s\n", config);
+ ret = 0;
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+
+static int net_idmap_get(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "range",
+ net_idmap_autorid_get_range,
+ NET_TRANSPORT_LOCAL,
+ N_("Get the range for a domain and range-index"),
+ N_("net idmap get range\n"
+ " Get the range for a domain and range-index")
+ },
+ {
+ "ranges",
+ net_idmap_autorid_get_ranges,
+ NET_TRANSPORT_LOCAL,
+ N_("Get all ranges for a domain"),
+ N_("net idmap get ranges <SID>\n"
+ " Get all ranges for a domain")
+ },
+ {
+ "config",
+ net_idmap_autorid_get_config,
+ NET_TRANSPORT_LOCAL,
+ N_("Get the global configuration from the autorid database"),
+ N_("net idmap get config \n"
+ " Get the global configuration from the autorid database ")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net idmap get", func);
+}
+
+static int net_idmap_check(struct net_context *c, int argc, const char **argv)
+{
+ char *dbfile;
+ struct check_options opts;
+ struct net_idmap_ctx ctx = { .backend = TDB };
+ int ret;
+
+ if ( argc > 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net idmap check [-v] [-r] [-a] [-T] [-f] [-l] [[--db=]<TDB>]\n"
+ " Check an idmap database.\n"
+ " --verbose,-v\tverbose\n"
+ " --repair,-r\trepair\n"
+ " --auto,-a\tnoninteractive mode\n"
+ " --test,-T\tdry run\n"
+ " --force,-f\tforce\n"
+ " --lock,-l\tlock db while doing the check\n"
+ " TDB\tidmap database\n"));
+ return c->display_usage ? 0 : -1;
+ }
+
+ if (argc > 0) {
+ dbfile = talloc_strdup(talloc_tos(), argv[0]);
+ } else {
+ dbfile = net_idmap_dbfile(c, &ctx);
+ }
+ if (dbfile == NULL) {
+ return -1;
+ }
+
+ if (ctx.backend != TDB) {
+ d_fprintf(stderr, _("Sorry, checking of non-TDB databases is "
+ "currently not supported\n"));
+ talloc_free(dbfile);
+ return -1;
+ }
+
+ d_fprintf(stderr, _("check database: %s\n"), dbfile);
+
+ opts = (struct check_options) {
+ .lock = c->opt_lock || c->opt_long_list_entries,
+ .test = c->opt_testmode,
+ .automatic = c->opt_auto,
+ .verbose = c->opt_verbose,
+ .force = c->opt_force,
+ .repair = c->opt_repair || c->opt_reboot,
+ };
+
+ ret = net_idmap_check_db(dbfile, &opts);
+ talloc_free(dbfile);
+ return ret;
+}
+
+/***********************************************************
+ Look at the current idmap
+ **********************************************************/
+int net_idmap(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "dump",
+ net_idmap_dump,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump the current ID mapping database"),
+ N_("net idmap dump\n"
+ " Dump the current ID mappings")
+ },
+ {
+ "restore",
+ net_idmap_restore,
+ NET_TRANSPORT_LOCAL,
+ N_("Restore entries from a file or stdin"),
+ N_("net idmap restore\n"
+ " Restore entries from stdin")
+ },
+ {
+ "get",
+ net_idmap_get,
+ NET_TRANSPORT_LOCAL,
+ N_("Read data from the ID mapping database"),
+ N_("net idmap get\n"
+ " Read data from the ID mapping database")
+ },
+ {
+ "set",
+ net_idmap_set,
+ NET_TRANSPORT_LOCAL,
+ N_("Write data to the ID mapping database"),
+ N_("net idmap set\n"
+ " Write data to the ID mapping database")
+ },
+ {
+ "delete",
+ net_idmap_delete,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete entries from the ID mapping database"),
+ N_("net idmap delete\n"
+ " Delete entries from the ID mapping database")
+ },
+ {
+ "check",
+ net_idmap_check,
+ NET_TRANSPORT_LOCAL,
+ N_("Check id mappings"),
+ N_("net idmap check\n"
+ " Check id mappings")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net idmap", func);
+}
+
+
diff --git a/source3/utils/net_idmap_check.c b/source3/utils/net_idmap_check.c
new file mode 100644
index 0000000..51f4a40
--- /dev/null
+++ b/source3/utils/net_idmap_check.c
@@ -0,0 +1,974 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Check the idmap database.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Mar 2011
+ */
+
+#include "net_idmap_check.h"
+#include "includes.h"
+#include "system/filesys.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "net.h"
+#include "../libcli/security/dom_sid.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include "util_tdb.h"
+#include "interact.h"
+
+static int traverse_commit(struct db_record *diff_rec, void* data);
+static int traverse_check(struct db_record *rec, void* data);
+
+/* TDB_DATA *******************************************************************/
+static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d);
+static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr);
+
+/* record *********************************************************************/
+
+enum DT {
+ DT_INV = 0,
+ DT_SID, DT_UID, DT_GID,
+ DT_HWM, DT_VER, DT_SEQ,
+};
+
+struct record {
+ enum DT key_type, val_type;
+ TDB_DATA key, val;
+ struct dom_sid sid;
+ long unsigned id;
+};
+
+static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val);
+static struct record* reverse_record(struct record* rec);
+
+static bool is_invalid(const struct record* r) {
+ return (r->key_type == DT_INV) || (r->val_type == DT_INV);
+}
+
+static bool is_map(const struct record* r) {
+ return (r->key_type == DT_SID)
+ || (r->key_type == DT_UID) || (r->key_type == DT_GID);
+}
+
+/* action *********************************************************************/
+
+typedef struct check_action {
+ void (*fmt)(struct check_action *a,
+ struct record *r,
+ TDB_DATA *v);
+ const char* name;
+ const char* prompt;
+ const char* answers;
+ char auto_action;
+ char default_action;
+ bool verbose;
+} check_action;
+
+struct check_actions {
+ check_action invalid_record;
+ check_action missing_reverse;
+ check_action invalid_mapping;
+ check_action invalid_edit;
+ check_action record_exists;
+ check_action no_version;
+ check_action wrong_version;
+ check_action invalid_hwm;
+ check_action commit;
+ check_action valid_mapping;
+ check_action valid_other;
+ check_action invalid_diff;
+};
+
+static void invalid_mapping_fmt(struct check_action *a,
+ struct record *r,
+ TDB_DATA *v)
+{
+ d_printf("%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n",
+ a->name,
+ print_data(r, r->key),
+ print_data(r, r->val),
+ (v ? print_data(r, *v) : ""));
+}
+
+static void record_exists_fmt(struct check_action *a,
+ struct record *r,
+ TDB_DATA *v)
+{
+ d_printf("%1$s: %2$s\n-%4$s\n+%3$s\n",
+ a->name,
+ print_data(r, r->key),
+ print_data(r, r->val),
+ (v ? print_data(r, *v) : ""));
+}
+
+static void valid_mapping_fmt(struct check_action *a,
+ struct record *r,
+ TDB_DATA *v)
+{
+ d_printf("%1$s: %2$s <-> %3$s\n",
+ a->name,
+ print_data(r, r->key),
+ print_data(r, r->val));
+}
+
+static struct check_actions
+check_actions_init(const struct check_options* opts) {
+ struct check_actions ret = {
+ .invalid_record = (check_action) {
+ .name = "Invalid record",
+ .prompt = "[e]dit/[d]elete/[D]elete all"
+ "/[s]kip/[S]kip all",
+ .answers = "eds",
+ .default_action = 'e',
+ .verbose = true,
+ },
+ .missing_reverse = (check_action) {
+ .name = "Missing reverse mapping for",
+ .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all"
+ "/[s]kip/[S]kip all",
+ .answers = "feds",
+ .default_action = 'f',
+ .verbose = true,
+ },
+ .invalid_mapping = (check_action) {
+ .fmt = invalid_mapping_fmt,
+ .name = "Invalid mapping",
+ .prompt = "[e]dit/[d]elete/[D]elete all"
+ "/[s]kip/[S]kip all",
+ .answers = "eds",
+ .default_action = 'd',
+ .verbose = true,
+ },
+ .invalid_edit = (check_action) {
+ .name = "Invalid record",
+ .prompt = "[e]dit/[d]elete/[D]elete all"
+ "/[s]kip/[S]kip all",
+ .answers = "eds",
+ .default_action = 'e',
+ .verbose = true,
+ },
+ .record_exists = (check_action) {
+ .fmt = record_exists_fmt,
+ .name = "Record exists",
+ .prompt = "[o]verwrite/[O]verwrite all/[e]dit"
+ "/[d]elete/[D]elete all/[s]kip/[S]kip all",
+ .answers = "oeds",
+ .default_action = 'o',
+ .verbose = true,
+ },
+ .no_version = (check_action) {
+ .prompt = "[f]ix/[s]kip/[a]bort",
+ .answers = "fsa",
+ .default_action = 'f',
+ },
+ .wrong_version = (check_action) {
+ .prompt = "[f]ix/[s]kip/[a]bort",
+ .answers = "fsa",
+ .default_action = 'a',
+ },
+ .invalid_hwm = (check_action) {
+ .prompt = "[f]ix/[s]kip",
+ .answers = "fs",
+ .default_action = 'f',
+ },
+ .commit = (check_action) {
+ .prompt = "[c]ommit/[l]ist/[s]kip",
+ .answers = "cls",
+ .default_action = 'l',
+ .verbose = true,
+ },
+ .valid_mapping = (check_action) {
+ .fmt = valid_mapping_fmt,
+ .name = "Mapping",
+ .auto_action = 's',
+ .verbose = opts->verbose,
+ },
+ .valid_other = (check_action) {
+ .name = "Other",
+ .auto_action = 's',
+ .verbose = opts->verbose,
+ },
+ .invalid_diff = (check_action) {
+ .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all"
+ "/[a]bort",
+ .answers = "sca",
+ .default_action = 's',
+ },
+ };
+
+ if (!opts->repair) {
+ ret.invalid_record.auto_action = 's';
+ ret.missing_reverse.auto_action = 's';
+ ret.invalid_mapping.auto_action = 's';
+ ret.no_version.auto_action = 's';
+ ret.wrong_version.auto_action = 's';
+ ret.invalid_hwm.auto_action = 's';
+ ret.commit.auto_action = 's';
+ }
+
+ if (opts->automatic) {
+ ret.invalid_record.auto_action = 'd'; /* delete */
+ ret.missing_reverse.auto_action = 'f'; /* fix */
+ ret.invalid_mapping.auto_action = 'd'; /* delete */
+ ret.no_version.auto_action = 'f'; /* fix */
+ ret.wrong_version.auto_action = 'a'; /* abort */
+ ret.invalid_hwm.auto_action = 'f'; /* fix */
+ ret.commit.auto_action = 'c'; /* commit */
+ ret.invalid_diff.auto_action = 'a'; /* abort */
+ if (opts->force) {
+ ret.wrong_version.auto_action = 'f'; /* fix */
+ ret.invalid_diff.auto_action = 'c'; /* commit */
+ }
+ }
+ if (opts->test) {
+ ret.invalid_diff.auto_action = 'c'; /* commit */
+/* ret.commit.auto_action = 'c';*/ /* commit */
+ }
+
+ return ret;
+}
+
+static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) {
+ char ret;
+ if (a->verbose && (r != NULL)) {
+ if (!a->fmt) {
+ d_printf("%s: %s ", a->name, print_data(r, r->key));
+ if (is_map(r)) {
+ d_printf("-> %s\n", print_data(r, r->val));
+ } else if (r->key_type == DT_HWM ||
+ r->key_type == DT_VER ||
+ r->key_type == DT_SEQ)
+ {
+ d_printf(": %ld\n", r->id);
+ } else {
+ d_printf("\n");
+ }
+ } else {
+ a->fmt(a, r, v);
+ }
+ }
+
+ if (a->auto_action != '\0') {
+ return a->auto_action;
+ }
+
+ ret = interact_prompt(a->prompt, a->answers, a->default_action);
+
+ if (isupper(ret)) {
+ ret = tolower(ret);
+ a->auto_action = ret;
+ }
+ a->default_action = ret;
+ return ret;
+}
+
+/* *************************************************************************/
+
+typedef struct {
+ TDB_DATA oval, nval;
+} TDB_DATA_diff;
+
+static TDB_DATA pack_diff(TDB_DATA_diff* diff) {
+ return (TDB_DATA) {
+ .dptr = (uint8_t *)diff,
+ .dsize = sizeof(TDB_DATA_diff),
+ };
+}
+
+static TDB_DATA_diff unpack_diff(TDB_DATA data) {
+ assert(data.dsize == sizeof(TDB_DATA_diff));
+ return *(TDB_DATA_diff*)data.dptr;
+}
+
+#define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \
+ DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \
+ if (!tdb_data_is_empty(OLD)) { \
+ DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \
+ } \
+ if (!tdb_data_is_empty(NEW)) { \
+ DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \
+ }
+
+struct check_ctx {
+ int oflags;
+ char* name;
+ bool transaction;
+ struct db_context *db;
+ struct db_context *diff;
+ struct check_actions action;
+
+ uint32_t uid_hwm;
+ uint32_t gid_hwm;
+
+ unsigned n_invalid_record;
+ unsigned n_missing_reverse;
+ unsigned n_invalid_mappping;
+ unsigned n_map;
+ unsigned n_other;
+ unsigned n_diff;
+ struct check_options opts;
+};
+
+
+static void adjust_hwm(struct check_ctx* ctx, const struct record* r);
+
+static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value)
+{
+ NTSTATUS status;
+ TDB_DATA_diff diff;
+ TALLOC_CTX* mem = talloc_new(ctx->diff);
+ TDB_DATA recvalue;
+ struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key);
+
+ if (rec == NULL) {
+ return -1;
+ }
+
+ recvalue = dbwrap_record_get_value(rec);
+
+ if (recvalue.dptr == 0) { /* first entry */
+ status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval);
+ if (!NT_STATUS_IS_OK(status)) {
+ diff.oval = tdb_null;
+ }
+ } else {
+ diff = unpack_diff(recvalue);
+ talloc_free(diff.nval.dptr);
+ }
+ diff.nval = tdb_data_talloc_copy(ctx->diff, value);
+
+ DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval);
+
+ status = dbwrap_record_store(rec, pack_diff(&diff), 0);
+
+ talloc_free(mem);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
+ return -1;
+ }
+ ctx->n_diff ++;
+ return 0;
+}
+
+static int del_record(struct check_ctx* ctx, TDB_DATA key) {
+ return add_record(ctx, key, tdb_null);
+}
+
+static TDB_DATA
+fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key)
+{
+ TDB_DATA tmp;
+ NTSTATUS status;
+
+ status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp);
+
+ if (NT_STATUS_IS_OK(status)) {
+ TDB_DATA_diff diff = unpack_diff(tmp);
+ TDB_DATA ret = tdb_data_talloc_copy(mem_ctx, diff.nval);
+ talloc_free(tmp.dptr);
+ return ret;
+ }
+
+ status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp);
+ if (!NT_STATUS_IS_OK(status)) {
+ return tdb_null;
+ }
+
+ return tmp;
+}
+
+static void edit_record(struct record* r) {
+ TALLOC_CTX* mem = talloc_new(r);
+ cbuf* ost = cbuf_new(mem);
+ const char* str;
+ struct record* nr;
+ TDB_DATA key;
+ TDB_DATA val;
+ cbuf_printf(ost, "%s %s\n",
+ print_data(mem, r->key), print_data(mem, r->val));
+ str = interact_edit(mem, cbuf_gets(ost, 0));
+ key = parse_data(mem, &str);
+ val = parse_data(mem, &str);
+ nr = parse_record(talloc_parent(r), key, val);
+ if (nr != NULL) {
+ *r = *nr;
+ }
+ talloc_free(mem);
+}
+
+static bool check_version(struct check_ctx* ctx) {
+ static const char* key = "IDMAP_VERSION";
+ uint32_t version;
+ NTSTATUS status;
+ char action = 's';
+ struct check_actions* act = &ctx->action;
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("No version number, assume 2\n");
+ action = get_action(&act->no_version, NULL, NULL);
+ } else if (version != 2) {
+ d_printf("Wrong version number %d, should be 2\n", version);
+ action = get_action(&act->wrong_version, NULL, NULL);
+ }
+ switch (action) {
+ case 's':
+ break;
+ case 'f':
+ SIVAL(&version, 0, 2);
+ add_record(ctx, string_term_tdb_data(key),
+ make_tdb_data((uint8_t *)&version, sizeof(uint32_t)));
+ break;
+ case 'a':
+ return false;
+ default:
+ assert(false);
+ }
+ return true;
+}
+
+static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) {
+ uint32_t hwm;
+ char action = 's';
+ NTSTATUS status;
+ struct check_actions* act = &ctx->action;
+
+ status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("No %s should be %d\n", key, target);
+ action = get_action(&act->invalid_hwm, NULL, NULL);
+ } else if (target < hwm) {
+ d_printf("Invalid %s %d: should be %d\n", key, hwm, target);
+ action = get_action(&act->invalid_hwm, NULL, NULL);
+ }
+ if (action == 'f') {
+ SIVAL(&hwm, 0, target);
+ add_record(ctx, string_term_tdb_data(key),
+ make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t)));
+ }
+}
+
+int traverse_check(struct db_record *rec, void* data) {
+ struct check_ctx* ctx = (struct check_ctx*)data;
+ struct check_actions* act = &ctx->action;
+ TALLOC_CTX* mem = talloc_new(ctx->diff);
+ TDB_DATA key;
+ TDB_DATA value;
+ struct record *r;
+ char action = 's';
+
+ key = dbwrap_record_get_key(rec);
+ value = dbwrap_record_get_value(rec);
+
+ r = parse_record(mem, key, value);
+
+ if (is_invalid(r)) {
+ action = get_action(&act->invalid_record, r, NULL);
+ ctx->n_invalid_record++;
+ } else if (is_map(r)) {
+ TDB_DATA back = fetch_record(ctx, mem, r->val);
+ if (back.dptr == NULL) {
+ action = get_action(&act->missing_reverse, r, NULL);
+ ctx->n_missing_reverse++;
+ } else if (!tdb_data_equal(r->key, back)) {
+ action = get_action(&act->invalid_mapping, r, &back);
+ ctx->n_invalid_mappping++;
+ } else {
+ if (r->key_type == DT_SID) {
+ action = get_action(&act->valid_mapping, r, NULL);
+ ctx->n_map++;
+ } else {
+ action = get_action(&act->valid_mapping, NULL,
+ NULL);
+ }
+ }
+ adjust_hwm(ctx, r);
+ } else {
+ action = get_action(&act->valid_other, r, NULL);
+ ctx->n_other++;
+ }
+
+ while (action) {
+ switch (action) {
+ case 's': /* skip */
+ break;
+ case 'd': /* delete */
+ del_record(ctx, key);
+ break;
+ case 'f': /* add reverse mapping */
+ add_record(ctx, value, key);
+ break;
+ case 'e': /* edit */
+ edit_record(r);
+ action = 'o';
+ if (is_invalid(r)) {
+ action = get_action(&act->invalid_edit, r,NULL);
+ continue;
+ }
+ if (!tdb_data_equal(key, r->key)) {
+ TDB_DATA oval = fetch_record(ctx, mem, r->key);
+ if (!tdb_data_is_empty(oval) &&
+ !tdb_data_equal(oval, r->val))
+ {
+ action = get_action(&act->record_exists,
+ r, &oval);
+ if (action != 'o') {
+ continue;
+ }
+ }
+ }
+ if (is_map(r)) {
+ TDB_DATA okey = fetch_record(ctx, mem, r->val);
+ if (!tdb_data_is_empty(okey) &&
+ !tdb_data_equal(okey, r->key))
+ {
+ action = get_action(&act->record_exists,
+ reverse_record(r),
+ &okey);
+ }
+ }
+ continue;
+ case 'o': /* overwrite */
+ adjust_hwm(ctx, r);
+ if (!tdb_data_equal(key, r->key)) {
+ del_record(ctx, key);
+ }
+ add_record(ctx, r->key, r->val);
+ if (is_map(r)) {
+ add_record(ctx, r->val, r->key);
+ }
+ }
+ action = '\0';
+ };
+
+ talloc_free(mem);
+
+ return 0;
+}
+
+/******************************************************************************/
+
+void adjust_hwm(struct check_ctx* ctx, const struct record* r) {
+ enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type;
+ if (type == DT_UID) {
+ ctx->uid_hwm = MAX(ctx->uid_hwm, r->id);
+ } else if (type == DT_GID) {
+ ctx->gid_hwm = MAX(ctx->gid_hwm, r->id);
+ }
+}
+
+static bool is_cstr(TDB_DATA str) {
+ return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0';
+}
+
+static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) {
+ struct dom_sid tmp;
+ const char* s = (const char*)str.dptr;
+ if ((s[0] == 'S') && string_to_sid(&tmp, s)) {
+ *sid = tmp;
+ *type = DT_SID;
+ return true;
+ }
+ return false;
+}
+
+static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) {
+ char c, t;
+ unsigned long tmp;
+ if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) {
+ if (c == 'U') {
+ *id = tmp;
+ *type = DT_UID;
+ return true;
+ } else if (c == 'G') {
+ *id = tmp;
+ *type = DT_GID;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+struct record*
+parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val)
+{
+ struct record* ret = talloc_zero(mem_ctx, struct record);
+ if (ret == NULL) {
+ DEBUG(0, ("Out of memory.\n"));
+ return NULL;
+ }
+ ret->key = tdb_data_talloc_copy(ret, key);
+ ret->val = tdb_data_talloc_copy(ret, val);
+ if ((ret->key.dptr == NULL && key.dptr != NULL) ||
+ (ret->val.dptr == NULL && val.dptr != NULL))
+ {
+ talloc_free(ret);
+ DEBUG(0, ("Out of memory.\n"));
+ return NULL;
+ }
+ assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV));
+
+ if (!is_cstr(key)) {
+ return ret;
+ }
+ if (parse_sid(key, &ret->key_type, &ret->sid)) {
+ parse_xid(val, &ret->val_type, &ret->id);
+ } else if (parse_xid(key, &ret->key_type, &ret->id)) {
+ if (is_cstr(val)) {
+ parse_sid(val, &ret->val_type, &ret->sid);
+ }
+ } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) {
+ ret->key_type = DT_HWM;
+ if (val.dsize == 4) {
+ ret->id = IVAL(val.dptr,0);
+ ret->val_type = DT_UID;
+ }
+ } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) {
+ ret->key_type = DT_HWM;
+ if (val.dsize == 4) {
+ ret->id = IVAL(val.dptr,0);
+ ret->val_type = DT_GID;
+ }
+ } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) {
+ ret->key_type = DT_VER;
+ if (val.dsize == 4) {
+ ret->id = IVAL(val.dptr,0);
+ ret->val_type = DT_VER;
+ }
+ } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) {
+ ret->key_type = DT_SEQ;
+ if (val.dsize == 8) {
+ ret->id = *(uint64_t*)val.dptr;
+ ret->val_type = DT_SEQ;
+ }
+ }
+
+ return ret;
+}
+
+struct record* reverse_record(struct record* in)
+{
+ return parse_record(talloc_parent(in), in->val, in->key);
+}
+
+
+/******************************************************************************/
+
+
+char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d)
+{
+ if (!tdb_data_is_empty(d)) {
+ char* ret = NULL;
+ cbuf* ost = cbuf_new(mem_ctx);
+ int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize);
+ if (len != -1) {
+ cbuf_swapptr(ost, &ret, 0);
+ talloc_steal(mem_ctx, ret);
+ }
+ talloc_free(ost);
+ return ret;
+ }
+ return talloc_strdup(mem_ctx, "<NULL>");
+}
+
+
+TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) {
+ cbuf* ost = cbuf_new(mem_ctx);
+ TDB_DATA ret = tdb_null;
+ srprs_skipws(ptr);
+ if (srprs_quoted(ptr, ost)) {
+ ret.dsize = cbuf_getpos(ost);
+ ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0));
+ }
+ talloc_free(ost);
+ return ret;
+}
+
+static int traverse_print_diff(struct db_record *rec, void* data) {
+ struct check_ctx* ctx = (struct check_ctx*)data;
+ TDB_DATA key;
+ TDB_DATA value;
+ TDB_DATA_diff diff;
+ TALLOC_CTX* mem = talloc_new(ctx->diff);
+
+ key = dbwrap_record_get_key(rec);
+ value = dbwrap_record_get_value(rec);
+ diff = unpack_diff(value);
+
+ DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval);
+
+ talloc_free(mem);
+ return 0;
+}
+
+
+static int traverse_commit(struct db_record *diff_rec, void* data) {
+ struct check_ctx* ctx = (struct check_ctx*)data;
+ TDB_DATA key;
+ TDB_DATA diff_value;
+ TDB_DATA_diff diff;
+ TDB_DATA value;
+ TALLOC_CTX* mem = talloc_new(ctx->diff);
+ int ret = -1;
+ NTSTATUS status;
+ struct check_actions* act = &ctx->action;
+ struct db_record* rec;
+
+ key = dbwrap_record_get_key(diff_rec);
+ diff_value = dbwrap_record_get_value(diff_rec);
+ diff = unpack_diff(diff_value);
+
+ rec = dbwrap_fetch_locked(ctx->db, mem, key);
+ if (rec == NULL) {
+ goto done;
+ }
+
+ value = dbwrap_record_get_value(rec);
+
+ if (!tdb_data_equal(value, diff.oval)) {
+ char action;
+
+ d_printf("Warning: record has changed: %s\n"
+ "expected: %s got %s\n", print_data(mem, key),
+ print_data(mem, diff.oval),
+ print_data(mem, value));
+
+ action = get_action(&act->invalid_diff, NULL, NULL);
+ if (action == 's') {
+ ret = 0;
+ goto done;
+ } else if (action == 'a') {
+ goto done;
+ }
+ }
+
+ DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval);
+
+ if (tdb_data_is_empty(diff.nval)) {
+ status = dbwrap_record_delete(rec);
+ } else {
+ status = dbwrap_record_store(rec, diff.nval, 0);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("could not store record %s\n", nt_errstr(status)));
+ if (!ctx->opts.force) {
+ goto done;
+ }
+ }
+ ret = 0;
+done:
+ talloc_free(mem);
+ return ret;
+}
+
+static struct check_ctx*
+check_init(TALLOC_CTX* mem_ctx, const struct check_options* o)
+{
+ struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx);
+ if (ctx == NULL) {
+ DEBUG(0, (_("No memory\n")));
+ return NULL;
+ }
+
+ ctx->diff = db_open_rbt(ctx);
+ if (ctx->diff == NULL) {
+ talloc_free(ctx);
+ DEBUG(0, (_("No memory\n")));
+ return NULL;
+ }
+
+ ctx->action = check_actions_init(o);
+ ctx->opts = *o;
+ return ctx;
+}
+
+static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags)
+{
+ if (name == NULL) {
+ d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n"));
+ return false;
+ }
+
+ if (ctx->db != NULL) {
+ if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) {
+ return true;
+ } else {
+ TALLOC_FREE(ctx->db);
+ }
+ }
+
+ ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (ctx->db == NULL) {
+ d_fprintf(stderr,
+ _("Could not open idmap db (%s) for writing: %s\n"),
+ name, strerror(errno));
+ return false;
+ }
+
+ if (ctx->name != name) {
+ TALLOC_FREE(ctx->name);
+ ctx->name = talloc_strdup(ctx, name);
+ }
+
+ ctx->oflags = oflags;
+ return true;
+}
+
+static bool check_do_checks(struct check_ctx* ctx)
+{
+ NTSTATUS status;
+
+ if (!check_version(ctx)) {
+ return false;
+ }
+
+ status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed to traverse %s\n", ctx->name));
+ return false;
+ }
+
+ check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1);
+ check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1);
+
+ return true;
+}
+
+static void check_summary(const struct check_ctx* ctx)
+{
+ d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm);
+ d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other);
+ d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n",
+ ctx->n_invalid_record, ctx->n_missing_reverse,
+ ctx->n_invalid_mappping);
+ d_printf("%u changes:\n", ctx->n_diff);
+}
+
+static bool check_transaction_start(struct check_ctx* ctx) {
+ return (dbwrap_transaction_start(ctx->db) == 0);
+}
+
+static bool check_transaction_commit(struct check_ctx* ctx) {
+ return (dbwrap_transaction_commit(ctx->db) == 0);
+}
+
+static bool check_transaction_cancel(struct check_ctx* ctx) {
+ return (dbwrap_transaction_cancel(ctx->db) == 0);
+}
+
+
+static void check_diff_list(struct check_ctx* ctx) {
+ NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("failed to traverse diff\n"));
+ }
+
+}
+
+static bool check_commit(struct check_ctx* ctx)
+{
+ struct check_actions* act = &ctx->action;
+ char action;
+ NTSTATUS status = NT_STATUS_OK;
+
+ check_summary(ctx);
+
+ if (ctx->n_diff == 0) {
+ return true;
+ }
+
+ while ((action = get_action(&act->commit, NULL, NULL)) == 'l') {
+ check_diff_list(ctx);
+ }
+ if (action == 's') {
+ return true;
+ }
+ assert(action == 'c');
+
+ if (!check_open_db(ctx, ctx->name, O_RDWR)) {
+ return false;
+ }
+
+ if (!check_transaction_start(ctx)) {
+ return false;
+ }
+
+ status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ check_transaction_cancel(ctx);
+ return false;
+ }
+ if (ctx->opts.test) { /*get_action? */
+ return check_transaction_cancel(ctx);
+ } else {
+ return check_transaction_commit(ctx);
+ }
+}
+
+int net_idmap_check_db(const char* db, const struct check_options* o)
+{
+ int ret = -1;
+ TALLOC_CTX* mem_ctx = talloc_stackframe();
+ struct check_ctx* ctx = check_init(mem_ctx, o);
+
+ if (!o->automatic && !isatty(STDIN_FILENO)) {
+ DEBUG(0, ("Interactive use needs tty, use --auto\n"));
+ goto done;
+ }
+ if (o->lock) {
+ if (check_open_db(ctx, db, O_RDWR)
+ && check_transaction_start(ctx))
+ {
+ if ( check_do_checks(ctx)
+ && check_commit(ctx)
+ && check_transaction_commit(ctx))
+ {
+ ret = 0;
+ } else {
+ check_transaction_cancel(ctx);
+ }
+ }
+ } else {
+ if (check_open_db(ctx, db, O_RDONLY)
+ && check_do_checks(ctx)
+ && check_commit(ctx))
+ {
+ ret = 0;
+ }
+ }
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+/*Local Variables:*/
+/*mode: c*/
+/*End:*/
diff --git a/source3/utils/net_idmap_check.h b/source3/utils/net_idmap_check.h
new file mode 100644
index 0000000..4176342
--- /dev/null
+++ b/source3/utils/net_idmap_check.h
@@ -0,0 +1,48 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Check the idmap database.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Mar 2011
+ */
+
+#ifndef NET_IDMAP_CHECK_H
+#define NET_IDMAP_CHECK_H
+
+#include <stdbool.h>
+
+struct net_context;
+
+struct check_options {
+ bool test;
+ bool verbose;
+ bool lock;
+ bool automatic;
+ bool force;
+ bool repair;
+};
+
+int net_idmap_check_db(const char* db, const struct check_options* opts);
+
+#endif /* NET_IDMAP_CHECK_H */
+
+/*Local Variables:*/
+/*mode: c*/
+/*End:*/
diff --git a/source3/utils/net_join.c b/source3/utils/net_join.c
new file mode 100644
index 0000000..f67f08f
--- /dev/null
+++ b/source3/utils/net_join.c
@@ -0,0 +1,55 @@
+/*
+ Samba Unix/Linux SMB client library
+ net join commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_join_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("\nnet [<method>] join [misc. options]\n"
+ "\tjoins this server to a domain\n"));
+ d_printf(_("Valid methods: (auto-detected if not specified)\n"));
+ d_printf(_("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n"));
+ d_printf(_("\trpc\t\t\t\tDCE-RPC\n"));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_join(struct net_context *c, int argc, const char **argv)
+{
+ if ((argc > 0) && (strcasecmp_m(argv[0], "HELP") == 0)) {
+ net_join_usage(c, argc, argv);
+ return 0;
+ }
+
+ net_warn_member_options();
+
+ if (net_ads_check_our_domain(c) == 0) {
+ if (net_ads_join(c, argc, argv) == 0)
+ return 0;
+ else
+ d_fprintf(stderr,
+ _("ADS join did not work, falling back to "
+ "RPC...\n"));
+ }
+ return net_rpc_join(c, argc, argv);
+}
+
+
diff --git a/source3/utils/net_lookup.c b/source3/utils/net_lookup.c
new file mode 100644
index 0000000..570135a
--- /dev/null
+++ b/source3/utils/net_lookup.c
@@ -0,0 +1,542 @@
+/*
+ Samba Unix/Linux SMB client library
+ net lookup command
+ Copyright (C) 2001 Andrew Tridgell (tridge@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "libsmb/namequery.h"
+#include "libads/sitename_cache.h"
+#include "lib/addns/dnsquery_srv.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "smb_krb5.h"
+#include "../libcli/security/security.h"
+#include "passdb/lookup_sid.h"
+#include "libsmb/dsgetdcname.h"
+
+int net_lookup_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+" net lookup [host] HOSTNAME[#<type>]\n\tgives IP for a hostname\n\n"
+" net lookup ldap [domain] [sitename]\n\tgives IP of domain's ldap server\n\n"
+" net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n"
+" net lookup pdc [domain|realm]\n\tgives IP of realm's kerberos KDC\n\n"
+" net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n"
+" net lookup master [domain|wg]\n\tgive IP of master browser\n\n"
+" net lookup name [name]\n\tLookup name's sid and type\n\n"
+" net lookup sid [sid]\n\tGive sid's name and type\n\n"
+" net lookup dsgetdcname [name] [flags] [sitename]\n\n"
+));
+ return -1;
+}
+
+/* lookup a hostname giving an IP */
+static int net_lookup_host(struct net_context *c, int argc, const char **argv)
+{
+ struct sockaddr_storage ss;
+ int name_type = 0x20;
+ char addr[INET6_ADDRSTRLEN];
+ const char *name = argv[0];
+ char *p;
+
+ if (argc == 0)
+ return net_lookup_usage(c, argc, argv);
+
+ p = strchr_m(name,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&name_type);
+ }
+
+ if (!resolve_name(name, &ss, name_type, false)) {
+ /* we deliberately use DEBUG() here to send it to stderr
+ so scripts aren't mucked up */
+ DEBUG(0,("Didn't find %s#%02x\n", name, name_type));
+ return -1;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &ss);
+ d_printf("%s\n", addr);
+ return 0;
+}
+
+#ifdef HAVE_ADS
+static void print_ldap_srvlist(struct dns_rr_srv *dclist, size_t numdcs)
+{
+ size_t i;
+
+ for ( i=0; i<numdcs; i++ ) {
+ struct dns_rr_srv *dc = &dclist[i];
+ size_t j;
+
+ for (j=0; j<dc->num_ips; j++) {
+ struct sockaddr_storage *ss = &dc->ss_s[j];
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), ss);
+#ifdef HAVE_IPV6
+ if (ss->ss_family == AF_INET6) {
+ d_printf("[%s]:%"PRIu16"\n", addr, dc->port);
+ }
+#endif
+ if (ss->ss_family == AF_INET) {
+ d_printf("%s:%"PRIu16"\n", addr, dc->port);
+ }
+ }
+ }
+}
+#endif
+
+static int net_lookup_ldap(struct net_context *c, int argc, const char **argv)
+{
+#ifdef HAVE_ADS
+ const char *domain;
+ struct sockaddr_storage ss;
+ struct dns_rr_srv *dcs = NULL;
+ size_t numdcs = 0;
+ const char *sitename = NULL;
+ TALLOC_CTX *ctx;
+ NTSTATUS status;
+ int ret;
+ char h_name[MAX_DNS_NAME_LENGTH];
+ char *query = NULL;
+
+ if (argc > 0)
+ domain = argv[0];
+ else
+ domain = c->opt_target_workgroup;
+
+ if (argc > 1) {
+ sitename = argv[1];
+ }
+
+ if ( (ctx = talloc_init("net_lookup_ldap")) == NULL ) {
+ d_fprintf(stderr,"net_lookup_ldap: talloc_init() %s!\n",
+ _("failed"));
+ return -1;
+ }
+
+ if (sitename == NULL) {
+ sitename = sitename_fetch(ctx, domain);
+ }
+ query = ads_dns_query_string_dcs(ctx, domain);
+
+ DEBUG(9, ("Lookup up ldap for domain %s\n", domain));
+
+ status = ads_dns_query_srv(
+ ctx,
+ lp_get_async_dns_timeout(),
+ sitename,
+ query,
+ &dcs,
+ &numdcs);
+ if ( NT_STATUS_IS_OK(status) && numdcs ) {
+ print_ldap_srvlist(dcs, numdcs);
+ TALLOC_FREE( ctx );
+ return 0;
+ }
+
+ DEBUG(9, ("Looking up PDC for domain %s\n", domain));
+ if (!get_pdc_ip(domain, &ss)) {
+ TALLOC_FREE( ctx );
+ return -1;
+ }
+
+ ret = sys_getnameinfo((struct sockaddr *)&ss,
+ sizeof(struct sockaddr_storage),
+ h_name, sizeof(h_name),
+ NULL, 0,
+ NI_NAMEREQD);
+
+ if (ret) {
+ TALLOC_FREE( ctx );
+ return -1;
+ }
+
+ DEBUG(9, ("Found PDC with DNS name %s\n", h_name));
+ domain = strchr(h_name, '.');
+ if (!domain) {
+ TALLOC_FREE( ctx );
+ return -1;
+ }
+ domain++;
+
+ DEBUG(9, ("Looking up ldap for domain %s\n", domain));
+
+ status = ads_dns_query_srv(
+ ctx,
+ lp_get_async_dns_timeout(),
+ sitename,
+ query,
+ &dcs,
+ &numdcs);
+ if ( NT_STATUS_IS_OK(status) && numdcs ) {
+ print_ldap_srvlist(dcs, numdcs);
+ TALLOC_FREE( ctx );
+ return 0;
+ }
+
+ TALLOC_FREE( ctx );
+
+ return -1;
+#endif
+ DEBUG(1,("No ADS support\n"));
+ return -1;
+}
+
+static int net_lookup_dc(struct net_context *c, int argc, const char **argv)
+{
+ struct samba_sockaddr *sa_list = NULL;
+ struct sockaddr_storage ss;
+ char *pdc_str = NULL;
+ const char *domain = NULL;
+ char *sitename = NULL;
+ size_t count = 0;
+ size_t i;
+ char addr[INET6_ADDRSTRLEN];
+ bool sec_ads = (lp_security() == SEC_ADS);
+ NTSTATUS status;
+
+ if (sec_ads) {
+ domain = lp_realm();
+ } else {
+ domain = c->opt_target_workgroup;
+ }
+
+ if (argc > 0)
+ domain=argv[0];
+
+ /* first get PDC */
+ if (!get_pdc_ip(domain, &ss))
+ return -1;
+
+ print_sockaddr(addr, sizeof(addr), &ss);
+ if (asprintf(&pdc_str, "%s", addr) == -1) {
+ return -1;
+ }
+ d_printf("%s\n", pdc_str);
+
+ sitename = sitename_fetch(talloc_tos(), domain);
+ status = get_sorted_dc_list(talloc_tos(),
+ domain,
+ sitename,
+ &sa_list,
+ &count,
+ sec_ads);
+ if (!NT_STATUS_IS_OK(status)) {
+ SAFE_FREE(pdc_str);
+ TALLOC_FREE(sitename);
+ return 0;
+ }
+ TALLOC_FREE(sitename);
+ for (i=0;i<count;i++) {
+ print_sockaddr(addr, sizeof(addr), &sa_list[i].u.ss);
+ if (!strequal(pdc_str, addr))
+ d_printf("%s\n", addr);
+ }
+ TALLOC_FREE(sa_list);
+ SAFE_FREE(pdc_str);
+ return 0;
+}
+
+static int net_lookup_pdc(struct net_context *c, int argc, const char **argv)
+{
+ struct sockaddr_storage ss;
+ char *pdc_str = NULL;
+ const char *domain;
+ char addr[INET6_ADDRSTRLEN];
+
+ if (lp_security() == SEC_ADS) {
+ domain = lp_realm();
+ } else {
+ domain = c->opt_target_workgroup;
+ }
+
+ if (argc > 0)
+ domain=argv[0];
+
+ /* first get PDC */
+ if (!get_pdc_ip(domain, &ss))
+ return -1;
+
+ print_sockaddr(addr, sizeof(addr), &ss);
+ if (asprintf(&pdc_str, "%s", addr) == -1) {
+ return -1;
+ }
+ d_printf("%s\n", pdc_str);
+ SAFE_FREE(pdc_str);
+ return 0;
+}
+
+
+static int net_lookup_master(struct net_context *c, int argc, const char **argv)
+{
+ struct sockaddr_storage master_ss;
+ const char *domain = c->opt_target_workgroup;
+ char addr[INET6_ADDRSTRLEN];
+
+ if (argc > 0)
+ domain=argv[0];
+
+ if (!find_master_ip(domain, &master_ss))
+ return -1;
+ print_sockaddr(addr, sizeof(addr), &master_ss);
+ d_printf("%s\n", addr);
+ return 0;
+}
+
+static int net_lookup_kdc(struct net_context *c, int argc, const char **argv)
+{
+#ifdef HAVE_KRB5
+ krb5_error_code rc;
+ krb5_context ctx;
+ struct samba_sockaddr *kdcs = NULL;
+ const char *realm;
+ char **get_host_realms = NULL;
+ size_t num_kdcs = 0;
+ size_t i;
+ NTSTATUS status;
+
+ rc = smb_krb5_init_context_common(&ctx);
+ if (rc) {
+ DBG_ERR("kerberos init context failed (%s)\n",
+ error_message(rc));
+ return -1;
+ }
+
+ if (argc > 0) {
+ realm = argv[0];
+ } else if (lp_realm() && *lp_realm()) {
+ realm = lp_realm();
+ } else {
+ rc = krb5_get_host_realm(ctx, NULL, &get_host_realms);
+ if (rc) {
+ DEBUG(1,("krb5_gethost_realm failed (%s)\n",
+ error_message(rc)));
+ krb5_free_context(ctx);
+ return -1;
+ }
+ realm = (const char *) *get_host_realms;
+ }
+
+ status = get_kdc_list(talloc_tos(),
+ realm,
+ NULL,
+ &kdcs,
+ &num_kdcs);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_WARNING("get_kdc_list failed (%s)\n",
+ nt_errstr(status));
+ krb5_free_host_realm(ctx, get_host_realms);
+ krb5_free_context(ctx);
+ return -1;
+ }
+
+ for (i = 0; i < num_kdcs; i++) {
+ char addr[INET6_ADDRSTRLEN];
+
+ print_sockaddr(addr, sizeof(addr), &kdcs[i].u.ss);
+
+ d_printf("%s:88\n", addr);
+ }
+
+ krb5_free_host_realm(ctx, get_host_realms);
+ krb5_free_context(ctx);
+ TALLOC_FREE(kdcs);
+ return 0;
+#endif
+ DEBUG(1, ("No kerberos support\n"));
+ return -1;
+}
+
+static int net_lookup_name(struct net_context *c, int argc, const char **argv)
+{
+ const char *dom, *name;
+ struct dom_sid sid;
+ struct dom_sid_buf buf;
+ enum lsa_SidType type;
+
+ if (argc != 1) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net lookup name <name>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_ALL,
+ &dom, &name, &sid, &type)) {
+ d_printf(_("Could not lookup name %s\n"), argv[0]);
+ return -1;
+ }
+
+ d_printf("%s %d (%s) %s\\%s\n", dom_sid_str_buf(&sid, &buf),
+ type, sid_type_lookup(type), dom, name);
+ return 0;
+}
+
+static int net_lookup_sid(struct net_context *c, int argc, const char **argv)
+{
+ const char *dom, *name;
+ struct dom_sid sid;
+ struct dom_sid_buf buf;
+ enum lsa_SidType type;
+
+ if (argc != 1) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net lookup sid <sid>\n"));
+ return -1;
+ }
+
+ if (!string_to_sid(&sid, argv[0])) {
+ d_printf(_("Could not convert %s to SID\n"), argv[0]);
+ return -1;
+ }
+
+ if (!lookup_sid(talloc_tos(), &sid,
+ &dom, &name, &type)) {
+ d_printf(_("Could not lookup name %s\n"), argv[0]);
+ return -1;
+ }
+
+ d_printf("%s %d (%s) %s\\%s\n", dom_sid_str_buf(&sid, &buf),
+ type, sid_type_lookup(type), dom, name);
+ return 0;
+}
+
+static int net_lookup_dsgetdcname(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ const char *domain_name = NULL;
+ const char *site_name = NULL;
+ uint32_t flags = 0;
+ struct netr_DsRGetDCNameInfo *info = NULL;
+ TALLOC_CTX *mem_ctx;
+ char *s = NULL;
+
+ if (argc < 1 || argc > 3) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net lookup dsgetdcname "
+ "<name> <flags> <sitename>\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_init("net_lookup_dsgetdcname");
+ if (!mem_ctx) {
+ return -1;
+ }
+
+ domain_name = argv[0];
+
+ if (argc >= 2) {
+ sscanf(argv[1], "%x", &flags);
+ }
+
+ if (flags == 0) {
+ flags = DS_DIRECTORY_SERVICE_REQUIRED;
+ }
+
+ if (argc == 3) {
+ site_name = argv[2];
+ }
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ return -1;
+ }
+
+ status = dsgetdcname(mem_ctx, c->msg_ctx, domain_name, NULL, site_name,
+ flags, &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("failed with: %s\n"), nt_errstr(status));
+ TALLOC_FREE(mem_ctx);
+ return -1;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, netr_DsRGetDCNameInfo, info);
+ printf("%s\n", s);
+ TALLOC_FREE(s);
+
+ TALLOC_FREE(mem_ctx);
+ return 0;
+}
+
+
+/* lookup hosts or IP addresses using internal samba lookup fns */
+int net_lookup(struct net_context *c, int argc, const char **argv)
+{
+ int i;
+
+ struct functable table[] = {
+ {
+ .funcname = "HOST",
+ .fn = net_lookup_host,
+ },
+ {
+ .funcname = "LDAP",
+ .fn = net_lookup_ldap,
+ },
+ {
+ .funcname = "DC",
+ .fn = net_lookup_dc,
+ },
+ {
+ .funcname = "PDC",
+ .fn = net_lookup_pdc,
+ },
+ {
+ .funcname = "MASTER",
+ .fn = net_lookup_master,
+ },
+ {
+ .funcname = "KDC",
+ .fn = net_lookup_kdc,
+ },
+ {
+ .funcname = "NAME",
+ .fn = net_lookup_name,
+ },
+ {
+ .funcname = "SID",
+ .fn = net_lookup_sid,
+ },
+ {
+ .funcname = "DSGETDCNAME",
+ .fn = net_lookup_dsgetdcname,
+ },
+ {
+ .funcname = NULL,
+ },
+ };
+
+ if (argc < 1) {
+ d_printf(_("\nUsage: \n"));
+ return net_lookup_usage(c, argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (strcasecmp_m(argv[0], table[i].funcname) == 0)
+ return table[i].fn(c, argc-1, argv+1);
+ }
+
+ /* Default to lookup a hostname so 'net lookup foo#1b' can be
+ used instead of 'net lookup host foo#1b'. The host syntax
+ is a bit confusing as non #00 names can't really be
+ considered hosts as such. */
+
+ return net_lookup_host(c, argc, argv);
+}
diff --git a/source3/utils/net_notify.c b/source3/utils/net_notify.c
new file mode 100644
index 0000000..eddfb0e
--- /dev/null
+++ b/source3/utils/net_notify.c
@@ -0,0 +1,199 @@
+/*
+ * Samba Unix/Linux notifyd client code
+ * Copyright (C) 2015 Volker Lendecke <vl@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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "lib/util/server_id.h"
+#include "lib/util/tevent_unix.h"
+#include "lib/util/server_id_db.h"
+#include "messages.h"
+#include "source3/smbd/notifyd/notifyd.h"
+
+static void net_notify_got_event(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
+{
+ struct notify_event_msg *event_msg;
+
+ if (data->length < offsetof(struct notify_event_msg, path) + 1) {
+ d_fprintf(stderr, "message too short\n");
+ return;
+ }
+ if (data->data[data->length-1] != 0) {
+ d_fprintf(stderr, "path not 0-terminated\n");
+ return;
+ }
+
+ event_msg = (struct notify_event_msg *)data->data;
+
+ d_printf("%u %s\n", (unsigned)event_msg->action,
+ event_msg->path);
+}
+
+static int net_notify_listen(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct messaging_context *msg_ctx = c->msg_ctx;
+ struct tevent_context *ev = messaging_tevent_context(msg_ctx);
+ struct server_id_db *names_db = messaging_names_db(msg_ctx);
+ struct server_id notifyd;
+ struct server_id_buf idbuf;
+ struct notify_rec_change_msg msg;
+ struct iovec iov[2];
+ NTSTATUS status;
+ bool ok;
+
+ if (argc != 3) {
+ d_printf("Usage: net notify listen <path> <filter> "
+ "<subdir-filter>\n");
+ return -1;
+ }
+
+ ok = server_id_db_lookup_one(names_db, "notify-daemon", &notifyd);
+ if (!ok) {
+ fprintf(stderr, "no notify daemon found\n");
+ return -1;
+ }
+
+ printf("notify daemon: %s\n", server_id_str_buf(notifyd, &idbuf));
+
+ msg = (struct notify_rec_change_msg) {
+ .instance.filter = atoi(argv[1]),
+ .instance.subdir_filter = atoi(argv[2])
+ };
+ iov[0] = (struct iovec) {
+ .iov_base = &msg,
+ .iov_len = offsetof(struct notify_rec_change_msg, path)
+ };
+ iov[1] = (struct iovec) {
+ .iov_base = discard_const_p(char, argv[0]),
+ .iov_len = strlen(argv[0])+1
+ };
+
+ status = messaging_register(c->msg_ctx, NULL, MSG_PVFS_NOTIFY,
+ net_notify_got_event);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "messaging_register failed: %s\n",
+ nt_errstr(status));
+ return -1;
+ }
+
+ status = messaging_send_iov(
+ c->msg_ctx, notifyd, MSG_SMB_NOTIFY_REC_CHANGE,
+ iov, ARRAY_SIZE(iov), NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Sending rec_change to %s returned %s\n",
+ server_id_str_buf(notifyd, &idbuf),
+ nt_errstr(status));
+ return -1;
+ }
+
+ while (true) {
+ int ret;
+
+ ret = tevent_loop_once(ev);
+ if (ret != 0) {
+ d_fprintf(stderr, "tevent_loop_once failed: %s\n",
+ strerror(errno));
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int net_notify_trigger(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct messaging_context *msg_ctx = c->msg_ctx;
+ struct server_id_db *names_db = messaging_names_db(msg_ctx);
+ struct server_id notifyd;
+ struct server_id_buf idbuf;
+ struct notify_trigger_msg msg;
+ struct iovec iov[2];
+ NTSTATUS status;
+ bool ok;
+
+ if (argc != 3) {
+ d_printf("Usage: net notify trigger <path> <action> "
+ "<filter>\n");
+ return -1;
+ }
+
+ ok = server_id_db_lookup_one(names_db, "notify-daemon", &notifyd);
+ if (!ok) {
+ fprintf(stderr, "no notify daemon found\n");
+ return -1;
+ }
+
+ printf("notify daemon: %s\n", server_id_str_buf(notifyd, &idbuf));
+
+ msg = (struct notify_trigger_msg) {
+ .action = atoi(argv[1]), .filter = atoi(argv[2])
+ };
+
+ iov[0] = (struct iovec) {
+ .iov_base = &msg,
+ .iov_len = offsetof(struct notify_trigger_msg, path)
+ };
+ iov[1] = (struct iovec) {
+ .iov_base = discard_const_p(char, argv[0]),
+ .iov_len = strlen(argv[0])+1
+ };
+
+ status = messaging_send_iov(
+ c->msg_ctx, notifyd, MSG_SMB_NOTIFY_TRIGGER,
+ iov, ARRAY_SIZE(iov), NULL, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Sending rec_change to %s returned %s\n",
+ server_id_str_buf(notifyd, &idbuf),
+ nt_errstr(status));
+ return -1;
+ }
+
+ return 0;
+}
+
+int net_notify(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ { "listen",
+ net_notify_listen,
+ NET_TRANSPORT_LOCAL,
+ N_("Register for a path and listen for changes"),
+ N_("net notify listen <path>")
+ },
+ { "trigger",
+ net_notify_trigger,
+ NET_TRANSPORT_LOCAL,
+ N_("Simulate a trigger action"),
+ N_("net notify trigger <path> <action> <filter>")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (c->msg_ctx == NULL) {
+ d_fprintf(stderr, "No connection to messaging, need to run "
+ "as root\n");
+ return -1;
+ }
+
+ return net_run_function(c, argc, argv, "net notify", func);
+}
diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c
new file mode 100644
index 0000000..3ec5c97
--- /dev/null
+++ b/source3/utils/net_offlinejoin.c
@@ -0,0 +1,600 @@
+/*
+ Samba Unix/Linux SMB client library
+ net join commands
+ Copyright (C) 2021 Guenther Deschner (gd@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include <netapi.h>
+#include "netapi/netapi_net.h"
+#include "libcli/registry/util_reg.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/cmdline/cmdline.h"
+
+int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("\nnet offlinejoin [misc. options]\n"
+ "\tjoins a computer to a domain\n"));
+ d_printf(_("Valid commands:\n"));
+ d_printf(_("\tprovision\t\t\tProvision machine account in AD\n"));
+ d_printf(_("\trequestodj\t\t\tRequest offline domain join\n"));
+ d_printf(_("\tcomposeodj\t\t\tCompose offline domain join blob\n"));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_offlinejoin(struct net_context *c, int argc, const char **argv)
+{
+ int ret;
+ NET_API_STATUS status;
+
+ if ((argc > 0) && (strcasecmp_m(argv[0], "HELP") == 0)) {
+ net_offlinejoin_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (argc == 0) {
+ net_offlinejoin_usage(c, argc, argv);
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ if (strcasecmp_m(argv[0], "provision") == 0) {
+ ret = net_offlinejoin_provision(c, argc, argv);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ if (strcasecmp_m(argv[0], "requestodj") == 0) {
+ ret = net_offlinejoin_requestodj(c, argc, argv);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ if (strcasecmp_m(argv[0], "composeodj") == 0) {
+ ret = net_offlinejoin_composeodj(c, argc, argv);
+ if (ret != 0) {
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int net_offlinejoin_provision_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("\nnet offlinejoin provision [misc. options]\n"
+ "\tProvisions machine account in AD\n"));
+ d_printf(_("Valid options:\n"));
+ d_printf(_("\tdomain=<DOMAIN>\t\t\t\tDefines AD Domain to join\n"));
+ d_printf(_("\tmachine_name=<MACHINE_NAME>\t\tDefines the machine account name\n"));
+ d_printf(_("\tmachine_account_ou=<OU>\t\t\tDefines the machine account organizational unit DN\n"));
+ d_printf(_("\tdcname=<DCNAME>\t\t\t\tSpecify a Domain Controller to join to\n"));
+ d_printf(_("\tdefpwd\t\t\t\t\tUse default machine account password\n"));
+ d_printf(_("\treuse\t\t\t\t\tReuse existing machine account in AD\n"));
+ d_printf(_("\tsavefile=<FILENAME>\t\t\tFile to store the ODJ data\n"));
+ d_printf(_("\tprintblob\t\t\t\tPrint the base64 encoded ODJ data on stdout\n"));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_offlinejoin_provision(struct net_context *c,
+ int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ const char *dcname = NULL;
+ const char *domain = NULL;
+ const char *machine_name = NULL;
+ const char *machine_account_ou = NULL;
+ const char *provision_text_data = NULL;
+ uint32_t options = 0;
+ const char *savefile = NULL;
+ bool printblob = false;
+ int i;
+
+ if (c->display_usage || argc == 1) {
+ return net_offlinejoin_provision_usage(c, argc, argv);
+ }
+
+ /* process additional command line args */
+
+ for (i = 0; i < argc; i++) {
+
+ if (strnequal(argv[i], "domain", strlen("domain"))) {
+ domain = get_string_param(argv[i]);
+ if (domain == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "machine_name", strlen("machine_name"))) {
+ machine_name = get_string_param(argv[i]);
+ if (machine_name == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "machine_account_ou", strlen("machine_account_ou"))) {
+ machine_account_ou = get_string_param(argv[i]);
+ if (machine_account_ou == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "dcname", strlen("dcname"))) {
+ dcname = get_string_param(argv[i]);
+ if (dcname == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "defpwd", strlen("defpwd"))) {
+ options |= NETSETUP_PROVISION_USE_DEFAULT_PASSWORD;
+ }
+ if (strnequal(argv[i], "reuse", strlen("reuse"))) {
+ options |= NETSETUP_PROVISION_REUSE_ACCOUNT;
+ }
+ if (strnequal(argv[i], "savefile", strlen("savefile"))) {
+ savefile = get_string_param(argv[i]);
+ if (savefile == NULL) {
+ return -1;
+ }
+ }
+ if (strnequal(argv[i], "printblob", strlen("printblob"))) {
+ printblob = true;
+ }
+ }
+
+ if (domain == NULL) {
+ d_printf("Failed to provision computer account: %s\n",
+ libnetapi_errstr(W_ERROR_V(WERR_INVALID_DOMAINNAME)));
+ return -1;
+ }
+
+ if (machine_name == NULL) {
+ d_printf("Failed to provision computer account: %s\n",
+ libnetapi_errstr(W_ERROR_V(WERR_INVALID_COMPUTERNAME)));
+ return -1;
+ }
+
+ status = NetProvisionComputerAccount(domain,
+ machine_name,
+ machine_account_ou,
+ dcname,
+ options,
+ NULL,
+ 0,
+ &provision_text_data);
+ if (status != 0) {
+ d_printf("Failed to provision computer account: %s\n",
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ return status;
+ }
+
+ if (savefile != NULL) {
+
+ DATA_BLOB ucs2_blob, blob;
+ bool ok;
+
+ /*
+ * Windows produces and consumes UTF16/UCS2 encoded blobs
+ * so we also do it for compatibility. Someone may provision an
+ * account for a Windows machine with samba.
+ */
+ ok = push_reg_sz(c, &ucs2_blob, provision_text_data);
+ if (!ok) {
+ return -1;
+ }
+
+ /* Add the unicode BOM mark */
+ blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2);
+ if (blob.data == NULL) {
+ d_printf("Failed to allocate blob: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ blob.data[0] = 0xff;
+ blob.data[1] = 0xfe;
+
+ memcpy(blob.data + 2, ucs2_blob.data, ucs2_blob.length);
+
+ ok = file_save(savefile, blob.data, blob.length);
+ if (!ok) {
+ d_printf("Failed to save %s: %s\n", savefile,
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ d_printf("Successfully provisioned computer '%s' in domain '%s'\n",
+ machine_name, domain);
+
+ if (printblob) {
+ printf("%s\n", provision_text_data);
+ }
+
+ return 0;
+}
+
+static int net_offlinejoin_requestodj_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("\nnet offlinejoin requestodj [misc. options]\n"
+ "\tRequests offline domain join\n"));
+ d_printf(_("Valid options:\n"));
+ d_printf(_("\t-i\t\t\t\t\tRead ODJ data from STDIN\n"));
+ d_printf(_("\tloadfile=<FILENAME>\t\t\tFile that provides the ODJ data\n"));
+ /*d_printf(_("\tlocalos\t\t\t\t\tModify the local machine\n"));*/
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_offlinejoin_requestodj(struct net_context *c,
+ int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ uint8_t *provision_bin_data = NULL;
+ size_t provision_bin_data_size = 0;
+ uint32_t options = NETSETUP_PROVISION_ONLINE_CALLER;
+ const char *windows_path = NULL;
+ int i;
+
+ if (c->display_usage) {
+ return net_offlinejoin_requestodj_usage(c, argc, argv);
+ }
+
+ /* process additional command line args */
+
+ for (i = 0; i < argc; i++) {
+
+ if (strnequal(argv[i], "loadfile", strlen("loadfile"))) {
+ const char *loadfile = NULL;
+
+ loadfile = get_string_param(argv[i]);
+ if (loadfile == NULL) {
+ return -1;
+ }
+
+ provision_bin_data =
+ (uint8_t *)file_load(loadfile,
+ &provision_bin_data_size,
+ 0,
+ c);
+ if (provision_bin_data == NULL) {
+ d_printf("Failed to read loadfile: %s\n",
+ loadfile);
+ return -1;
+ }
+ }
+#if 0
+ if (strnequal(argv[i], "localos", strlen("localos"))) {
+ options |= NETSETUP_PROVISION_ONLINE_CALLER;
+ }
+#endif
+ }
+
+ if (c->opt_stdin) {
+ if (isatty(STDIN_FILENO) == 1) {
+ d_fprintf(stderr,
+ "hint: stdin waiting for ODJ blob, end "
+ "with <crtl-D>.\n");
+ }
+ provision_bin_data =
+ (uint8_t *)fd_load(STDIN_FILENO,
+ &provision_bin_data_size, 0, c);
+ if (provision_bin_data == NULL) {
+ d_printf("Failed to read ODJ blob from stdin\n");
+ return -1;
+ }
+
+ /* Strip last newline */
+ if (provision_bin_data[provision_bin_data_size - 1] == '\n') {
+ provision_bin_data[provision_bin_data_size - 1] = '\0';
+ }
+ }
+
+ if (provision_bin_data == NULL || provision_bin_data_size == 0) {
+ d_printf("Please provide provision data either from file "
+ "(using loadfile parameter) or from stdin (-i)\n");
+ return -1;
+ }
+ if (provision_bin_data_size > UINT32_MAX) {
+ d_printf("provision binary data size too big: %zu\n",
+ provision_bin_data_size);
+ return -1;
+ }
+
+ status = NetRequestOfflineDomainJoin(provision_bin_data,
+ provision_bin_data_size,
+ options,
+ windows_path);
+ if (status != 0 && status != 0x00000a99) {
+ /* NERR_JoinPerformedMustRestart */
+ printf("Failed to call NetRequestOfflineDomainJoin: %s\n",
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ return -1;
+ }
+
+ d_printf("Successfully requested Offline Domain Join\n");
+
+ return 0;
+}
+
+static int net_offlinejoin_composeodj_usage(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ d_printf(_("\nnet offlinejoin composeodj [misc. options]\n"
+ "\tComposes offline domain join blob\n"));
+ d_printf(_("Valid options:\n"));
+ d_printf(_("\tdomain_sid=<SID>\t\t\tThe domain SID\n"));
+ d_printf(_("\tdomain_guid=<GUID>\t\t\tThe domain GUID\n"));
+ d_printf(_("\tforest_name=<NAME>\t\t\tThe forest name\n"));
+ d_printf(_("\tdomain_is_nt4\t\t\t\tThe domain not AD but NT4\n"));
+ d_printf(_("\tsavefile=<FILENAME>\t\t\tFile to store the ODJ data\n"));
+ d_printf(_("\tprintblob\t\t\t\tPrint the base64 encoded ODJ data on stdout\n"));
+ net_common_flags_usage(c, argc, argv);
+ d_printf(_("Example:\n"));
+ d_printf("\tnet offlinejoin composeodj --realm=<realm> "
+ "--workgroup=<domain> domain_sid=<sid> domain_guid=<guid> "
+ "forest_name=<name> -S <dc name> -I <dc address> "
+ "--password=<password> printblob\n");
+ return -1;
+}
+
+int net_offlinejoin_composeodj(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ NET_API_STATUS status;
+ const char *dns_domain_name = NULL;
+ const char *netbios_domain_name = NULL;
+ const char *machine_account_name = NULL;
+ const char *machine_account_password = NULL;
+ const char *domain_sid_str = NULL;
+ const char *domain_guid_str = NULL;
+ struct dom_sid domain_sid;
+ struct GUID domain_guid;
+ const char *forest_name = NULL;
+ const char *dc_name = NULL;
+ char dc_address[INET6_ADDRSTRLEN] = { 0 };
+ bool domain_is_ad = true;
+ const char *provision_text_data = NULL;
+ const char *savefile = NULL;
+ bool printblob = false;
+ enum credentials_obtained obtained;
+ bool ok;
+ NTSTATUS ntstatus;
+ int i;
+
+ if (c->display_usage || argc < 4) {
+ return net_offlinejoin_composeodj_usage(c, argc, argv);
+ }
+
+ dns_domain_name = cli_credentials_get_realm(creds);
+ netbios_domain_name = cli_credentials_get_domain(creds);
+
+ machine_account_name = cli_credentials_get_username_and_obtained(creds, &obtained);
+ if (obtained < CRED_CALLBACK_RESULT) {
+ const char *netbios_name = cli_credentials_get_workstation(creds);
+ cli_credentials_set_username(
+ creds,
+ talloc_asprintf(c, "%s$", netbios_name),
+ CRED_SPECIFIED);
+ }
+
+ machine_account_name = cli_credentials_get_username(creds);
+ machine_account_password = cli_credentials_get_password(creds);
+ dc_name = c->opt_host;
+
+ if (c->opt_have_ip) {
+ struct sockaddr_in *in4 = NULL;
+ struct sockaddr_in6 *in6 = NULL;
+ const char *p = NULL;
+
+ switch(c->opt_dest_ip.ss_family) {
+ case AF_INET:
+ in4 = (struct sockaddr_in *)&c->opt_dest_ip;
+ p = inet_ntop(AF_INET, &in4->sin_addr, dc_address, sizeof(dc_address));
+ break;
+ case AF_INET6:
+ in6 = (struct sockaddr_in6 *)&c->opt_dest_ip;
+ p = inet_ntop(AF_INET6, &in6->sin6_addr, dc_address, sizeof(dc_address));
+ break;
+ default:
+ d_printf("Unknown IP address family\n");
+ return -1;
+ }
+
+ if (p == NULL) {
+ d_fprintf(stderr, "Failed to parse IP address: %s\n", strerror(errno));
+ return -1;
+ }
+ }
+
+ /* process additional command line args */
+
+ for (i = 0; i < argc; i++) {
+ if (strnequal(argv[i], "domain_sid", strlen("domain_sid"))) {
+ domain_sid_str = get_string_param(argv[i]);
+ if (domain_sid_str == NULL) {
+ return -1;
+ }
+ }
+
+ if (strnequal(argv[i], "domain_guid", strlen("domain_guid"))) {
+ domain_guid_str = get_string_param(argv[i]);
+ if (domain_guid_str == NULL) {
+ return -1;
+ }
+ }
+
+ if (strnequal(argv[i], "forest_name", strlen("forest_name"))) {
+ forest_name = get_string_param(argv[i]);
+ if (forest_name == NULL) {
+ return -1;
+ }
+ }
+
+ if (strnequal(argv[i], "savefile", strlen("savefile"))) {
+ savefile = get_string_param(argv[i]);
+ if (savefile == NULL) {
+ return -1;
+ }
+ }
+
+ if (strnequal(argv[i], "printblob", strlen("printblob"))) {
+ printblob = true;
+ }
+
+ if (strnequal(argv[i], "domain_is_nt4", strlen("domain_is_nt4"))) {
+ domain_is_ad = false;
+ }
+ }
+
+ /* Check command line arguments */
+
+ if (savefile == NULL && !printblob) {
+ d_printf("Choose either save the blob to a file or print it\n");
+ return -1;
+ }
+
+ if (dns_domain_name == NULL) {
+ d_printf("Please provide a valid realm parameter (--realm)\n");
+ return -1;
+ }
+
+ if (netbios_domain_name == NULL) {
+ d_printf("Please provide a valid domain parameter (--workgroup)\n");
+ return -1;
+ }
+
+ if (dc_name == NULL) {
+ d_printf("Please provide a valid DC name parameter (-S)\n");
+ return -1;
+ }
+
+ if (strlen(dc_address) == 0) {
+ d_printf("Please provide a valid domain controller address parameter (-I)\n");
+ return -1;
+ }
+
+ if (machine_account_name == NULL) {
+ d_printf("Please provide a valid netbios name parameter\n");
+ return -1;
+ }
+
+ if (machine_account_password == NULL) {
+ d_printf("Please provide a valid password parameter\n");
+ return -1;
+ }
+
+ if (domain_sid_str == NULL) {
+ d_printf("Please provide a valid <domain_sid> parameter\n");
+ return -1;
+ }
+
+ if (domain_guid_str == NULL) {
+ d_printf("Please provide a valid <domain_guid> parameter\n");
+ return -1;
+ }
+
+ if (forest_name == NULL) {
+ d_printf("Please provide a valid <forest_name> parameter\n");
+ return -1;
+ }
+
+ ok = dom_sid_parse(domain_sid_str, &domain_sid);
+ if (!ok) {
+ d_fprintf(stderr, _("Failed to parse domain SID\n"));
+ return -1;
+ }
+
+ ntstatus = GUID_from_string(domain_guid_str, &domain_guid);
+ if (NT_STATUS_IS_ERR(ntstatus)) {
+ d_fprintf(stderr, _("Failed to parse domain GUID\n"));
+ return -1;
+ }
+
+ status = NetComposeOfflineDomainJoin(dns_domain_name,
+ netbios_domain_name,
+ (struct domsid *)&domain_sid,
+ &domain_guid,
+ forest_name,
+ machine_account_name,
+ machine_account_password,
+ dc_name,
+ dc_address,
+ domain_is_ad,
+ NULL,
+ 0,
+ &provision_text_data);
+ if (status != 0) {
+ d_printf("Failed to compose offline domain join blob: %s\n",
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ return status;
+ }
+
+ if (savefile != NULL) {
+ DATA_BLOB ucs2_blob, blob;
+
+ /*
+ * Windows produces and consumes UTF16/UCS2 encoded blobs
+ * so we also do it for compatibility. Someone may provision an
+ * account for a Windows machine with samba.
+ */
+ ok = push_reg_sz(c, &ucs2_blob, provision_text_data);
+ if (!ok) {
+ return -1;
+ }
+
+ /* Add the unicode BOM mark */
+ blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2);
+ if (blob.data == NULL) {
+ d_printf("Failed to allocate blob: %s\n",
+ strerror(errno));
+ return -1;
+ }
+
+ blob.data[0] = 0xff;
+ blob.data[1] = 0xfe;
+
+ memcpy(blob.data + 2, ucs2_blob.data, ucs2_blob.length);
+
+ ok = file_save(savefile, blob.data, blob.length);
+ if (!ok) {
+ d_printf("Failed to save %s: %s\n", savefile,
+ strerror(errno));
+ return -1;
+ }
+ }
+
+ if (printblob) {
+ printf("%s\n", provision_text_data);
+ }
+
+ return 0;
+}
diff --git a/source3/utils/net_printing.c b/source3/utils/net_printing.c
new file mode 100644
index 0000000..04a3acc
--- /dev/null
+++ b/source3/utils/net_printing.c
@@ -0,0 +1,592 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Local printing tdb migration interface
+
+ Copyright (C) Guenther Deschner 2010
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "rpc_client/cli_pipe.h"
+#include "librpc/gen_ndr/ndr_ntprinting.h"
+#include "librpc/gen_ndr/ndr_spoolss.h"
+#include "../libcli/security/security.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "../librpc/gen_ndr/ndr_winreg.h"
+#include "util_tdb.h"
+#include "printing/nt_printing_migrate.h"
+#include "lib/param/param.h"
+
+#define FORMS_PREFIX "FORMS/"
+#define FORMS_PREFIX_LEN 6
+#define DRIVERS_PREFIX "DRIVERS/"
+#define DRIVERS_PREFIX_LEN 8
+#define PRINTERS_PREFIX "PRINTERS/"
+#define PRINTERS_PREFIX_LEN 9
+#define SECDESC_PREFIX "SECDESC/"
+#define SECDESC_PREFIX_LEN 8
+
+#define ARG_ENCODING "encoding="
+
+struct printing_opts {
+ const char *encoding;
+ const char *tdb;
+};
+
+static NTSTATUS printing_parse_args(TALLOC_CTX *mem_ctx,
+ struct printing_opts **popts,
+ int argc, const char **argv)
+{
+ size_t c;
+ struct printing_opts *o;
+
+ if (argc == 0) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ o = talloc_zero(mem_ctx, struct printing_opts);
+ if (o == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ for (c = 0; c < argc; c++) {
+ if (strnequal(argv[c], ARG_ENCODING, sizeof(ARG_ENCODING) - 1)) {
+ o->encoding = talloc_strdup(o,
+ argv[c] + sizeof(ARG_ENCODING) - 1);
+ if (o->encoding == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ o->tdb = talloc_strdup(o, argv[c]);
+ if (o->tdb == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+
+ *popts = o;
+ return NT_STATUS_OK;
+}
+
+static void dump_form(TALLOC_CTX *mem_ctx,
+ const char *key_name,
+ unsigned char *data,
+ size_t length)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char *s;
+ struct ntprinting_form r;
+
+ printf("found form: %s\n", key_name);
+
+ blob = data_blob_const(data, length);
+
+ ZERO_STRUCT(r);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_ntprinting_form);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("form pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ return;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_form, &r);
+ if (s) {
+ printf("%s\n", s);
+ }
+}
+
+static void dump_driver(TALLOC_CTX *mem_ctx,
+ const char *key_name,
+ unsigned char *data,
+ size_t length,
+ bool do_string_conversion)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char *s;
+ struct ntprinting_driver r;
+
+ printf("found driver: %s\n", key_name);
+
+ blob = data_blob_const(data, length);
+
+ ZERO_STRUCT(r);
+
+ if (do_string_conversion) {
+ r.string_flags = LIBNDR_FLAG_STR_ASCII;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_ntprinting_driver);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("driver pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ return;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_driver, &r);
+ if (s) {
+ printf("%s\n", s);
+ }
+}
+
+static void dump_printer(TALLOC_CTX *mem_ctx,
+ const char *key_name,
+ unsigned char *data,
+ size_t length,
+ bool do_string_conversion)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char *s;
+ struct ntprinting_printer r;
+
+ printf("found printer: %s\n", key_name);
+
+ blob = data_blob_const(data, length);
+
+ ZERO_STRUCT(r);
+
+ if (do_string_conversion) {
+ r.info.string_flags = LIBNDR_FLAG_STR_ASCII;
+ }
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_ntprinting_printer);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("printer pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ return;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_printer, &r);
+ if (s) {
+ printf("%s\n", s);
+ }
+}
+
+static void dump_sd(TALLOC_CTX *mem_ctx,
+ const char *key_name,
+ unsigned char *data,
+ size_t length)
+{
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ char *s;
+ struct sec_desc_buf r;
+
+ printf("found security descriptor: %s\n", key_name);
+
+ blob = data_blob_const(data, length);
+
+ ZERO_STRUCT(r);
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r,
+ (ndr_pull_flags_fn_t)ndr_pull_sec_desc_buf);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_fprintf(stderr, _("security descriptor pull failed: %s\n"),
+ ndr_errstr(ndr_err));
+ return;
+ }
+
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, sec_desc_buf, &r);
+ if (s) {
+ printf("%s\n", s);
+ }
+}
+
+
+static int net_printing_dump(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, newkey, dbuf;
+ struct printing_opts *o;
+ const char *save_dos_charset = lp_dos_charset();
+ bool do_string_conversion = false;
+ NTSTATUS status;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf( "%s\n"
+ "net printing dump [options] <file.tdb>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Dump formatted printer information of the tdb."));
+ d_printf(_("Valid options:\n"));
+ d_printf(_(" encoding=<CP> Set the Code Page of the tdb file.\n"
+ " See iconv -l for the list of CP values\n"
+ " (CP1252 is Western latin1, CP1251 is Cyrillic).\n"));
+ goto done;
+ }
+
+ status = printing_parse_args(ctx, &o, argc, argv);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("failed to parse arguments\n"));
+ goto done;
+ }
+
+ tdb = tdb_open_log(o->tdb, 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (!tdb) {
+ d_fprintf(stderr, _("failed to open tdb file: %s\n"), o->tdb);
+ goto done;
+ }
+
+ if (o->encoding != NULL) {
+ lpcfg_set_cmdline(c->lp_ctx, "dos charset", o->encoding);
+ d_fprintf(stderr, _("do string conversion from %s to %s\n"),
+ lp_dos_charset(), lp_unix_charset());
+ do_string_conversion = true;
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf=newkey)
+ {
+ int cmp;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr) {
+ continue;
+ }
+
+ cmp = strncmp((const char *)kbuf.dptr,
+ FORMS_PREFIX,
+ FORMS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(FORMS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ dump_form(ctx, key_name, dbuf.dptr, dbuf.dsize);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ cmp = strncmp((const char *)kbuf.dptr,
+ DRIVERS_PREFIX,
+ DRIVERS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(DRIVERS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ dump_driver(ctx,
+ key_name,
+ dbuf.dptr,
+ dbuf.dsize,
+ do_string_conversion);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ cmp = strncmp((const char *)kbuf.dptr,
+ PRINTERS_PREFIX,
+ PRINTERS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(PRINTERS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ dump_printer(ctx,
+ key_name,
+ dbuf.dptr,
+ dbuf.dsize,
+ do_string_conversion);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ cmp = strncmp((const char *)kbuf.dptr,
+ SECDESC_PREFIX,
+ SECDESC_PREFIX_LEN);
+ if (cmp == 0) {
+ dump_sd(ctx, (const char *)kbuf.dptr+strlen(SECDESC_PREFIX), dbuf.dptr, dbuf.dsize);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ }
+
+ ret = 0;
+
+ done:
+ lpcfg_set_cmdline(c->lp_ctx, "dos charset", save_dos_charset);
+ talloc_free(ctx);
+ return ret;
+}
+
+static NTSTATUS printing_migrate_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *winreg_pipe,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct printing_opts *o;
+ TALLOC_CTX *tmp_ctx;
+ TDB_CONTEXT *tdb;
+ TDB_DATA kbuf, newkey, dbuf;
+ NTSTATUS status;
+ const char *save_dos_charset = lp_dos_charset();
+ bool do_string_conversion = false;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = printing_parse_args(tmp_ctx, &o, argc, argv);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("failed to parse arguments\n"));
+ goto done;
+ }
+
+ tdb = tdb_open_log(o->tdb, 0, TDB_DEFAULT, O_RDONLY, 0600);
+ if (tdb == NULL) {
+ d_fprintf(stderr, _("failed to open tdb file: %s\n"), o->tdb);
+ status = NT_STATUS_NO_SUCH_FILE;
+ goto done;
+ }
+
+ if (o->encoding != NULL) {
+ lpcfg_set_cmdline(c->lp_ctx, "dos charset", o->encoding);
+ d_fprintf(stderr, _("do string conversion from %s to %s\n"),
+ lp_dos_charset(), lp_unix_charset());
+ do_string_conversion = true;
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf = newkey)
+ {
+ int cmp;
+
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr) {
+ continue;
+ }
+
+ cmp = strncmp((const char *) kbuf.dptr,
+ FORMS_PREFIX,
+ FORMS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(tmp_ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(FORMS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ printing_tdb_migrate_form(tmp_ctx,
+ winreg_pipe,
+ key_name,
+ dbuf.dptr,
+ dbuf.dsize);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ cmp = strncmp((const char *) kbuf.dptr,
+ DRIVERS_PREFIX,
+ DRIVERS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(tmp_ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(DRIVERS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ printing_tdb_migrate_driver(tmp_ctx,
+ winreg_pipe,
+ key_name,
+ dbuf.dptr,
+ dbuf.dsize,
+ do_string_conversion);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+
+ cmp = strncmp((const char *) kbuf.dptr,
+ PRINTERS_PREFIX,
+ PRINTERS_PREFIX_LEN);
+ if (cmp == 0) {
+ char *key_name = NULL;
+ size_t converted_size = 0;
+ bool ok;
+
+ ok = pull_ascii_talloc(tmp_ctx,
+ &key_name,
+ (const char *) kbuf.dptr + strlen(PRINTERS_PREFIX),
+ &converted_size);
+ if (!ok) {
+ continue;
+ }
+
+ printing_tdb_migrate_printer(tmp_ctx,
+ winreg_pipe,
+ key_name,
+ dbuf.dptr,
+ dbuf.dsize,
+ do_string_conversion);
+ TALLOC_FREE(key_name);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+ SAFE_FREE(dbuf.dptr);
+ }
+
+ for (kbuf = tdb_firstkey(tdb);
+ kbuf.dptr;
+ newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf = newkey)
+ {
+ dbuf = tdb_fetch(tdb, kbuf);
+ if (!dbuf.dptr) {
+ continue;
+ }
+
+ if (strncmp((const char *) kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) {
+ printing_tdb_migrate_secdesc(tmp_ctx,
+ winreg_pipe,
+ (const char *) kbuf.dptr + strlen(SECDESC_PREFIX),
+ dbuf.dptr,
+ dbuf.dsize);
+ SAFE_FREE(dbuf.dptr);
+ continue;
+ }
+ SAFE_FREE(dbuf.dptr);
+
+ }
+
+ status = NT_STATUS_OK;
+
+ done:
+ lpcfg_set_cmdline(c->lp_ctx, "dos charset", save_dos_charset);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+static int net_printing_migrate(struct net_context *c,
+ int argc,
+ const char **argv)
+{
+ if (argc < 1 || c->display_usage) {
+ d_printf( "%s\n"
+ "net printing migrate [options] <file.tdb>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate tdb printing files to new storage"));
+ d_printf(_("Valid options:\n"));
+ d_printf(_(" encoding=<CP> Set the Code Page of the tdb file.\n"
+ " See iconv -l for the list of CP values\n"
+ " (CP1252 is Western latin1, CP1251 is Cyrillic).\n"));
+ return 0;
+ }
+
+ return run_rpc_command(c,
+ NULL,
+ &ndr_table_winreg,
+ 0,
+ printing_migrate_internal,
+ argc,
+ argv);
+}
+/**
+ * 'net printing' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_printing(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+
+ struct functable func[] = {
+ {
+ "dump",
+ net_printing_dump,
+ NET_TRANSPORT_LOCAL,
+ N_("Dump printer databases"),
+ N_("net printing dump\n"
+ " Dump tdb printing file")
+ },
+
+ {
+ "migrate",
+ net_printing_migrate,
+ NET_TRANSPORT_LOCAL | NET_TRANSPORT_RPC,
+ N_("Migrate printer databases"),
+ N_("net printing migrate\n"
+ " Migrate tdb printing files to new storage")
+ },
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ ret = net_run_function(c, argc, argv, "net printing", func);
+
+ return ret;
+}
diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h
new file mode 100644
index 0000000..ccfa89a
--- /dev/null
+++ b/source3/utils/net_proto.h
@@ -0,0 +1,488 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * collected prototypes header
+ *
+ * frozen from "make proto" in May 2008
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NET_PROTO_H_
+#define _NET_PROTO_H_
+
+#include "ads.h"
+#include "libads/ads_status.h"
+#include "librpc/gen_ndr/libnet_join.h"
+
+/* The following definitions come from utils/net.c */
+
+enum netr_SchannelType get_sec_channel_type(const char *param);
+
+/* The following definitions come from utils/net_ads.c */
+struct ads_struct;
+ADS_STATUS ads_startup(struct net_context *c,
+ bool only_own_domain,
+ TALLOC_CTX *mem_ctx,
+ struct ads_struct **ads);
+ADS_STATUS ads_startup_nobind(struct net_context *c,
+ bool only_own_domain,
+ TALLOC_CTX *mem_ctx,
+ struct ads_struct **ads);
+int net_ads_check_our_domain(struct net_context *c);
+int net_ads_check(struct net_context *c);
+int net_ads_user(struct net_context *c, int argc, const char **argv);
+int net_ads_group(struct net_context *c, int argc, const char **argv);
+int net_ads_testjoin(struct net_context *c, int argc, const char **argv);
+int net_ads_join(struct net_context *c, int argc, const char **argv);
+int net_ads_printer_usage(struct net_context *c, int argc, const char **argv);
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv);
+int net_ads_keytab(struct net_context *c, int argc, const char **argv);
+int net_ads_kerberos(struct net_context *c, int argc, const char **argv);
+int net_ads_setspn(struct net_context *c, int argc, const char **argv);
+int net_ads(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_ads_join_dns.c */
+void use_in_memory_ccache(void);
+NTSTATUS net_update_dns_ext(struct net_context *c,
+ TALLOC_CTX *mem_ctx, ADS_STRUCT *ads,
+ const char *hostname,
+ struct sockaddr_storage *iplist,
+ int num_addrs, bool remove_host);
+void net_ads_join_dns_updates(struct net_context *c, TALLOC_CTX *ctx, struct libnet_JoinCtx *r);
+
+/* The following definitions come from utils/net_ads_gpo.c */
+
+int net_ads_gpo(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_cache.c */
+
+int net_cache(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_conf.c */
+
+int net_conf(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_dns.c */
+
+int get_my_ip_address( struct sockaddr_storage **pp_ss );
+
+/* The following definitions come from utils/net_dom.c */
+
+int net_dom_usage(struct net_context *c, int argc, const char **argv);
+int net_dom(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_file.c */
+
+int net_file_usage(struct net_context *c, int argc, const char **argv);
+int net_file(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_group.c */
+
+int net_group_usage(struct net_context *c, int argc, const char **argv);
+int net_group(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_groupmap.c */
+
+int net_groupmap_usage(struct net_context *c, int argc, const char **argv);
+int net_groupmap(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_help.c */
+
+int net_help(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_idmap.c */
+
+int net_idmap(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_join.c */
+
+int net_join_usage(struct net_context *c, int argc, const char **argv);
+int net_join(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from source3/utils/net_offlinejoin.c */
+
+int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv);
+int net_offlinejoin(struct net_context *c, int argc, const char **argv);
+int net_offlinejoin_provision(struct net_context *c,
+ int argc, const char **argv);
+int net_offlinejoin_requestodj(struct net_context *c,
+ int argc, const char **argv);
+int net_offlinejoin_composeodj(struct net_context *c,
+ int argc, const char **argv);
+
+/* The following definitions come from utils/net_lookup.c */
+
+int net_lookup_usage(struct net_context *c, int argc, const char **argv);
+int net_lookup(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rap.c */
+
+int net_rap_file_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_file(struct net_context *c, int argc, const char **argv);
+int net_rap_share_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_share(struct net_context *c, int argc, const char **argv);
+int net_rap_session_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_session(struct net_context *c, int argc, const char **argv);
+int net_rap_server_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_server(struct net_context *c, int argc, const char **argv);
+int net_rap_domain_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_domain(struct net_context *c, int argc, const char **argv);
+int net_rap_printq_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_printq(struct net_context *c, int argc, const char **argv);
+int net_rap_user(struct net_context *c, int argc, const char **argv);
+int net_rap_group_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_group(struct net_context *c, int argc, const char **argv);
+int net_rap_groupmember_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_groupmember(struct net_context *c, int argc, const char **argv);
+int net_rap_validate_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_validate(struct net_context *c, int argc, const char **argv);
+int net_rap_service_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_service(struct net_context *c, int argc, const char **argv);
+int net_rap_password_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_password(struct net_context *c, int argc, const char **argv);
+int net_rap_admin_usage(struct net_context *c, int argc, const char **argv);
+int net_rap_admin(struct net_context *c, int argc, const char **argv);
+int net_rap(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_registry.c */
+
+int net_registry(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc.c */
+
+NTSTATUS net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ struct dom_sid **domain_sid,
+ const char **domain_name);
+int run_rpc_command(struct net_context *c,
+ struct cli_state *cli_arg,
+ const struct ndr_interface_table *table,
+ int conn_flags,
+ rpc_command_fn fn,
+ int argc,
+ const char **argv);
+int net_rpc_changetrustpw(struct net_context *c, int argc, const char **argv);
+int net_rpc_testjoin(struct net_context *c, int argc, const char **argv);
+int net_rpc_join(struct net_context *c, int argc, const char **argv);
+NTSTATUS rpc_info_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+int net_rpc_info(struct net_context *c, int argc, const char **argv);
+int net_rpc_getsid(struct net_context *c, int argc, const char **argv);
+int net_rpc_user(struct net_context *c, int argc, const char **argv);
+struct rpc_sh_cmd *net_rpc_user_edit_cmds(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+struct rpc_sh_cmd *net_rpc_user_cmds(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+int net_rpc_group(struct net_context *c, int argc, const char **argv);
+bool copy_top_level_perms(struct net_context *c,
+ struct copy_clistate *cp_clistate,
+ const char *sharename);
+int net_usersidlist(struct net_context *c, int argc, const char **argv);
+int net_usersidlist_usage(struct net_context *c, int argc, const char **argv);
+int net_rpc_share(struct net_context *c, int argc, const char **argv);
+struct rpc_sh_cmd *net_rpc_share_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+int net_rpc_file(struct net_context *c, int argc, const char **argv);
+NTSTATUS rpc_init_shutdown_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_reg_shutdown_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+bool net_rpc_check(struct net_context *c, unsigned flags);
+int rpc_printer_migrate(struct net_context *c, int argc, const char **argv);
+int rpc_printer_usage(struct net_context *c, int argc, const char **argv);
+int net_rpc_printer(struct net_context *c, int argc, const char **argv);
+int net_rpc(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_audit.c */
+
+int net_rpc_audit(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_printer.c */
+
+NTSTATUS net_copy_fileattr(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *src_name, const char *dst_name,
+ bool copy_acls, bool copy_attrs,
+ bool copy_timestamps, bool is_file);
+NTSTATUS net_copy_file(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *src_name, const char *dst_name,
+ bool copy_acls, bool copy_attrs,
+ bool copy_timestamps, bool is_file);
+NTSTATUS rpc_printer_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_driver_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_publish_publish_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_publish_unpublish_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_publish_update_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_publish_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_migrate_security_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_migrate_forms_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_migrate_drivers_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_migrate_printers_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+NTSTATUS rpc_printer_migrate_settings_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv);
+
+/* The following definitions come from utils/net_rpc_registry.c */
+
+int net_rpc_registry(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_rights.c */
+
+int net_rpc_rights(struct net_context *c, int argc, const char **argv);
+struct rpc_sh_cmd *net_rpc_rights_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+
+/* The following definitions come from utils/net_rpc_samsync.c */
+
+int rpc_vampire_usage(struct net_context *c, int argc, const char **argv);
+int rpc_vampire_passdb(struct net_context *c, int argc, const char **argv);
+int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_service.c */
+
+const char *svc_status_string( uint32_t state );
+int net_rpc_service(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_sh_acct.c */
+
+struct rpc_sh_cmd *net_rpc_acct_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx);
+
+/* The following definitions come from utils/net_rpc_shell.c */
+
+int net_rpc_shell(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_sam.c */
+
+int net_sam(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_share.c */
+
+int net_share_usage(struct net_context *c, int argc, const char **argv);
+int net_share(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_status.c */
+
+int net_status_usage(struct net_context *c, int argc, const char **argv);
+int net_status(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_time.c */
+
+int net_time_usage(struct net_context *c, int argc, const char **argv);
+int net_time(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_user.c */
+
+int net_user_usage(struct net_context *c, int argc, const char **argv);
+int net_user(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_usershare.c */
+
+int net_usershare_usage(struct net_context *c, int argc, const char **argv);
+int net_usershare_help(struct net_context *c, int argc, const char **argv);
+int net_usershare(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_eventlog.c */
+
+int net_eventlog(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_printing.c */
+
+int net_printing(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_serverid.c */
+
+int net_serverid(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_util.c */
+
+NTSTATUS net_rpc_lookup_name(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ const char *name, const char **ret_domain,
+ const char **ret_name, struct dom_sid *ret_sid,
+ enum lsa_SidType *ret_type);
+NTSTATUS connect_to_service(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name,
+ const char *service_name,
+ const char *service_type);
+NTSTATUS connect_to_ipc(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name);
+NTSTATUS connect_to_ipc_anonymous(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name);
+NTSTATUS connect_dst_pipe(struct net_context *c, struct cli_state **cli_dst,
+ struct rpc_pipe_client **pp_pipe_hnd,
+ const struct ndr_interface_table *table);
+int net_use_krb_machine_account(struct net_context *c);
+bool net_find_server(struct net_context *c,
+ const char *domain,
+ unsigned flags,
+ struct sockaddr_storage *server_ss,
+ char **server_name);
+bool net_find_pdc(struct sockaddr_storage *server_ss,
+ fstring server_name,
+ const char *domain_name);
+NTSTATUS net_make_ipc_connection(struct net_context *c, unsigned flags,
+ struct cli_state **pcli);
+NTSTATUS net_make_ipc_connection_ex(struct net_context *c ,const char *domain,
+ const char *server,
+ const struct sockaddr_storage *pss,
+ unsigned flags, struct cli_state **pcli);
+const char *net_prompt_pass(struct net_context *c, const char *user);
+int net_run_function(struct net_context *c, int argc, const char **argv,
+ const char *whoami, struct functable *table);
+void net_display_usage_from_functable(struct functable *table);
+
+void net_warn_member_options(void);
+
+const char *net_share_type_str(int num_type);
+
+NTSTATUS net_scan_dc(struct net_context *c,
+ struct cli_state *cli,
+ struct net_dc_info *dc_info);
+
+/* The following definitions come from utils/netlookup.c */
+
+NTSTATUS net_lookup_name_from_sid(struct net_context *c,
+ TALLOC_CTX *ctx,
+ struct dom_sid *psid,
+ const char **ppdomain,
+ const char **ppname);
+NTSTATUS net_lookup_sid_from_name(struct net_context *c, TALLOC_CTX *ctx,
+ const char *full_name, struct dom_sid *pret_sid);
+
+/* The following definitions come from utils/net_g_lock.c */
+int net_g_lock(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_trust.c */
+int net_rpc_trust(struct net_context *c, int argc, const char **argv);
+
+/* The following definitions come from utils/net_rpc_conf.c */
+int net_rpc_conf(struct net_context *c, int argc, const char **argv);
+
+int net_notify(struct net_context *c, int argc, const char **argv);
+
+int net_tdb(struct net_context *c, int argc, const char **argv);
+
+int net_vfs(struct net_context *c, int argc, const char **argv);
+
+int net_witness(struct net_context *c, int argc, const char **argv);
+
+#endif /* _NET_PROTO_H_ */
diff --git a/source3/utils/net_rap.c b/source3/utils/net_rap.c
new file mode 100644
index 0000000..9818623
--- /dev/null
+++ b/source3/utils/net_rap.c
@@ -0,0 +1,1386 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Steve French (sfrench@us.ibm.com)
+ Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2001 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+
+ Originally written by Steve and Jim. Largely rewritten by tridge in
+ November 2001.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "../librpc/gen_ndr/svcctl.h"
+#include "utils/net.h"
+#include "libsmb/libsmb.h"
+#include "clirap2.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/* The following messages were for error checking that is not properly
+ reported at the moment. Which should be reinstated? */
+#define ERRMSG_TARGET_WG_NOT_VALID "\nTarget workgroup option not valid "\
+ "except on net rap server command, ignored"
+#define ERRMSG_INVALID_HELP_OPTION "\nInvalid help option\n"
+
+#define ERRMSG_BOTH_SERVER_IPADDRESS "\nTarget server and IP address both "\
+ "specified. Do not set both at the same time. The target IP address was used\n"
+
+static int errmsg_not_implemented(void)
+{
+ d_printf(_("\nNot implemented\n"));
+ return 0;
+}
+
+int net_rap_file_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_file_usage(c, argc, argv);
+}
+
+/***************************************************************************
+ list info on an open file
+***************************************************************************/
+static void file_fn(const char * pPath, const char * pUser, uint16_t perms,
+ uint16_t locks, uint32_t id)
+{
+ d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n",
+ id, pUser, perms, locks, pPath);
+}
+
+static void one_file_fn(const char *pPath, const char *pUser, uint16_t perms,
+ uint16_t locks, uint32_t id)
+{
+ d_printf(_("File ID %d\n"
+ "User name %s\n"
+ "Locks 0x%-4.2x\n"
+ "Path %s\n"
+ "Permissions 0x%x\n"),
+ id, pUser, locks, pPath, perms);
+}
+
+
+static int rap_file_close(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0 || c->display_usage) {
+ return net_rap_file_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetFileClose(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_info(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0 || c->display_usage)
+ return net_rap_file_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_file_user(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage)
+ return net_rap_file_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ /* list open files */
+
+ d_printf(_("\nEnumerating open files on remote server:\n\n"
+ "\nFileId Opened by Perms Locks Path \n"
+ "------ --------- ----- ----- ---- \n"));
+ ret = cli_NetFileEnum(cli, argv[0], NULL, file_fn);
+
+ if (ret == -1)
+ d_printf(_("\nOperation not supported by server!\n\n"));
+
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_file(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "close",
+ rap_file_close,
+ NET_TRANSPORT_RAP,
+ N_("Close specified file on server"),
+ N_("net rap file close\n"
+ " Close specified file on server")
+ },
+ {
+ "user",
+ rap_file_user,
+ NET_TRANSPORT_RAP,
+ N_("List all files opened by username"),
+ N_("net rap file user\n"
+ " List all files opened by username")
+ },
+ {
+ "info",
+ rap_file_info,
+ NET_TRANSPORT_RAP,
+ N_("Display info about an opened file"),
+ N_("net rap file info\n"
+ " Display info about an opened file")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap file\n"
+ " List all open files on rempte "
+ "server\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ /* list open files */
+
+ d_printf(_("\nEnumerating open files on remote server:\n\n"
+ "\nFileId Opened by Perms Locks Path\n"
+ "------ --------- ----- ----- ----\n"));
+ ret = cli_NetFileEnum(cli, NULL, NULL, file_fn);
+
+ if (ret == -1)
+ d_printf(_("\nOperation not supported by server!\n\n"));
+
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap file", func);
+}
+
+int net_rap_share_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_share_usage(c, argc, argv);
+}
+
+static void long_share_fn(const char *share_name, uint32_t type,
+ const char *comment, void *state)
+{
+ d_printf("%-12s %-8.8s %-50s\n",
+ share_name, net_share_type_str(type), comment);
+}
+
+static void share_fn(const char *share_name, uint32_t type,
+ const char *comment, void *state)
+{
+ d_printf("%s\n", share_name);
+}
+
+static int rap_share_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage) {
+ return net_rap_share_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetShareDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_share_add(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ struct rap_share_info_2 sinfo;
+ char *p;
+ char *sharename;
+
+ if (argc == 0 || c->display_usage) {
+ return net_rap_share_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ sharename = SMB_STRDUP(argv[0]);
+ p = strchr(sharename, '=');
+ if (p == NULL) {
+ d_printf(_("Server path not specified\n"));
+ SAFE_FREE(sharename);
+ return net_rap_share_usage(c, argc, argv);
+ }
+ *p = 0;
+ strlcpy((char *)sinfo.share_name, sharename, sizeof(sinfo.share_name));
+ sinfo.reserved1 = '\0';
+ sinfo.share_type = 0;
+ sinfo.comment = c->opt_comment ? smb_xstrdup(c->opt_comment) : "";
+ sinfo.perms = 0;
+ sinfo.maximum_users = c->opt_maxusers;
+ sinfo.active_users = 0;
+ sinfo.path = p+1;
+ memset(sinfo.password, '\0', sizeof(sinfo.password));
+ sinfo.reserved2 = '\0';
+
+ ret = cli_NetShareAdd(cli, &sinfo);
+ cli_shutdown(cli);
+ SAFE_FREE(sharename);
+ return ret;
+}
+
+
+int net_rap_share(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "delete",
+ rap_share_delete,
+ NET_TRANSPORT_RAP,
+ N_("Delete a share from server"),
+ N_("net rap share delete\n"
+ " Delete a share from server")
+ },
+ {
+ "close",
+ rap_share_delete,
+ NET_TRANSPORT_RAP,
+ N_("Delete a share from server"),
+ N_("net rap share close\n"
+ " Delete a share from server\n"
+ " Alias for net rap share delete")
+ },
+ {
+ "add",
+ rap_share_add,
+ NET_TRANSPORT_RAP,
+ N_("Add a share to server"),
+ N_("net rap share add\n"
+ " Add a share to server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap share\n"
+ " List all shares on remote server\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ if (c->opt_long_list_entries) {
+ d_printf(_(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"
+ "\nShare name Type Description\n"
+ "---------- ---- -----------\n"));
+ ret = cli_RNetShareEnum(cli, long_share_fn, NULL);
+ } else {
+ ret = cli_RNetShareEnum(cli, share_fn, NULL);
+ }
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap share", func);
+}
+
+int net_rap_session_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "\nnet rap session [misc. options] [targets]"
+ "\n\tenumerates all active SMB/CIFS sessions on target server\n"));
+ d_printf(_(
+ "\nnet rap session DELETE <client_name> [misc. options] [targets] \n"
+ "\tor"
+ "\nnet rap session CLOSE <client_name> [misc. options] [targets]"
+ "\n\tDeletes (closes) a session from specified client to server\n"));
+ d_printf(_(
+ "\nnet rap session INFO <client_name>"
+ "\n\tEnumerates all open files in specified session\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+static void list_sessions_func(char *wsname, char *username, uint16_t conns,
+ uint16_t opens, uint16_t users, uint32_t sess_time,
+ uint32_t idle_time, uint32_t user_flags, char *clitype)
+{
+ int hrs = idle_time / 3600;
+ int min = (idle_time / 60) % 60;
+ int sec = idle_time % 60;
+
+ d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n",
+ wsname, username, clitype, opens, hrs, min, sec);
+}
+
+static void display_session_func(const char *wsname, const char *username,
+ uint16_t conns, uint16_t opens, uint16_t users,
+ uint32_t sess_time, uint32_t idle_time,
+ uint32_t user_flags, const char *clitype)
+{
+ int ihrs = idle_time / 3600;
+ int imin = (idle_time / 60) % 60;
+ int isec = idle_time % 60;
+ int shrs = sess_time / 3600;
+ int smin = (sess_time / 60) % 60;
+ int ssec = sess_time % 60;
+ d_printf(_("User name %-20.20s\n"
+ "Computer %-20.20s\n"
+ "Guest logon %-20.20s\n"
+ "Client Type %-40.40s\n"
+ "Sess time %2.2d:%2.2d:%2.2d\n"
+ "Idle time %2.2d:%2.2d:%2.2d\n"),
+ username, wsname,
+ (user_flags&0x0)?_("yes"):_("no"), clitype,
+ shrs, smin, ssec, ihrs, imin, isec);
+}
+
+static void display_conns_func(uint16_t conn_id, uint16_t conn_type, uint16_t opens,
+ uint16_t users, uint32_t conn_time,
+ const char *username, const char *netname)
+{
+ d_printf("%-14.14s %-8.8s %5d\n",
+ netname, net_share_type_str(conn_type), opens);
+}
+
+static int rap_session_info(struct net_context *c, int argc, const char **argv)
+{
+ const char *sessname;
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage)
+ return net_rap_session_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ sessname = argv[0];
+
+ ret = cli_NetSessionGetInfo(cli, sessname, display_session_func);
+ if (ret < 0) {
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ d_printf(_("Share name Type # Opens\n-------------------------"
+ "-----------------------------------------------------\n"));
+ ret = cli_NetConnectionEnum(cli, sessname, display_conns_func);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_session_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage)
+ return net_rap_session_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetSessionDel(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_session(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "info",
+ rap_session_info,
+ NET_TRANSPORT_RAP,
+ N_("Display information about session"),
+ N_("net rap session info\n"
+ " Display information about session")
+ },
+ {
+ "delete",
+ rap_session_delete,
+ NET_TRANSPORT_RAP,
+ N_("Close specified session"),
+ N_("net rap session delete\n"
+ " Close specified session\n"
+ " Alias for net rap session close")
+ },
+ {
+ "close",
+ rap_session_delete,
+ NET_TRANSPORT_RAP,
+ N_("Close specified session"),
+ N_("net rap session close\n"
+ " Close specified session")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap session\n"
+ " List all open sessions on remote "
+ "server\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ d_printf(_("Computer User name "
+ "Client Type Opens Idle time\n"
+ "------------------------------------------"
+ "------------------------------------\n"));
+ ret = cli_NetSessionEnum(cli, list_sessions_func);
+
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap session", func);
+}
+
+/****************************************************************************
+list a server name
+****************************************************************************/
+static void display_server_func(const char *name, uint32_t m,
+ const char *comment, void * reserved)
+{
+ d_printf("\t%-16.16s %s\n", name, comment);
+}
+
+static int net_rap_server_name(struct net_context *c, int argc, const char *argv[])
+{
+ struct cli_state *cli;
+ char *name;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rap server name\n"
+ " Get the name of the server\n"));
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ if (!cli_get_server_name(NULL, cli, &name)) {
+ d_fprintf(stderr, _("cli_get_server_name failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ d_printf(_("Server name = %s\n"), name);
+
+ TALLOC_FREE(name);
+ cli_shutdown(cli);
+ return 0;
+}
+
+static int net_rap_server_domain(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rap server domain\n"
+ " Enumerate servers in this domain/workgroup\n"));
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ d_printf(_("\nEnumerating servers in this domain or workgroup: \n\n"
+ "\tServer name Server description\n"
+ "\t------------- ----------------------------\n"));
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_server(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "name",
+ net_rap_server_name,
+ NET_TRANSPORT_RAP,
+ N_("Get the name of the server"),
+ N_("net rap server name\n"
+ " Get the name of the server")
+ },
+ {
+ "domain",
+ net_rap_server_domain,
+ NET_TRANSPORT_RAP,
+ N_("Get the servers in this domain/workgroup"),
+ N_("net rap server domain\n"
+ " Get the servers in this domain/workgroup")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ /* smb4k uses 'net [rap|rpc] server domain' to query servers in a domain */
+ /* Fall through for 'domain', any other forms will cause to show usage message */
+ return net_run_function(c, argc, argv, "net rap server", func);
+
+}
+
+int net_rap_domain_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rap domain [misc. options] [target]\n\tlists the"
+ " domains or workgroups visible on the current network\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_rap_domain(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (c->display_usage)
+ return net_rap_domain_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ d_printf(_("\nEnumerating domains:\n\n"
+ "\tDomain name Server name of Browse Master\n"
+ "\t------------- ----------------------------\n"));
+
+ ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM,
+ display_server_func,NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_printq_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net rap printq [misc. options] [targets]\n"
+ "\tor\n"
+ "net rap printq info [<queue_name>] [misc. options] [targets]\n"
+ "\tlists the specified queue and jobs on the target server.\n"
+ "\tIf the queue name is not specified, all queues are listed.\n\n"));
+ d_printf(_(
+ "net rap printq delete [<queue name>] [misc. options] [targets]\n"
+ "\tdeletes the specified job number on the target server, or the\n"
+ "\tprinter queue if no job number is specified\n"));
+
+ net_common_flags_usage(c, argc, argv);
+
+ return -1;
+}
+
+static void enum_queue(const char *queuename, uint16_t pri, uint16_t start,
+ uint16_t until, const char *sep, const char *pproc,
+ const char *dest, const char *qparms,
+ const char *qcomment, uint16_t status, uint16_t jobcount)
+{
+ d_printf(_("%-17.17s Queue %5d jobs "),
+ queuename, jobcount);
+
+ switch (status) {
+ case 0:
+ d_printf(_("*Printer Active*\n"));
+ break;
+ case 1:
+ d_printf(_("*Printer Paused*\n"));
+ break;
+ case 2:
+ d_printf(_("*Printer error*\n"));
+ break;
+ case 3:
+ d_printf(_("*Delete Pending*\n"));
+ break;
+ default:
+ d_printf(_("**UNKNOWN STATUS**\n"));
+ }
+}
+
+static void enum_jobs(uint16_t jobid, const char *ownername,
+ const char *notifyname, const char *datatype,
+ const char *jparms, uint16_t pos, uint16_t status,
+ const char *jstatus, unsigned int submitted, unsigned int jobsize,
+ const char *comment)
+{
+ d_printf(" %-23.23s %5d %9d ",
+ ownername, jobid, jobsize);
+ switch (status) {
+ case 0:
+ d_printf(_("Waiting\n"));
+ break;
+ case 1:
+ d_printf(_("Held in queue\n"));
+ break;
+ case 2:
+ d_printf(_("Spooling\n"));
+ break;
+ case 3:
+ d_printf(_("Printing\n"));
+ break;
+ default:
+ d_printf(_("**UNKNOWN STATUS**\n"));
+ }
+}
+
+#define PRINTQ_ENUM_DISPLAY \
+ _("Print queues at \\\\%s\n\n"\
+ "Name Job # Size Status\n\n"\
+ "------------------------------------------------------------------"\
+ "-------------\n")
+
+static int rap_printq_info(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage)
+ return net_rap_printq_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, smbXcli_conn_remote_name(cli->conn)); /* list header */
+ ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_printq_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ NTSTATUS status;
+
+ if (argc == 0 || c->display_usage)
+ return net_rap_printq_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ status = cli_printjob_del(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+ return 0;
+}
+
+int net_rap_printq(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ struct functable func[] = {
+ {
+ "info",
+ rap_printq_info,
+ NET_TRANSPORT_RAP,
+ N_("Display info about print queues and jobs"),
+ N_("net rap printq info [queue]\n"
+ " Display info about print jobs in queue.\n"
+ " If queue is not specified, all queues are "
+ "listed")
+ },
+ {
+ "delete",
+ rap_printq_delete,
+ NET_TRANSPORT_RAP,
+ N_("Delete print job(s)"),
+ N_("net rap printq delete\n"
+ " Delete print job(s)")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap printq\n"
+ " List the print queue\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ d_printf(PRINTQ_ENUM_DISPLAY, smbXcli_conn_remote_name(cli->conn)); /* list header */
+ ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap printq", func);
+}
+
+static int net_rap_user_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_user_usage(c, argc, argv);
+}
+
+static void user_fn(const char *user_name, void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static void long_user_fn(const char *user_name, const char *comment,
+ const char * home_dir, const char * logon_script,
+ void *state)
+{
+ d_printf("%-21.21s %s\n",
+ user_name, comment);
+}
+
+static void group_member_fn(const char *user_name, void *state)
+{
+ d_printf("%-21.21s\n", user_name);
+}
+
+static int rap_user_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc == 0 || c->display_usage) {
+ return net_rap_user_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetUserDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_add(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ struct rap_user_info_1 userinfo;
+
+ if (argc == 0 || c->display_usage) {
+ return net_rap_user_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ strlcpy((char *)userinfo.user_name, argv[0], sizeof(userinfo.user_name));
+ if (c->opt_flags == 0)
+ c->opt_flags = 0x21;
+
+ userinfo.userflags = c->opt_flags;
+ userinfo.reserved1 = '\0';
+ userinfo.comment = smb_xstrdup(c->opt_comment ? c->opt_comment : "");
+ userinfo.priv = 1;
+ userinfo.home_dir = NULL;
+ userinfo.logon_script = NULL;
+ userinfo.passwrd[0] = '\0';
+
+ ret = cli_NetUserAdd(cli, &userinfo);
+
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_user_info(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0 || c->display_usage) {
+ return net_rap_user_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_user(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+ struct functable func[] = {
+ {
+ "add",
+ rap_user_add,
+ NET_TRANSPORT_RAP,
+ N_("Add specified user"),
+ N_("net rap user add\n"
+ " Add specified user")
+ },
+ {
+ "info",
+ rap_user_info,
+ NET_TRANSPORT_RAP,
+ N_("List domain groups of specified user"),
+ N_("net rap user info\n"
+ " List domain groups of specified user")
+
+ },
+ {
+ "delete",
+ rap_user_delete,
+ NET_TRANSPORT_RAP,
+ N_("Remove specified user"),
+ N_("net rap user delete\n"
+ " Remove specified user")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap user\n"
+ " List all users\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ goto done;
+ if (c->opt_long_list_entries) {
+ d_printf(_("\nUser name Comment"
+ "\n-----------------------------\n"));
+ ret = cli_RNetUserEnum(cli, long_user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+ ret = cli_RNetUserEnum0(cli, user_fn, NULL);
+ cli_shutdown(cli);
+ goto done;
+ }
+
+ ret = net_run_function(c, argc, argv, "net rap user", func);
+ done:
+ if (ret != 0) {
+ DEBUG(1, (_("Net user returned: %d\n"), ret));
+ }
+ return ret;
+}
+
+
+int net_rap_group_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_group_usage(c, argc, argv);
+}
+
+static void long_group_fn(const char *group_name, const char *comment,
+ void *state)
+{
+ d_printf("%-21.21s %s\n", group_name, comment);
+}
+
+static void group_fn(const char *group_name, void *state)
+{
+ d_printf("%-21.21s\n", group_name);
+}
+
+static int rap_group_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0 || c->display_usage) {
+ return net_rap_group_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetGroupDelete(cli, argv[0]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_group_add(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ struct rap_group_info_1 grinfo;
+
+ if (argc == 0 || c->display_usage) {
+ return net_rap_group_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ /* BB check for length 21 or smaller explicitly ? BB */
+ strlcpy((char *)grinfo.group_name, argv[0], sizeof(grinfo.group_name));
+ grinfo.reserved1 = '\0';
+ grinfo.comment = smb_xstrdup(c->opt_comment ? c->opt_comment : "");
+
+ ret = cli_NetGroupAdd(cli, &grinfo);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_group(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ rap_group_add,
+ NET_TRANSPORT_RAP,
+ N_("Add specified group"),
+ N_("net rap group add\n"
+ " Add specified group")
+ },
+ {
+ "delete",
+ rap_group_delete,
+ NET_TRANSPORT_RAP,
+ N_("Delete specified group"),
+ N_("net rap group delete\n"
+ " Delete specified group")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap group\n"
+ " List all groups\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+ if (c->opt_long_list_entries) {
+ d_printf(_("Group name Comment\n"
+ "-----------------------------\n"));
+ ret = cli_RNetGroupEnum(cli, long_group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+ ret = cli_RNetGroupEnum0(cli, group_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap group", func);
+}
+
+int net_rap_groupmember_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net rap groupmember LIST <group> [misc. options] [targets]"
+ "\n\t Enumerate users in a group\n"
+ "\nnet rap groupmember DELETE <group> <user> [misc. options] "
+ "[targets]\n\t Delete specified user from specified group\n"
+ "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]"
+ "\n\t Add specified user to specified group\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+
+static int rap_groupmember_add(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2 || c->display_usage) {
+ return net_rap_groupmember_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetGroupAddUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_delete(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc != 2 || c->display_usage) {
+ return net_rap_groupmember_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetGroupDelUser(cli, argv[0], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+static int rap_groupmember_list(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+ if (argc == 0 || c->display_usage) {
+ return net_rap_groupmember_usage(c, argc, argv);
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL );
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_groupmember(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ rap_groupmember_add,
+ NET_TRANSPORT_RAP,
+ N_("Add specified user to group"),
+ N_("net rap groupmember add\n"
+ " Add specified user to group")
+ },
+ {
+ "list",
+ rap_groupmember_list,
+ NET_TRANSPORT_RAP,
+ N_("List users in group"),
+ N_("net rap groupmember list\n"
+ " List users in group")
+ },
+ {
+ "delete",
+ rap_groupmember_delete,
+ NET_TRANSPORT_RAP,
+ N_("Remove user from group"),
+ N_("net rap groupmember delete\n"
+ " Remove user from group")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rap groupmember", func);
+}
+
+int net_rap_validate_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rap validate <username> [password]\n"
+ "\tValidate user and password to check whether they"
+ " can access target server or domain\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+int net_rap_validate(struct net_context *c, int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+int net_rap_service_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rap service [misc. options] [targets] \n"
+ "\tlists all running service daemons on target server\n"));
+ d_printf(_("\nnet rap service START <name> [service startup arguments]"
+ " [misc. options] [targets]"
+ "\n\tStart named service on remote server\n"));
+ d_printf(_("\nnet rap service STOP <name> [misc. options] [targets]\n"
+ "\n\tStop named service on remote server\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+static int rap_service_start(struct net_context *c, int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+static int rap_service_stop(struct net_context *c, int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+static void service_fn(const char *service_name, const char *dummy,
+ void *state)
+{
+ d_printf("%-21.21s\n", service_name);
+}
+
+int net_rap_service(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "start",
+ rap_service_start,
+ NET_TRANSPORT_RAP,
+ N_("Start service on remote server"),
+ N_("net rap service start\n"
+ " Start service on remote server")
+ },
+ {
+ "stop",
+ rap_service_stop,
+ NET_TRANSPORT_RAP,
+ N_("Stop named serve on remote server"),
+ N_("net rap service stop\n"
+ " Stop named serve on remote server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ struct cli_state *cli;
+ int ret;
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rap service\n"
+ " List services on remote server\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ if (c->opt_long_list_entries) {
+ d_printf(_("Service name Comment\n"
+ "-----------------------------\n"));
+ ret = cli_RNetServiceEnum(cli, long_group_fn, NULL);
+ if (ret) {
+ cli_shutdown(cli);
+ return ret;
+ }
+ }
+ ret = cli_RNetServiceEnum(cli, service_fn, NULL);
+ cli_shutdown(cli);
+ return ret;
+ }
+
+ return net_run_function(c, argc, argv, "net rap service", func);
+}
+
+int net_rap_password_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n"
+ "\tchanges the password for the specified user at target\n"));
+
+ return -1;
+}
+
+
+int net_rap_password(struct net_context *c, int argc, const char **argv)
+{
+ struct cli_state *cli;
+ int ret;
+
+ if (argc < 3 || c->display_usage)
+ return net_rap_password_usage(c, argc, argv);
+
+ if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli)))
+ return -1;
+
+ /* BB Add check for password lengths? */
+ ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]);
+ cli_shutdown(cli);
+ return ret;
+}
+
+int net_rap_admin_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]"
+ "\n\texecutes a remote command on an os/2 target server\n"));
+
+ return -1;
+}
+
+
+int net_rap_admin(struct net_context *c, int argc, const char **argv)
+{
+ return errmsg_not_implemented();
+}
+
+/* Entry-point for all the RAP functions. */
+
+int net_rap(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "file",
+ net_rap_file,
+ NET_TRANSPORT_RAP,
+ N_("List open files"),
+ N_("net rap file\n"
+ " List open files")
+ },
+ {
+ "share",
+ net_rap_share,
+ NET_TRANSPORT_RAP,
+ N_("List shares exported by server"),
+ N_("net rap share\n"
+ " List shares exported by server")
+ },
+ {
+ "session",
+ net_rap_session,
+ NET_TRANSPORT_RAP,
+ N_("List open sessions"),
+ N_("net rap session\n"
+ " List open sessions")
+ },
+ {
+ "server",
+ net_rap_server,
+ NET_TRANSPORT_RAP,
+ N_("List servers in workgroup"),
+ N_("net rap server\n"
+ " List servers in domain/workgroup")
+ },
+ {
+ "domain",
+ net_rap_domain,
+ NET_TRANSPORT_RAP,
+ N_("List domains in network"),
+ N_("net rap domain\n"
+ " List domains in network")
+ },
+ {
+ "printq",
+ net_rap_printq,
+ NET_TRANSPORT_RAP,
+ N_("List printer queues on server"),
+ N_("net rap printq\n"
+ " List printer queues on server")
+ },
+ {
+ "user",
+ net_rap_user,
+ NET_TRANSPORT_RAP,
+ N_("List users"),
+ N_("net rap user\n"
+ " List users")
+ },
+ {
+ "group",
+ net_rap_group,
+ NET_TRANSPORT_RAP,
+ N_("List user groups"),
+ N_("net rap group\n"
+ " List user groups")
+ },
+ {
+ "validate",
+ net_rap_validate,
+ NET_TRANSPORT_RAP,
+ N_("Check username/password"),
+ N_("net rap validate\n"
+ " Check username/password")
+ },
+ {
+ "groupmember",
+ net_rap_groupmember,
+ NET_TRANSPORT_RAP,
+ N_("List/modify group memberships"),
+ N_("net rap groupmember\n"
+ " List/modify group memberships")
+ },
+ {
+ "admin",
+ net_rap_admin,
+ NET_TRANSPORT_RAP,
+ N_("Execute commands on remote OS/2"),
+ N_("net rap admin\n"
+ " Execute commands on remote OS/2")
+ },
+ {
+ "service",
+ net_rap_service,
+ NET_TRANSPORT_RAP,
+ N_("Start/stop remote service"),
+ N_("net rap service\n"
+ " Start/stop remote service")
+ },
+ {
+ "password",
+ net_rap_password,
+ NET_TRANSPORT_RAP,
+ N_("Change user password"),
+ N_("net rap password\n"
+ " Change user password")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rap", func);
+}
+
diff --git a/source3/utils/net_registry.c b/source3/utils/net_registry.c
new file mode 100644
index 0000000..5d1314e
--- /dev/null
+++ b/source3/utils/net_registry.c
@@ -0,0 +1,1732 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Local registry interface
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "registry/reg_api.h"
+#include "registry/reg_util_token.h"
+#include "registry/reg_init_basic.h"
+#include "utils/net.h"
+#include "utils/net_registry_util.h"
+#include "include/g_lock.h"
+#include "registry/reg_backend_db.h"
+#include "registry/reg_import.h"
+#include "registry/reg_format.h"
+#include "registry/reg_api_util.h"
+#include <assert.h>
+#include "../libcli/security/display_sec.h"
+#include "../libcli/security/sddl.h"
+#include "../libcli/registry/util_reg.h"
+#include "passdb/machine_sid.h"
+#include "net_registry_check.h"
+#include "lib/util/util_tdb.h"
+#include "lib/util/smb_strtox.h"
+
+/*
+ *
+ * Helper functions
+ *
+ */
+
+/**
+ * split given path into hive and remaining path and open the hive key
+ */
+static WERROR open_hive(TALLOC_CTX *ctx, const char *path,
+ uint32_t desired_access,
+ struct registry_key **hive,
+ char **subkeyname)
+{
+ WERROR werr;
+ struct security_token *token = NULL;
+ char *hivename = NULL;
+ char *tmp_subkeyname = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ if ((hive == NULL) || (subkeyname == NULL)) {
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ werr = split_hive_key(tmp_ctx, path, &hivename, &tmp_subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ *subkeyname = talloc_strdup(ctx, tmp_subkeyname);
+ if (*subkeyname == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ werr = ntstatus_to_werror(registry_create_admin_token(tmp_ctx, &token));
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ werr = reg_openhive(ctx, hivename, desired_access, token, hive);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return werr;
+}
+
+static WERROR open_key(TALLOC_CTX *ctx, const char *path,
+ uint32_t desired_access,
+ struct registry_key **key)
+{
+ WERROR werr;
+ char *subkey_name = NULL;
+ struct registry_key *hive = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ if ((path == NULL) || (key == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = open_hive(tmp_ctx, path, desired_access, &hive, &subkey_name);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_hive failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_openkey(ctx, hive, subkey_name, desired_access, key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_openkey failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return werr;
+}
+
+static WERROR registry_enumkey(struct registry_key *parent, const char *keyname,
+ bool recursive)
+{
+ WERROR werr;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ char *subkey_name;
+ NTTIME modtime;
+ uint32_t count;
+ char *valname = NULL;
+ struct registry_value *valvalue = NULL;
+ struct registry_key *key = NULL;
+
+ werr = reg_openkey(ctx, parent, keyname, REG_KEY_READ, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ if (recursive) {
+ printf("[%s]\n\n", key->key->name);
+ } else {
+ for (count = 0;
+ werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime),
+ W_ERROR_IS_OK(werr);
+ count++)
+ {
+ print_registry_key(subkey_name, &modtime);
+ }
+ if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+ goto done;
+ }
+ }
+
+ for (count = 0;
+ werr = reg_enumvalue(ctx, key, count, &valname, &valvalue),
+ W_ERROR_IS_OK(werr);
+ count++)
+ {
+ print_registry_value_with_name(valname, valvalue);
+ }
+ if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+ goto done;
+ }
+
+ if (!recursive) {
+ werr = WERR_OK;
+ goto done;
+ }
+
+ for (count = 0;
+ werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime),
+ W_ERROR_IS_OK(werr);
+ count++)
+ {
+ werr = registry_enumkey(key, subkey_name, recursive);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+ if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(ctx);
+ return werr;
+}
+
+
+
+/*
+ *
+ * the main "net registry" function implementations
+ *
+ */
+static int net_registry_enumerate(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ struct registry_key *key = NULL;
+ char *name = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry enumerate <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry enumerate 'HKLM\\Software\\Samba'\n"));
+ goto done;
+ }
+
+ werr = open_hive(ctx, argv[0], REG_KEY_READ, &key, &name);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ werr = registry_enumkey(key, name, c->opt_reboot);
+ if (W_ERROR_IS_OK(werr)) {
+ ret = 0;
+ }
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_enumerate_recursive(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ struct registry_key *key = NULL;
+ char *name = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry enumerate <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry enumerate 'HKLM\\Software\\Samba'\n"));
+ goto done;
+ }
+
+ werr = open_hive(ctx, argv[0], REG_KEY_READ, &key, &name);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ werr = registry_enumkey(key, name, true);
+ if (W_ERROR_IS_OK(werr)) {
+ ret = 0;
+ }
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+
+static int net_registry_createkey(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ enum winreg_CreateAction action;
+ char *subkeyname = NULL;
+ struct registry_key *hivekey = NULL;
+ struct registry_key *subkey = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry createkey <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry createkey "
+ "'HKLM\\Software\\Samba\\smbconf.127.0.0.1'\n"));
+ goto done;
+ }
+ if (strlen(argv[0]) == 0) {
+ d_fprintf(stderr, _("error: zero length key name given\n"));
+ goto done;
+ }
+
+ werr = open_hive(ctx, argv[0], REG_KEY_WRITE, &hivekey, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_hive failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_createkey(ctx, hivekey, subkeyname, REG_KEY_WRITE,
+ &subkey, &action);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_createkey failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+ switch (action) {
+ case REG_ACTION_NONE:
+ d_printf(_("createkey did nothing -- huh?\n"));
+ break;
+ case REG_CREATED_NEW_KEY:
+ d_printf(_("createkey created %s\n"), argv[0]);
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ d_printf(_("createkey opened existing %s\n"), argv[0]);
+ break;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_deletekey_internal(struct net_context *c, int argc,
+ const char **argv,
+ bool recursive)
+{
+ WERROR werr;
+ char *subkeyname = NULL;
+ struct registry_key *hivekey = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry deletekey <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry deletekey "
+ "'HKLM\\Software\\Samba\\smbconf.127.0.0.1'\n"));
+ goto done;
+ }
+ if (strlen(argv[0]) == 0) {
+ d_fprintf(stderr, _("error: zero length key name given\n"));
+ goto done;
+ }
+
+ werr = open_hive(ctx, argv[0], REG_KEY_WRITE, &hivekey, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "open_hive %s: %s\n", _("failed"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ if (recursive) {
+ werr = reg_deletekey_recursive(hivekey, subkeyname);
+ } else {
+ werr = reg_deletekey(hivekey, subkeyname);
+ }
+ if (!W_ERROR_IS_OK(werr) &&
+ !(c->opt_force && W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)))
+ {
+ d_fprintf(stderr, "reg_deletekey %s: %s\n", _("failed"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_deletekey(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_registry_deletekey_internal(c, argc, argv, false);
+}
+
+static int net_registry_deletekey_recursive(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_registry_deletekey_internal(c, argc, argv, true);
+}
+
+static int net_registry_getvalue_internal(struct net_context *c, int argc,
+ const char **argv, bool raw)
+{
+ WERROR werr;
+ int ret = -1;
+ struct registry_key *key = NULL;
+ struct registry_value *value = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net registry getvalue <key> <valuename>\n"));
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], REG_KEY_READ, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_queryvalue(ctx, key, argv[1], &value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_queryvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ print_registry_value(value, raw);
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_getvalue(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_registry_getvalue_internal(c, argc, argv, false);
+}
+
+static int net_registry_getvalueraw(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_registry_getvalue_internal(c, argc, argv, true);
+}
+
+static int net_registry_getvaluesraw(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ int ret = -1;
+ struct registry_key *key = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ uint32_t idx;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "usage: net rpc registry getvaluesraw "
+ "<key>\n");
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], REG_KEY_READ, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "open_key failed: %s\n", win_errstr(werr));
+ goto done;
+ }
+
+ idx = 0;
+ while (true) {
+ struct registry_value *val;
+
+ werr = reg_enumvalue(talloc_tos(), key, idx, NULL, &val);
+
+ if (W_ERROR_EQUAL(werr, WERR_NO_MORE_ITEMS)) {
+ ret = 0;
+ break;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ break;
+ }
+ print_registry_value(val, true);
+ TALLOC_FREE(val);
+ idx += 1;
+ }
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_setvalue(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ struct registry_value value;
+ struct registry_key *key = NULL;
+ int ret = -1;
+ TALLOC_CTX *ctx = talloc_stackframe();
+
+ if (argc < 4 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net registry setvalue <key> <valuename> "
+ "<type> [<val>]+\n"));
+ goto done;
+ }
+
+ if (!strequal(argv[2], "multi_sz") && (argc != 4)) {
+ d_fprintf(stderr, _("Too many args for type %s\n"), argv[2]);
+ goto done;
+ }
+
+ if (strequal(argv[2], "dword")) {
+ int error = 0;
+ uint32_t v;
+
+ v = smb_strtoul(argv[3], NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ goto done;
+ }
+
+ value.type = REG_DWORD;
+ value.data = data_blob_talloc(ctx, NULL, 4);
+ SIVAL(value.data.data, 0, v);
+ } else if (strequal(argv[2], "sz")) {
+ value.type = REG_SZ;
+ if (!push_reg_sz(ctx, &value.data, argv[3])) {
+ goto done;
+ }
+ } else if (strequal(argv[2], "multi_sz")) {
+ const char **array;
+ int count = argc - 3;
+ int i;
+ value.type = REG_MULTI_SZ;
+ array = talloc_zero_array(ctx, const char *, count + 1);
+ if (array == NULL) {
+ goto done;
+ }
+ for (i=0; i < count; i++) {
+ array[i] = talloc_strdup(array, argv[count+i]);
+ if (array[i] == NULL) {
+ goto done;
+ }
+ }
+ if (!push_reg_multi_sz(ctx, &value.data, array)) {
+ goto done;
+ }
+ } else {
+ d_fprintf(stderr, _("type \"%s\" not implemented\n"), argv[2]);
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], REG_KEY_WRITE, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_setvalue(key, argv[1], &value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_setvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_increment(struct net_context *c, int argc,
+ const char **argv)
+{
+ TDB_DATA lock_key = string_term_tdb_data("registry_increment_lock");
+ struct g_lock_ctx *ctx = NULL;
+ const char *keyname = NULL;
+ struct registry_key *key = NULL;
+ const char *valuename = NULL;
+ struct registry_value *value = NULL;
+ uint32_t v;
+ uint32_t increment;
+ uint32_t newvalue;
+ NTSTATUS status;
+ WERROR werr;
+ int ret = -1;
+
+ if (argc < 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net registry increment <key> <valuename> "
+ "[<increment>]\n"));
+ goto done;
+ }
+
+ keyname = argv[0];
+ valuename = argv[1];
+
+ increment = 1;
+ if (argc == 3) {
+ int error = 0;
+
+ increment = smb_strtoul(
+ argv[2], NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ goto done;
+ }
+ }
+
+ ctx = g_lock_ctx_init(c, c->msg_ctx);
+ if (ctx == NULL) {
+ d_fprintf(stderr, _("g_lock_ctx_init failed\n"));
+ goto done;
+ }
+
+ status = g_lock_lock(ctx,
+ lock_key,
+ G_LOCK_WRITE,
+ timeval_set(600, 0),
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("g_lock_lock failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ werr = open_key(c, keyname, REG_KEY_READ|REG_KEY_WRITE, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_queryvalue(key, key, valuename, &value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_queryvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ if (value->type != REG_DWORD) {
+ d_fprintf(stderr, _("value not a DWORD: %s\n"),
+ str_regtype(value->type));
+ goto done;
+ }
+
+ if (value->data.length < 4) {
+ d_fprintf(stderr, _("value too short for regular DWORD\n"));
+ goto done;
+ }
+
+ v = IVAL(value->data.data, 0);
+ v += increment;
+ newvalue = v;
+
+ SIVAL(value->data.data, 0, v);
+
+ werr = reg_setvalue(key, valuename, value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_setvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("increment failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ g_lock_unlock(ctx, lock_key);
+
+ d_printf(_("%"PRIu32"\n"), newvalue);
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(value);
+ TALLOC_FREE(key);
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_deletevalue(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ struct registry_key *key = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net registry deletevalue <key> <valuename>\n"));
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], REG_KEY_WRITE, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_deletevalue(key, argv[1]);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_deletevalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static WERROR net_registry_getsd_internal(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char *keyname,
+ struct security_descriptor **sd)
+{
+ WERROR werr;
+ struct registry_key *key = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ uint32_t access_mask = REG_KEY_READ |
+ SEC_FLAG_MAXIMUM_ALLOWED |
+ SEC_FLAG_SYSTEM_SECURITY;
+
+ /*
+ * net_rpc_regsitry uses SEC_FLAG_SYSTEM_SECURITY, but access
+ * is denied with these perms right now...
+ */
+ access_mask = REG_KEY_READ;
+
+ if (sd == NULL) {
+ d_fprintf(stderr, _("internal error: invalid argument\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ if (strlen(keyname) == 0) {
+ d_fprintf(stderr, _("error: zero length key name given\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ werr = open_key(ctx, keyname, access_mask, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "%s%s\n", _("open_key failed: "),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_getkeysecurity(mem_ctx, key, sd);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "%s%s\n", _("reg_getkeysecurity failed: "),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(ctx);
+ return werr;
+}
+
+static int net_registry_getsd(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ int ret = -1;
+ struct security_descriptor *secdesc = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry getsd <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry getsd 'HKLM\\Software\\Samba'\n"));
+ goto done;
+ }
+
+ werr = net_registry_getsd_internal(c, ctx, argv[0], &secdesc);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ display_sec_desc(secdesc);
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_getsd_sddl(struct net_context *c,
+ int argc, const char **argv)
+{
+ WERROR werr;
+ int ret = -1;
+ struct security_descriptor *secdesc = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry getsd_sddl <path>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry getsd_sddl 'HKLM\\Software\\Samba'\n"));
+ goto done;
+ }
+
+ werr = net_registry_getsd_internal(c, ctx, argv[0], &secdesc);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ d_printf("%s\n", sddl_encode(ctx, secdesc, get_global_sam_sid()));
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static WERROR net_registry_setsd_internal(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ const char *keyname,
+ struct security_descriptor *sd)
+{
+ WERROR werr;
+ struct registry_key *key = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ uint32_t access_mask = REG_KEY_WRITE |
+ SEC_FLAG_MAXIMUM_ALLOWED |
+ SEC_FLAG_SYSTEM_SECURITY;
+
+ /*
+ * net_rpc_regsitry uses SEC_FLAG_SYSTEM_SECURITY, but access
+ * is denied with these perms right now...
+ */
+ access_mask = REG_KEY_WRITE;
+
+ if (strlen(keyname) == 0) {
+ d_fprintf(stderr, _("error: zero length key name given\n"));
+ werr = WERR_INVALID_PARAMETER;
+ goto done;
+ }
+
+ werr = open_key(ctx, keyname, access_mask, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "%s%s\n", _("open_key failed: "),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_setkeysecurity(key, sd);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "%s%s\n", _("reg_setkeysecurity failed: "),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(ctx);
+ return werr;
+}
+
+static int net_registry_setsd_sddl(struct net_context *c,
+ int argc, const char **argv)
+{
+ WERROR werr;
+ int ret = -1;
+ struct security_descriptor *secdesc = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry setsd_sddl <path> <security_descriptor>\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry setsd_sddl 'HKLM\\Software\\Samba'\n"));
+ goto done;
+ }
+
+ secdesc = sddl_decode(ctx, argv[1], get_global_sam_sid());
+ if (secdesc == NULL) {
+ goto done;
+ }
+
+ werr = net_registry_setsd_internal(c, ctx, argv[0], secdesc);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+/******************************************************************************/
+/**
+ * @defgroup net_registry net registry
+ */
+
+/**
+ * @defgroup net_registry_import Import
+ * @ingroup net_registry
+ * @{
+ */
+
+struct import_ctx {
+ TALLOC_CTX *mem_ctx;
+};
+
+
+static WERROR import_create_key(struct import_ctx *ctx,
+ struct registry_key *parent,
+ const char *name, void **pkey, bool *existing)
+{
+ WERROR werr;
+ TALLOC_CTX *mem_ctx = talloc_new(ctx->mem_ctx);
+
+ struct registry_key *key = NULL;
+ enum winreg_CreateAction action;
+
+ if (parent == NULL) {
+ char *subkeyname = NULL;
+ werr = open_hive(mem_ctx, name, REG_KEY_WRITE,
+ &parent, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_hive failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+ name = subkeyname;
+ }
+
+ action = REG_ACTION_NONE;
+ werr = reg_createkey(mem_ctx, parent, name, REG_KEY_WRITE,
+ &key, &action);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_createkey failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ if (action == REG_ACTION_NONE) {
+ d_fprintf(stderr, _("createkey did nothing -- huh?\n"));
+ werr = WERR_CREATE_FAILED;
+ goto done;
+ }
+
+ if (existing != NULL) {
+ *existing = (action == REG_OPENED_EXISTING_KEY);
+ }
+
+ if (pkey!=NULL) {
+ *pkey = talloc_steal(ctx->mem_ctx, key);
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_close_key(struct import_ctx *ctx,
+ struct registry_key *key)
+{
+ return WERR_OK;
+}
+
+static WERROR import_delete_key(struct import_ctx *ctx,
+ struct registry_key *parent, const char *name)
+{
+ WERROR werr;
+ TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
+
+ if (parent == NULL) {
+ char *subkeyname = NULL;
+ werr = open_hive(mem_ctx, name, REG_KEY_WRITE,
+ &parent, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_hive failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+ name = subkeyname;
+ }
+
+ werr = reg_deletekey_recursive(parent, name);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "reg_deletekey_recursive %s: %s\n",
+ _("failed"), win_errstr(werr));
+ goto done;
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_create_val (struct import_ctx *ctx,
+ struct registry_key *parent, const char *name,
+ const struct registry_value *value)
+{
+ WERROR werr;
+
+ if (parent == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = reg_setvalue(parent, name, value);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_setvalue failed: %s\n"),
+ win_errstr(werr));
+ }
+ return werr;
+}
+
+static WERROR import_delete_val (struct import_ctx *ctx,
+ struct registry_key *parent, const char *name)
+{
+ WERROR werr;
+
+ if (parent == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ werr = reg_deletevalue(parent, name);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_deletevalue failed: %s\n"),
+ win_errstr(werr));
+ }
+
+ return werr;
+}
+
+struct precheck_ctx {
+ TALLOC_CTX *mem_ctx;
+ bool failed;
+};
+
+static WERROR precheck_create_key(struct precheck_ctx *ctx,
+ struct registry_key *parent,
+ const char *name, void **pkey, bool *existing)
+{
+ WERROR werr;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct registry_key *key = NULL;
+
+ if (parent == NULL) {
+ char *subkeyname = NULL;
+ werr = open_hive(frame, name, REG_KEY_READ,
+ &parent, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: open_hive of [%s] failed: %s\n",
+ name, win_errstr(werr));
+ goto done;
+ }
+ name = subkeyname;
+ }
+
+ werr = reg_openkey(frame, parent, name, 0, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: openkey [%s] failed: %s\n",
+ name, win_errstr(werr));
+ goto done;
+ }
+
+ if (existing != NULL) {
+ *existing = true;
+ }
+
+ if (pkey != NULL) {
+ *pkey = talloc_steal(ctx->mem_ctx, key);
+ }
+
+done:
+ talloc_free(frame);
+ ctx->failed = !W_ERROR_IS_OK(werr);
+ return werr;
+}
+
+static WERROR precheck_close_key(struct precheck_ctx *ctx,
+ struct registry_key *key)
+{
+ talloc_free(key);
+ return WERR_OK;
+}
+
+static WERROR precheck_delete_key(struct precheck_ctx *ctx,
+ struct registry_key *parent, const char *name)
+{
+ WERROR werr;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct registry_key *key;
+
+ if (parent == NULL) {
+ char *subkeyname = NULL;
+ werr = open_hive(frame, name, REG_KEY_READ,
+ &parent, &subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: open_hive of [%s] failed: %s\n",
+ name, win_errstr(werr));
+ goto done;
+ }
+ name = subkeyname;
+ }
+
+ werr = reg_openkey(ctx->mem_ctx, parent, name, 0, &key);
+ if (W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: key [%s\\%s] should not exist\n",
+ parent->key->name, name);
+ werr = WERR_FILE_EXISTS;
+ } else if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ werr = WERR_OK;
+ } else {
+ d_printf("Precheck: openkey [%s\\%s] failed: %s\n",
+ parent->key->name, name, win_errstr(werr));
+ }
+
+done:
+ talloc_free(frame);
+ ctx->failed = !W_ERROR_IS_OK(werr);
+ return werr;
+}
+
+static int registry_value_cmp(
+ const struct registry_value* v1, const struct registry_value* v2)
+{
+ if (v1->type == v2->type) {
+ return data_blob_cmp(&v1->data, &v2->data);
+ }
+ return v1->type - v2->type;
+}
+
+static WERROR precheck_create_val(struct precheck_ctx *ctx,
+ struct registry_key *parent,
+ const char *name,
+ const struct registry_value *value)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct registry_value *old;
+ WERROR werr;
+
+ SMB_ASSERT(parent);
+
+ werr = reg_queryvalue(frame, parent, name, &old);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: queryvalue \"%s\" of [%s] failed: %s\n",
+ name, parent->key->name, win_errstr(werr));
+ goto done;
+ }
+ if (registry_value_cmp(value, old) != 0) {
+ d_printf("Precheck: unexpected value \"%s\" of key [%s]\n",
+ name, parent->key->name);
+ ctx->failed = true;
+ }
+done:
+ talloc_free(frame);
+ return werr;
+}
+
+static WERROR precheck_delete_val(struct precheck_ctx *ctx,
+ struct registry_key *parent,
+ const char *name)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct registry_value *old;
+ WERROR werr;
+
+ SMB_ASSERT(parent);
+
+ werr = reg_queryvalue(frame, parent, name, &old);
+ if (W_ERROR_IS_OK(werr)) {
+ d_printf("Precheck: value \"%s\" of key [%s] should not exist\n",
+ name, parent->key->name);
+ werr = WERR_FILE_EXISTS;
+ } else if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ werr = WERR_OK;
+ } else {
+ printf("Precheck: queryvalue \"%s\" of key [%s] failed: %s\n",
+ name, parent->key->name, win_errstr(werr));
+ }
+
+ talloc_free(frame);
+ ctx->failed = !W_ERROR_IS_OK(werr);
+ return werr;
+}
+
+static bool import_precheck(const char *fname, const char *parse_options)
+{
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ struct precheck_ctx precheck_ctx = {
+ .mem_ctx = mem_ctx,
+ .failed = false,
+ };
+ struct reg_import_callback precheck_callback = {
+ .openkey = NULL,
+ .closekey = (reg_import_callback_closekey_t)&precheck_close_key,
+ .createkey = (reg_import_callback_createkey_t)&precheck_create_key,
+ .deletekey = (reg_import_callback_deletekey_t)&precheck_delete_key,
+ .deleteval = (reg_import_callback_deleteval_t)&precheck_delete_val,
+ .setval = {
+ .registry_value = (reg_import_callback_setval_registry_value_t)
+ &precheck_create_val,
+ },
+ .setval_type = REGISTRY_VALUE,
+ .data = &precheck_ctx
+ };
+ struct reg_parse_callback *parse_callback;
+ int ret;
+
+ if (!fname) {
+ return true;
+ }
+
+ parse_callback = reg_import_adapter(mem_ctx, precheck_callback);
+ if (parse_callback == NULL) {
+ d_printf("talloc failed\n");
+ return false;
+ }
+
+ ret = reg_parse_file(fname, parse_callback, parse_options);
+
+ if (ret < 0 || precheck_ctx.failed) {
+ d_printf("Precheck failed\n");
+ return false;
+ }
+ return true;
+}
+
+static int import_with_precheck_action(const char *import_fname,
+ const char *precheck_fname,
+ const char *parse_options)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct import_ctx import_ctx = {
+ .mem_ctx = frame,
+ };
+ struct reg_import_callback import_callback = {
+ .openkey = NULL,
+ .closekey = (reg_import_callback_closekey_t)&import_close_key,
+ .createkey = (reg_import_callback_createkey_t)&import_create_key,
+ .deletekey = (reg_import_callback_deletekey_t)&import_delete_key,
+ .deleteval = (reg_import_callback_deleteval_t)&import_delete_val,
+ .setval = {
+ .registry_value = (reg_import_callback_setval_registry_value_t)
+ &import_create_val,
+ },
+ .setval_type = REGISTRY_VALUE,
+ .data = &import_ctx
+ };
+ struct reg_parse_callback *parse_callback;
+ int ret = -1;
+ bool precheck_ok;
+
+ precheck_ok = import_precheck(precheck_fname, parse_options);
+ if (!precheck_ok) {
+ goto done;
+ }
+
+ parse_callback = reg_import_adapter(frame, import_callback);
+ if (parse_callback == NULL) {
+ d_printf("talloc failed\n");
+ goto done;
+ }
+
+ ret = reg_parse_file(import_fname, parse_callback, parse_options);
+
+done:
+ talloc_free(frame);
+ return ret;
+}
+
+static int net_registry_import(struct net_context *c, int argc,
+ const char **argv)
+{
+ const char *parse_options = (argc > 1) ? argv[1] : NULL;
+ int ret = -1;
+ WERROR werr;
+
+ if (argc < 1 || argc > 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry import <reg> [options]\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry import file.reg enc=CP1252\n"));
+ return -1;
+ }
+
+ werr = regdb_open();
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to open regdb: %s\n", win_errstr(werr));
+ return -1;
+ }
+
+ werr = regdb_transaction_start();
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to start transaction on regdb: %s\n",
+ win_errstr(werr));
+ goto done;
+ }
+
+ ret = import_with_precheck_action(argv[0], c->opt_precheck,
+ parse_options);
+
+ if (ret < 0) {
+ d_printf("Transaction canceled!\n");
+ regdb_transaction_cancel();
+ goto done;
+ }
+
+ SMB_ASSERT(ret == 0);
+
+ if (c->opt_testmode) {
+ d_printf("Testmode: not committing changes.\n");
+ regdb_transaction_cancel();
+ goto done;
+ }
+
+ werr = regdb_transaction_commit();
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("Failed to commit transaction on regdb: %s\n",
+ win_errstr(werr));
+ ret = -1;
+ }
+
+done:
+ regdb_close();
+ return ret;
+}
+/**@}*/
+
+/******************************************************************************/
+
+/**
+ * @defgroup net_registry_export Export
+ * @ingroup net_registry
+ * @{
+ */
+
+static int registry_export(TALLOC_CTX *ctx, /*const*/ struct registry_key *key,
+ struct reg_format *f)
+{
+ int ret=-1;
+ WERROR werr;
+ uint32_t count;
+
+ struct registry_value *valvalue = NULL;
+ char *valname = NULL;
+
+ char *subkey_name = NULL;
+ NTTIME modtime = 0;
+
+ reg_format_registry_key(f, key, false);
+
+ /* print values */
+ for (count = 0;
+ werr = reg_enumvalue(ctx, key, count, &valname, &valvalue),
+ W_ERROR_IS_OK(werr);
+ count++)
+ {
+ reg_format_registry_value(f, valname, valvalue);
+ }
+ if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+ d_fprintf(stderr, _("reg_enumvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ /* recurse on subkeys */
+ for (count = 0;
+ werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime),
+ W_ERROR_IS_OK(werr);
+ count++)
+ {
+ struct registry_key *subkey = NULL;
+
+ werr = reg_openkey(ctx, key, subkey_name, REG_KEY_READ,
+ &subkey);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("reg_openkey failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ registry_export(ctx, subkey, f);
+ TALLOC_FREE(subkey);
+ }
+ if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) {
+ d_fprintf(stderr, _("reg_enumkey failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+ ret = 0;
+done:
+ return ret;
+}
+
+static int net_registry_export(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret=-1;
+ WERROR werr;
+ struct registry_key *key = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ struct reg_format *f=NULL;
+
+ if (argc < 2 || argc > 3 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry export <path> <file> [opt]\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry export 'HKLM\\Software\\Samba' "
+ "samba.reg regedit5\n"));
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], REG_KEY_READ, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr));
+ goto done;
+ }
+
+ f = reg_format_file(ctx, argv[1], (argc > 2) ? argv[2] : NULL);
+ if (f == NULL) {
+ d_fprintf(stderr, _("open file failed: %s\n"), strerror(errno));
+ goto done;
+ }
+
+ ret = registry_export(ctx, key, f);
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+/**@}*/
+
+/******************************************************************************/
+/**
+ * @defgroup net_registry_convert Convert
+ * @ingroup net_registry
+ * @{
+ */
+
+static int net_registry_convert(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret;
+ TALLOC_CTX *mem_ctx;
+ const char *in_opt = NULL;
+ const char *out_opt = NULL;
+
+ if (argc < 2 || argc > 4|| c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry convert <in> <out> [in_opt] [out_opt]\n"
+ "net registry convert <in> <out> [out_opt]\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net registry convert in.reg out.reg regedit4,enc=CP1252\n"));
+ return -1;
+ }
+
+ mem_ctx = talloc_stackframe();
+
+ switch (argc ) {
+ case 2:
+ break;
+ case 3:
+ out_opt = argv[2];
+ break;
+ case 4:
+ out_opt = argv[3];
+ in_opt = argv[2];
+ break;
+ default:
+ assert(false);
+ }
+
+
+ ret = reg_parse_file(argv[0], (struct reg_parse_callback*)
+ reg_format_file(mem_ctx, argv[1], out_opt),
+ in_opt);
+
+ talloc_free(mem_ctx);
+
+ return ret;
+}
+/**@}*/
+
+static int net_registry_check(struct net_context *c, int argc,
+ const char **argv)
+{
+ char *dbfile;
+ struct check_options opts;
+ int ret;
+
+ if (argc > 1|| c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net registry check [-vraTfl] [-o <ODB>] [--wipe] [<TDB>]\n"
+ " Check a registry database.\n"
+ " -v|--verbose\t be verbose\n"
+ " -r|--repair\t\t interactive repair mode\n"
+ " -a|--auto\t\t noninteractive repair mode\n"
+ " -T|--test\t\t dry run\n"
+ " -f|--force\t\t force\n"
+ " -l|--lock\t\t lock <TDB> while doing the check\n"
+ " -o|--output=<ODB>\t output database\n"
+ " --reg-version=n\t assume database format version {n|1,2,3}\n"
+ " --wipe\t\t create a new database from scratch\n"
+ " --db=<TDB>\t\t registry database to open\n"));
+ return c->display_usage ? 0 : -1;
+ }
+
+ if (c->opt_db != NULL) {
+ dbfile = talloc_strdup(talloc_tos(), c->opt_db);
+ } else if (argc > 0) {
+ dbfile = talloc_strdup(talloc_tos(), argv[0]);
+ } else {
+ dbfile = state_path(talloc_tos(), "registry.tdb");
+ }
+ if (dbfile == NULL) {
+ return -1;
+ }
+
+ opts = (struct check_options) {
+ .lock = c->opt_lock || c->opt_long_list_entries,
+ .test = c->opt_testmode,
+ .automatic = c->opt_auto,
+ .verbose = c->opt_verbose,
+ .force = c->opt_force,
+ .repair = c->opt_repair || c->opt_reboot,
+ .version = c->opt_reg_version,
+ .output = c->opt_output,
+ .wipe = c->opt_wipe,
+ .implicit_db = (c->opt_db == NULL) && (argc == 0),
+ };
+
+ ret = net_registry_check_db(dbfile, &opts);
+ talloc_free(dbfile);
+ return ret;
+}
+
+
+/******************************************************************************/
+
+int net_registry(struct net_context *c, int argc, const char **argv)
+{
+ int ret = -1;
+
+ struct functable func[] = {
+ {
+ "enumerate",
+ net_registry_enumerate,
+ NET_TRANSPORT_LOCAL,
+ N_("Enumerate registry keys and values"),
+ N_("net registry enumerate\n"
+ " Enumerate registry keys and values")
+ },
+ {
+ "enumerate_recursive",
+ net_registry_enumerate_recursive,
+ NET_TRANSPORT_LOCAL,
+ N_("Enumerate registry keys and values"),
+ N_("net registry enumerate_recursive\n"
+ " Enumerate registry keys and values")
+ },
+ {
+ "createkey",
+ net_registry_createkey,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new registry key"),
+ N_("net registry createkey\n"
+ " Create a new registry key")
+ },
+ {
+ "deletekey",
+ net_registry_deletekey,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a registry key"),
+ N_("net registry deletekey\n"
+ " Delete a registry key")
+ },
+ {
+ "deletekey_recursive",
+ net_registry_deletekey_recursive,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a registry key with subkeys"),
+ N_("net registry deletekey_recursive\n"
+ " Delete a registry key with subkeys")
+ },
+ {
+ "getvalue",
+ net_registry_getvalue,
+ NET_TRANSPORT_LOCAL,
+ N_("Print a registry value"),
+ N_("net registry getvalue\n"
+ " Print a registry value")
+ },
+ {
+ "getvalueraw",
+ net_registry_getvalueraw,
+ NET_TRANSPORT_LOCAL,
+ N_("Print a registry value (raw format)"),
+ N_("net registry getvalueraw\n"
+ " Print a registry value (raw format)")
+ },
+ {
+ "getvaluesraw",
+ net_registry_getvaluesraw,
+ NET_TRANSPORT_LOCAL,
+ "Print all values of a key in raw format",
+ "net registry getvaluesraw <key>\n"
+ " Print a registry value (raw format)"
+ },
+ {
+ "setvalue",
+ net_registry_setvalue,
+ NET_TRANSPORT_LOCAL,
+ N_("Set a new registry value"),
+ N_("net registry setvalue\n"
+ " Set a new registry value")
+ },
+ {
+ "increment",
+ net_registry_increment,
+ NET_TRANSPORT_LOCAL,
+ N_("Increment a DWORD registry value under a lock"),
+ N_("net registry increment\n"
+ " Increment a DWORD registry value under a lock")
+ },
+ {
+ "deletevalue",
+ net_registry_deletevalue,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a registry value"),
+ N_("net registry deletevalue\n"
+ " Delete a registry value")
+ },
+ {
+ "getsd",
+ net_registry_getsd,
+ NET_TRANSPORT_LOCAL,
+ N_("Get security descriptor"),
+ N_("net registry getsd\n"
+ " Get security descriptor")
+ },
+ {
+ "getsd_sddl",
+ net_registry_getsd_sddl,
+ NET_TRANSPORT_LOCAL,
+ N_("Get security descriptor in sddl format"),
+ N_("net registry getsd_sddl\n"
+ " Get security descriptor in sddl format")
+ },
+ {
+ "setsd_sddl",
+ net_registry_setsd_sddl,
+ NET_TRANSPORT_LOCAL,
+ N_("Set security descriptor from sddl format string"),
+ N_("net registry setsd_sddl\n"
+ " Set security descriptor from sddl format string")
+ },
+ {
+ "import",
+ net_registry_import,
+ NET_TRANSPORT_LOCAL,
+ N_("Import .reg file"),
+ N_("net registry import\n"
+ " Import .reg file")
+ },
+ {
+ "export",
+ net_registry_export,
+ NET_TRANSPORT_LOCAL,
+ N_("Export .reg file"),
+ N_("net registry export\n"
+ " Export .reg file")
+ },
+ {
+ "convert",
+ net_registry_convert,
+ NET_TRANSPORT_LOCAL,
+ N_("Convert .reg file"),
+ N_("net registry convert\n"
+ " Convert .reg file")
+ },
+ {
+ "check",
+ net_registry_check,
+ NET_TRANSPORT_LOCAL,
+ N_("Check a registry database"),
+ N_("net registry check\n"
+ " Check a registry database")
+ },
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ if (!c->display_usage
+ && argc > 0
+ && (strcasecmp_m(argv[0], "convert") != 0)
+ && (strcasecmp_m(argv[0], "check") != 0))
+ {
+ if (!W_ERROR_IS_OK(registry_init_basic())) {
+ return -1;
+ }
+ }
+
+ ret = net_run_function(c, argc, argv, "net registry", func);
+
+ return ret;
+}
diff --git a/source3/utils/net_registry_check.c b/source3/utils/net_registry_check.c
new file mode 100644
index 0000000..6cb9256
--- /dev/null
+++ b/source3/utils/net_registry_check.c
@@ -0,0 +1,1324 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Check the registry database.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Mar 2011
+ */
+
+#include "net_registry_check.h"
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "net.h"
+#include "libcli/security/dom_sid.h"
+#include "libcli/security/secdesc.h"
+#include "cbuf.h"
+#include "srprs.h"
+#include <termios.h>
+#include "util_tdb.h"
+#include "registry/reg_db.h"
+#include "libcli/registry/util_reg.h"
+#include "registry/reg_parse_internal.h"
+#include "interact.h"
+
+/*
+ check tree:
+ + every key has a subkeylist
+ + every key is referenced by the subkeylist of its parent
+ check path:
+ + starts with valid hive
+ + UTF-8 (option to convert ???)
+ + only uppercase
+ + separator ???
+ check value:
+ + REG_DWORD has size 4
+ + REG_QWORD has size 8
+ + STRINGS are zero terminated UTF-16
+*/
+
+struct regval {
+ char *name;
+ uint32_t type;
+ DATA_BLOB data;
+};
+
+struct regkey {
+ char *name;
+ char *path;
+ bool has_subkeylist;
+ bool needs_update;
+ struct regkey *parent;
+ size_t nsubkeys;
+ struct regkey **subkeys;
+ size_t nvalues;
+ struct regval **values;
+ struct security_descriptor *sd;
+};
+
+struct check_ctx {
+ char *fname;
+ struct check_options opt;
+
+ uint32_t version;
+ char sep;
+ struct db_context *idb;
+ struct db_context *odb;
+
+ struct regkey *root; /*dummy key to hold all basekeys*/
+ struct db_context *reg;
+ struct db_context *del;
+
+ bool transaction;
+ char auto_action;
+ char default_action;
+};
+
+static void* talloc_array_append(void *mem_ctx, void* array[], void *ptr)
+{
+ size_t size = array ? talloc_array_length(array) : 1;
+ void **tmp = talloc_realloc(mem_ctx, array, void*, size + 1);
+ if (tmp == NULL) {
+ talloc_free(array);
+ return NULL;
+ }
+ tmp[size-1] = ptr;
+ tmp[size] = NULL;
+ return tmp;
+}
+
+static void regkey_add_subkey(struct regkey *key, struct regkey *subkey)
+{
+ key->subkeys = (struct regkey**)
+ talloc_array_append(key, (void**)key->subkeys, subkey);
+ if (key->subkeys != NULL) {
+ key->nsubkeys++;
+ }
+}
+
+static struct regval* regval_copy(TALLOC_CTX *mem_ctx, const struct regval *val)
+{
+ struct regval *ret = talloc_zero(mem_ctx, struct regval);
+ if (ret == NULL) {
+ goto fail;
+ }
+
+ ret->name = talloc_strdup(ret, val->name);
+ if (ret->name == NULL) {
+ goto fail;
+ }
+
+ ret->data = data_blob_dup_talloc(ret, val->data);
+ if (ret->data.data == NULL) {
+ goto fail;
+ }
+
+ ret->type = val->type;
+
+ return ret;
+fail:
+ talloc_free(ret);
+ return NULL;
+}
+
+static void regkey_add_regval(struct regkey *key, struct regval *val)
+{
+ key->values = (struct regval**)
+ talloc_array_append(key, (void**)key->values, val);
+ if (key->values != NULL) {
+ key->nvalues++;
+ }
+}
+
+static bool tdb_data_read_uint32(TDB_DATA *buf, uint32_t *result)
+{
+ const size_t len = sizeof(uint32_t);
+ if (buf->dsize >= len) {
+ *result = IVAL(buf->dptr, 0);
+ buf->dptr += len;
+ buf->dsize -= len;
+ return true;
+ }
+ return false;
+}
+
+static bool tdb_data_read_cstr(TDB_DATA *buf, char **result)
+{
+ const size_t len = strnlen((char*)buf->dptr, buf->dsize) + 1;
+ if (buf->dsize >= len) {
+ *result = (char*)buf->dptr;
+ buf->dptr += len;
+ buf->dsize -= len;
+ return true;
+ }
+ return false;
+}
+
+static bool tdb_data_read_blob(TDB_DATA *buf, DATA_BLOB *result)
+{
+ TDB_DATA tmp = *buf;
+ uint32_t len;
+ if (!tdb_data_read_uint32(&tmp, &len)) {
+ return false;
+ }
+ if (tmp.dsize >= len) {
+ *buf = tmp;
+ result->data = tmp.dptr;
+ result->length = len;
+ buf->dptr += len;
+ buf->dsize -= len;
+ return true;
+ }
+ return false;
+}
+
+static bool tdb_data_read_regval(TDB_DATA *buf, struct regval *result)
+{
+ TDB_DATA tmp = *buf;
+ struct regval value;
+ if (!tdb_data_read_cstr(&tmp, &value.name)
+ || !tdb_data_read_uint32(&tmp, &value.type)
+ || !tdb_data_read_blob(&tmp, &value.data))
+ {
+ return false;
+ }
+ *buf = tmp;
+ *result = value;
+ return true;
+}
+
+static bool tdb_data_is_cstr(TDB_DATA d) {
+ if (tdb_data_is_empty(d) || (d.dptr[d.dsize-1] != '\0')) {
+ return false;
+ }
+ return strlen((char *)d.dptr) == d.dsize-1;
+}
+
+static TDB_DATA cbuf_make_tdb_data(cbuf *b)
+{
+ return make_tdb_data((void*)cbuf_gets(b, 0), cbuf_getpos(b));
+}
+
+static void remove_all(char *str, char c)
+{
+ char *out=str;
+ while (*str) {
+ if (*str != c) {
+ *out = *str;
+ out++;
+ }
+ str++;
+ }
+ *out = '\0';
+}
+
+static char* parent_path(const char *path, char sep)
+{
+ const char *p = strrchr(path, sep);
+ return p ? talloc_strndup(talloc_tos(), path, p-path) : NULL;
+}
+
+/* return the regkey corresponding to path, create if not yet existing */
+static struct regkey*
+check_ctx_lookup_key(struct check_ctx *ctx, const char *path) {
+ struct regkey *ret = NULL;
+ NTSTATUS status;
+ TDB_DATA val = tdb_null;
+
+ if ( path == NULL) {
+ return ctx->root;
+ }
+
+ status = dbwrap_fetch(ctx->reg, ctx, string_term_tdb_data(path), &val);
+ if (NT_STATUS_IS_OK(status)) {
+ if (ctx->opt.verbose) {
+ printf("Open: %s\n", path);
+ }
+ ret = *(struct regkey**)val.dptr;
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /* not yet existing, create */
+ char *pp;
+ if (ctx->opt.verbose) {
+ printf("New: %s\n", path);
+ }
+ ret = talloc_zero(ctx, struct regkey);
+ if (ret == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+ ret->path = talloc_strdup(ret, path);
+
+ pp = parent_path(path, ctx->sep);
+ ret->parent = check_ctx_lookup_key(ctx, pp);
+ regkey_add_subkey(ret->parent, ret);
+ TALLOC_FREE(pp);
+
+ /* the dummy root key has no subkeylist so set the name */
+ if (ret->parent == ctx->root) {
+ ret->name = talloc_strdup(ret, path);
+ }
+
+ dbwrap_store(ctx->reg, string_term_tdb_data(path),
+ make_tdb_data((void*)&ret, sizeof(ret)), 0);
+ } else {
+ DEBUG(0, ("lookup key: failed to fetch %s: %s\n", path,
+ nt_errstr(status)));
+ }
+done:
+ talloc_free(val.dptr);
+ return ret;
+}
+
+static struct check_ctx* check_ctx_create(TALLOC_CTX *mem_ctx, const char *db,
+ const struct check_options *opt)
+{
+ struct check_ctx *ctx = talloc_zero(mem_ctx, struct check_ctx);
+
+ ctx->opt = *opt;
+ ctx->reg = db_open_rbt(ctx);
+ ctx->del = db_open_rbt(ctx);
+ ctx->root = talloc_zero(ctx, struct regkey);
+ ctx->fname = talloc_strdup(ctx, db);
+
+ if (opt->automatic && (opt->output == NULL)) {
+ ctx->opt.repair = true;
+ ctx->opt.output = ctx->fname;
+ }
+
+ if (opt->repair) {
+ if (opt->output) {
+ d_fprintf(stderr, "You can not specify --output "
+ "with --repair\n");
+ goto fail;
+ } else {
+ ctx->opt.output = ctx->fname;
+ }
+ }
+
+ ctx->default_action = 'r';
+ return ctx;
+fail:
+ talloc_free(ctx);
+ return NULL;
+}
+
+static bool check_ctx_open_output(struct check_ctx *ctx)
+{
+ int oflags = O_RDWR | O_CREAT ;
+
+ if (ctx->opt.output == NULL) {
+ return true;
+ }
+
+ if (!ctx->opt.repair) {
+ if (!ctx->opt.wipe) {
+ oflags |= O_EXCL;
+ }
+ ctx->opt.wipe = true;
+ }
+
+ ctx->odb = db_open(ctx, ctx->opt.output, 0, TDB_DEFAULT, oflags, 0644,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (ctx->odb == NULL) {
+ d_fprintf(stderr,
+ _("Could not open db (%s) for writing: %s\n"),
+ ctx->opt.output, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+
+static bool check_ctx_open_input(struct check_ctx *ctx) {
+ ctx->idb = db_open(ctx, ctx->fname, 0, TDB_DEFAULT, O_RDONLY, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+ if (ctx->idb == NULL) {
+ d_fprintf(stderr,
+ _("Could not open db (%s) for reading: %s\n"),
+ ctx->fname, strerror(errno));
+ return false;
+ }
+ return true;
+}
+
+static bool check_ctx_transaction_start(struct check_ctx *ctx) {
+ if (ctx->odb == NULL) {
+ return true;
+ }
+ if (dbwrap_transaction_start(ctx->odb) != 0) {
+ DEBUG(0, ("transaction_start failed\n"));
+ return false;
+ }
+ ctx->transaction = true;
+ return true;
+}
+
+static void check_ctx_transaction_stop(struct check_ctx *ctx, bool ok) {
+ if (!ctx->transaction) {
+ return;
+ }
+ if (!ctx->opt.test && ok) {
+ d_printf("Committing changes\n");
+ if (dbwrap_transaction_commit(ctx->odb) != 0) {
+ DEBUG(0, ("transaction_commit failed\n"));
+ }
+ } else {
+ d_printf("Discarding changes\n");
+ dbwrap_transaction_cancel(ctx->odb);
+ }
+}
+
+static bool read_info(struct check_ctx *ctx, const char *key, TDB_DATA val)
+{
+ if (val.dsize==sizeof(uint32_t) && strcmp(key, "version")==0) {
+ uint32_t v = IVAL(val.dptr, 0);
+ printf("INFO: %s = %d\n", key, v);
+ return true;
+ }
+ printf("INFO: %s = <invalid>\n", key);
+ return false;
+}
+
+static bool is_all_upper(const char *str) {
+ bool ret;
+ char *tmp = talloc_strdup(talloc_tos(), str);
+ if (!strupper_m(tmp)) {
+ talloc_free(tmp);
+ return false;
+ }
+ ret = (strcmp(tmp, str) == 0);
+ talloc_free(tmp);
+ return ret;
+}
+
+static void move_to_back(struct regkey *key, struct regkey *subkey)
+{
+ struct regkey **ptr;
+ size_t nidx;
+
+ DEBUG(5, ("Move to back subkey \"%s\" of \"%s\"\n",
+ subkey->path, key->path));
+
+ for (ptr=key->subkeys; *ptr != subkey; ptr++)
+ ;
+
+ nidx = ptr + 1 - key->subkeys;
+ memmove(ptr, ptr+1, (key->nsubkeys - nidx) * sizeof(*ptr));
+
+ key->subkeys[key->nsubkeys-1] = subkey;
+}
+
+static void set_subkey_name(struct check_ctx *ctx, struct regkey *key,
+ const char *name, int nlen)
+{
+ char *path = key->path;
+ TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
+ char *p;
+ struct regkey *subkey;
+ char *nname = talloc_strndup(mem_ctx, name, nlen);
+ remove_all(nname, ctx->sep);
+
+ if (strncmp(name, nname, nlen) != 0) {
+ /* XXX interaction: delete/edit */
+ printf("Warning: invalid name: \"%s\" replace with \"%s\"\n",
+ name, nname);
+ key->needs_update = true;
+ }
+ p = talloc_asprintf_strupper_m(mem_ctx, "%s%c%s",
+ path, ctx->sep, nname);
+ subkey = check_ctx_lookup_key(ctx, p);
+ if (subkey->name) {
+ bool do_replace = false;
+
+ if (strcmp(subkey->name, nname) != 0) {
+ int action;
+ char default_action;
+
+ if (is_all_upper(nname)) {
+ default_action = 'o';
+ } else {
+ default_action = 'n';
+ }
+
+ printf("Conflicting subkey names of [%s]: "
+ "old: \"%s\", new: \"%s\"\n",
+ key->path, subkey->name, nname);
+
+ if (ctx->opt.output == NULL || ctx->opt.automatic) {
+ action = default_action;
+ } else {
+ do {
+ action = interact_prompt(
+ "choose spelling [o]ld, [n]ew,"
+ "or [e]dit", "one",
+ default_action);
+ if (action == 'e') {
+ printf("Sorry, edit is not yet "
+ "implemented here...\n");
+ }
+ } while (action == 'e');
+ }
+
+ if (action == 'n') {
+ do_replace = true;
+ }
+ }
+
+ if (do_replace) {
+ if (ctx->opt.verbose) {
+ printf("Replacing name: %s: \"%s\""
+ " -> \"%s\"\n", path,
+ subkey->name, nname);
+ }
+ TALLOC_FREE(subkey->name);
+ subkey->name = talloc_steal(subkey, nname);
+ key->needs_update = true;
+ }
+ } else {
+ if (ctx->opt.verbose) {
+ printf("Set name: %s: \"%s\"\n", path, nname);
+ }
+ subkey->name = talloc_steal(subkey, nname);
+ }
+
+ move_to_back(key, subkey);
+ TALLOC_FREE(mem_ctx);
+}
+
+static void
+read_subkeys(struct check_ctx *ctx, const char *path, TDB_DATA val, bool update)
+{
+ uint32_t num_items, found_items = 0;
+ char *subkey;
+ struct regkey *key = check_ctx_lookup_key(ctx, path);
+
+ key->needs_update |= update;
+
+ /* printf("SUBKEYS: %s\n", path); */
+ if (key->has_subkeylist) {
+ printf("Duplicate subkeylist \"%s\"\n",
+ path);
+ found_items = key->nsubkeys;
+ }
+
+ /* exists as defined by regdb_key_exists() */
+ key->has_subkeylist = true;
+
+ /* name is set if a key is referenced by the */
+ /* subkeylist of its parent. */
+
+ if (!tdb_data_read_uint32(&val, &num_items) ) {
+ printf("Invalid subkeylist: \"%s\"\n", path);
+ return;
+ }
+
+ while (tdb_data_read_cstr(&val, &subkey)) {
+ /* printf(" SUBKEY: %s\n", subkey); */
+ set_subkey_name(ctx, key, subkey, strlen(subkey));
+ found_items++;
+ }
+
+ if (val.dsize != 0) {
+ printf("Subkeylist of \"%s\": trailing: \"%.*s\"\n",
+ path, (int)val.dsize, val.dptr);
+ /* ask: best effort, delete or edit?*/
+ set_subkey_name(ctx, key, (char*)val.dptr, val.dsize);
+ found_items++;
+ key->needs_update = true;
+ }
+
+ if (num_items != found_items) {
+ printf("Subkeylist of \"%s\": invalid number of subkeys, "
+ "expected: %d got: %d\n", path, num_items, found_items);
+ key->needs_update = true;
+ }
+
+}
+
+static void read_values(struct check_ctx *ctx, const char *path, TDB_DATA val)
+{
+ struct regkey *key = check_ctx_lookup_key(ctx, path);
+ uint32_t num_items, found_items;
+ struct regval value;
+
+ /* printf("VALUES: %s\n", path); */
+
+ if (!tdb_data_read_uint32(&val, &num_items) ) {
+ printf("Invalid valuelist: \"%s\"\n", path);
+ return;
+ }
+
+ found_items=0;
+ while (tdb_data_read_regval(&val, &value)) {
+ /* printf(" VAL: %s type: %s(%d) length: %d\n", value.name, */
+ /* str_regtype(value.type), value.type, */
+ /* (int)value.data.length); */
+ regkey_add_regval(key, regval_copy(key, &value));
+ found_items++;
+ }
+
+ if (num_items != found_items) {
+ printf("Valuelist of \"%s\": invalid number of values, "
+ "expected: %d got: %d\n", path, num_items, found_items);
+ key->needs_update = true;
+ }
+
+ if (val.dsize != 0) {
+ printf("Valuelist of \"%s\": trailing: \"%*s\"\n", path,
+ (int)val.dsize, val.dptr);
+ key->needs_update = true;
+ /* XXX best effort ??? */
+ /* ZERO_STRUCT(value); */
+ /* if (tdb_data_read_cstr(&val, &value.name) */
+ /* && tdb_data_read_uint32(&val, &value.type)) */
+ /* { */
+ /* uint32_t len = -1; */
+ /* tdb_data_read_uint32(&val, &len); */
+ /* ... */
+ /* found_items ++; */
+ /* regkey_add_regval(key, regval_copy(key, value)); */
+ /* } */
+ }
+ if (found_items == 0) {
+ printf("Valuelist of \"%s\" empty\n", path);
+ key->needs_update = true;
+ }
+}
+
+static bool read_sorted(struct check_ctx *ctx, const char *path, TDB_DATA val)
+{
+ if (ctx->version >= 3) {
+ return false;
+ }
+
+ if ((val.dptr == NULL) || (val.dsize<4)) {
+ return false;
+ }
+
+ /* ToDo: check */
+ /* struct regkey *key = check_ctx_lookup_key(ctx, path); */
+ /* printf("SORTED: %s\n", path); */
+ return true;
+}
+
+static bool read_sd(struct check_ctx *ctx, const char *path, TDB_DATA val)
+{
+ NTSTATUS status;
+ struct regkey *key = check_ctx_lookup_key(ctx, path);
+ /* printf("SD: %s\n", path); */
+
+ status = unmarshall_sec_desc(key, val.dptr, val.dsize, &key->sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to read SD of %s: %s\n",
+ path, nt_errstr(status)));
+ }
+ return true;
+}
+
+static bool srprs_path(const char **ptr, const char* prefix, char sep,
+ const char **ppath)
+{
+ const char *path, *pos = *ptr;
+ if (prefix != NULL) {
+ if (!srprs_str(&pos, prefix, -1) || !srprs_char(&pos, sep) ) {
+ return false;
+ }
+ }
+ path = pos;
+ if ( !srprs_hive(&pos, NULL) ) {
+ return false;
+ }
+ if ( !srprs_eos(&pos) && !srprs_char(&pos, sep) ) {
+ return false;
+ }
+ *ppath = path;
+ *ptr = strchr(pos, '\0');
+ return true;
+}
+
+/* Fixme: this doesn't work in the general multibyte char case.
+ see string_replace()
+*/
+static bool normalize_path_internal(char* path, char sep) {
+ size_t len = strlen(path);
+ const char *orig = talloc_strndup(talloc_tos(), path, len);
+ char *optr = path, *iptr = path;
+ bool changed;
+
+ while (*iptr == sep ) {
+ iptr++;
+ }
+ while (*iptr) {
+ *optr = *iptr;
+ if (*iptr == sep) {
+ while (*iptr == sep) {
+ iptr++;
+ }
+ if (*iptr) {
+ optr++;
+ }
+ } else {
+ iptr++;
+ optr++;
+ }
+ }
+ *optr = '\0';
+
+ if (!strupper_m(path)) {
+ talloc_free(discard_const(orig));
+ return false;
+ }
+ changed = (strcmp(orig, path) != 0);
+ talloc_free(discard_const(orig));
+ return changed;
+}
+
+static bool normalize_path(char* path, char sep) {
+ static const char* SEPS = "\\/";
+ char* firstsep = strpbrk(path, SEPS);
+ bool wrong_sep = (firstsep && (*firstsep != sep));
+
+ assert (strchr(SEPS, sep));
+
+ if (wrong_sep) {
+ string_replace(path, *firstsep, sep);
+ }
+ return normalize_path_internal(path, sep) || wrong_sep;
+}
+
+static int check_tdb_action(struct db_record *rec, void *check_ctx)
+{
+ struct check_ctx *ctx = (struct check_ctx*)check_ctx;
+ TALLOC_CTX *frame = talloc_stackframe();
+ TDB_DATA val = dbwrap_record_get_value(rec);
+ TDB_DATA rec_key = dbwrap_record_get_key(rec);
+ char *key;
+ bool invalid_path = false;
+ bool once_more;
+ bool first_iter = true;
+
+ if (!tdb_data_is_cstr(rec_key)) {
+ printf("Key is not zero terminated: \"%.*s\"\ntry to go on.\n",
+ (int)rec_key.dsize, rec_key.dptr);
+ invalid_path = true;
+ }
+ key = talloc_strndup(frame, (char*)rec_key.dptr, rec_key.dsize);
+
+ do {
+ const char *path, *pos = key;
+ once_more = false;
+
+ if (srprs_str(&pos, "INFO/", -1)) {
+ if ( read_info(ctx, pos, val) ) {
+ break;
+ }
+ invalid_path = true;
+ /* ask: mark invalid */
+ } else if (srprs_str(&pos, "__db_sequence_number__", -1)) {
+ printf("Skip key: \"%.*s\"\n",
+ (int)rec_key.dsize, rec_key.dptr);
+ /* skip: do nothing + break */
+ break;
+
+ } else if (normalize_path(key, ctx->sep)) {
+ printf("Unnormal key: \"%.*s\"\n",
+ (int)rec_key.dsize, rec_key.dptr);
+ printf("Normalize to: \"%s\"\n", key);
+ invalid_path = true;
+ } else if (srprs_path(&pos, NULL,
+ ctx->sep, &path))
+ {
+ read_subkeys(ctx, path, val, invalid_path);
+ break;
+ } else if (srprs_path(&pos, REG_VALUE_PREFIX,
+ ctx->sep, &path))
+ {
+ read_values(ctx, path, val);
+ break;
+ } else if (srprs_path(&pos, REG_SECDESC_PREFIX,
+ ctx->sep, &path))
+ {
+ read_sd(ctx, path, val);
+ break;
+ } else if (srprs_path(&pos, REG_SORTED_SUBKEYS_PREFIX,
+ ctx->sep, &path))
+ {
+ if (!read_sorted(ctx, path, val)) {
+ /* delete: mark invalid + break */
+ printf("Invalid sorted subkeys for: \"%s\"\n", path);
+ invalid_path = true;
+ key = NULL;
+ }
+ break;
+ } else {
+ printf("Unrecognized key: \"%.*s\"\n",
+ (int)rec_key.dsize, rec_key.dptr);
+ invalid_path = true;
+ }
+
+ if (invalid_path) {
+ unsigned char action;
+ if (ctx->opt.output == NULL) {
+ action = first_iter ? 'r' : 's';
+ } else if (ctx->opt.automatic) {
+ action = first_iter ? 'r' : 'd';
+ } else if (ctx->auto_action != '\0') {
+ action = ctx->auto_action;
+ } else {
+ action = interact_prompt("[s]kip,[S]kip all,"
+ "[d]elete,[D]elete all"
+ ",[e]dit,[r]etry"
+ , "sder",
+ ctx->default_action);
+ }
+ if (isupper(action)) {
+ action = tolower(action);
+ ctx->auto_action = action;
+ }
+ ctx->default_action = action;
+ switch (action) {
+ case 's': /* skip */
+ invalid_path = false;
+ break;
+ case 'd': /* delete */
+ invalid_path = true;
+ key = NULL;
+ break;
+ case 'e': /* edit */ {
+ char *p = interact_edit(frame, key);
+ if (p) {
+ talloc_free(key);
+ key = p;
+ }
+ FALL_THROUGH;
+ }
+ case 'r': /* retry */
+ once_more = true;
+ break;
+ }
+ }
+ first_iter = false;
+ } while (once_more);
+
+ if (invalid_path) {
+ dbwrap_store(ctx->del, rec_key, string_term_tdb_data(key), 0);
+ }
+
+ talloc_free(frame);
+ return 0;
+}
+
+static bool get_version(struct check_ctx *ctx) {
+ static const uint32_t curr_version = REGDB_CODE_VERSION;
+ uint32_t version = ctx->opt.version ? ctx->opt.version : curr_version;
+ uint32_t info_version = 0;
+ NTSTATUS status;
+
+ status = dbwrap_fetch_uint32_bystring(ctx->idb, "INFO/version",
+ &info_version);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Warning: no INFO/version found!\n");
+ /* info_version = guess_version(ctx); */
+ }
+
+ if (ctx->opt.version) {
+ version = ctx->opt.version;
+ } else if (ctx->opt.implicit_db) {
+ version = curr_version;
+ } else {
+ version = info_version;
+ }
+
+ if (!version) {
+ printf("Couldn't determine registry format version, "
+ "specify with --reg-version\n");
+ return false;
+ }
+
+
+ if ( version != info_version ) {
+ if (ctx->opt.force || !ctx->opt.repair) {
+ printf("Warning: overwrite registry format "
+ "version %d with %d\n", info_version, version);
+ } else {
+ printf("Warning: found registry format version %d but "
+ "expected %d, use --force to proceed.\n", info_version, version);
+ return false;
+ }
+ }
+
+ ctx->version = version;
+ ctx->sep = (version > 1) ? '\\' : '/';
+
+ return true;
+}
+
+static bool
+dbwrap_store_verbose(struct db_context *db, const char *key, TDB_DATA nval)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
+ TDB_DATA oval;
+ NTSTATUS status;
+
+ status = dbwrap_fetch_bystring(db, mem_ctx, key, &oval);
+ if (NT_STATUS_IS_OK(status)) {
+ if (tdb_data_equal(nval, oval)) {
+ goto done;
+ }
+ printf("store %s:\n overwrite: %s\n with: %s\n", key,
+ tdb_data_string(mem_ctx, oval),
+ tdb_data_string(mem_ctx, nval));
+
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ printf("store %s:\n write: %s\n", key,
+ tdb_data_string(mem_ctx, nval));
+ } else {
+ printf ("store %s:\n failed to fetch old value: %s\n", key,
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dbwrap_store_bystring(db, key, nval, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf ("store %s failed: %s\n", key, nt_errstr(status));
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return NT_STATUS_IS_OK(status);
+}
+
+static bool
+dbwrap_store_uint32_verbose(struct db_context *db, const char *key, uint32_t nval)
+{
+ uint32_t oval;
+ NTSTATUS status;
+
+ status = dbwrap_fetch_uint32_bystring(db, key, &oval);
+ if (NT_STATUS_IS_OK(status)) {
+ if (nval == oval) {
+ goto done;
+ }
+ printf("store %s:\n overwrite: %d\n with: %d\n", key,
+ (int)oval, (int)nval);
+
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ printf("store %s:\n write: %d\n", key, (int)nval);
+ } else {
+ printf ("store %s:\n failed to fetch old value: %s\n", key,
+ nt_errstr(status));
+ goto done;
+ }
+
+ status = dbwrap_store_uint32_bystring(db, key, nval);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf ("store %s failed: %s\n", key, nt_errstr(status));
+ }
+
+done:
+ return NT_STATUS_IS_OK(status);
+}
+
+static int cmp_keynames(char **p1, char **p2)
+{
+ return strcasecmp_m(*p1, *p2);
+}
+
+static bool
+write_subkeylist(struct db_context *db, struct regkey *key, char sep)
+{
+ cbuf *buf = cbuf_new(talloc_tos());
+ size_t i;
+ bool ret;
+
+ cbuf_putdw(buf, key->nsubkeys);
+
+ for (i=0; i < key->nsubkeys; i++) {
+ struct regkey *subkey = key->subkeys[i];
+ const char *name = subkey->name;
+ if (name == NULL) {
+ printf("Warning: no explicit name for key %s\n",
+ subkey->path);
+ name = strrchr_m(subkey->path, sep);
+ assert(name);
+ name ++;
+ }
+ cbuf_puts(buf, name, -1);
+ cbuf_putc(buf, '\0');
+ }
+
+ ret = dbwrap_store_verbose(db, key->path, cbuf_make_tdb_data(buf));
+
+ talloc_free(buf);
+ return ret;
+}
+
+static bool write_sorted(struct db_context *db, struct regkey *key, char sep)
+{
+ cbuf *buf = cbuf_new(talloc_tos());
+ char *path;
+ size_t i;
+ bool ret = false;
+ char **sorted = talloc_zero_array(buf, char*, key->nsubkeys);
+ int offset = (1 + key->nsubkeys) * sizeof(uint32_t);
+
+ for (i=0; i < key->nsubkeys; i++) {
+ sorted[i] = talloc_strdup_upper(sorted, key->subkeys[i]->name);
+ }
+ TYPESAFE_QSORT(sorted, key->nsubkeys, cmp_keynames);
+
+ cbuf_putdw(buf, key->nsubkeys);
+ for (i=0; i < key->nsubkeys; i++) {
+ cbuf_putdw(buf, offset);
+ offset += strlen(sorted[i]) + 1;
+ }
+ for (i=0; i < key->nsubkeys; i++) {
+ cbuf_puts(buf, sorted[i], -1);
+ cbuf_putc(buf, '\0');
+ }
+
+ path = talloc_asprintf(buf, "%s%c%s", REG_SORTED_SUBKEYS_PREFIX, sep,
+ key->path);
+ if (path == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
+done:
+ talloc_free(buf);
+ return ret;
+}
+
+static bool write_values(struct db_context *db, struct regkey *key, char sep)
+{
+ cbuf *buf = cbuf_new(talloc_tos());
+ char *path;
+ size_t i;
+ bool ret = false;
+
+ cbuf_putdw(buf, key->nvalues);
+ for (i=0; i < key->nvalues; i++) {
+ struct regval *val = key->values[i];
+ cbuf_puts(buf, val->name, -1);
+ cbuf_putc(buf, '\0');
+ cbuf_putdw(buf, val->type);
+ cbuf_putdw(buf, val->data.length);
+ cbuf_puts(buf, (void*)val->data.data, val->data.length);
+ }
+
+ path = talloc_asprintf(buf, "%s%c%s", REG_VALUE_PREFIX, sep, key->path);
+ if (path == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf));
+done:
+ talloc_free(buf);
+ return ret;
+}
+
+static bool write_sd(struct db_context *db, struct regkey *key, char sep)
+{
+ TDB_DATA sd;
+ NTSTATUS status;
+ char *path;
+ bool ret = false;
+ TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
+
+ status = marshall_sec_desc(mem_ctx, key->sd, &sd.dptr, &sd.dsize);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("marshall sec desc %s failed: %s\n",
+ key->path, nt_errstr(status));
+ goto done;
+ }
+ path = talloc_asprintf(mem_ctx, "%s%c%s", REG_SECDESC_PREFIX,
+ sep, key->path);
+ if (path == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ ret = dbwrap_store_verbose(db, path, sd);
+done:
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+
+static int check_write_db_action(struct db_record *rec, void *check_ctx)
+{
+ struct check_ctx *ctx = (struct check_ctx*)check_ctx;
+ TDB_DATA rec_val = dbwrap_record_get_value(rec);
+ struct regkey *key = *(struct regkey**)rec_val.dptr;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ /* write subkeylist */
+ if ((ctx->version > 2) || (key->nsubkeys > 0) || (key->has_subkeylist)) {
+ write_subkeylist(ctx->odb, key, ctx->sep);
+ }
+
+ /* write sorted subkeys */
+ if ((ctx->version < 3) && (key->nsubkeys > 0)) {
+ write_sorted(ctx->odb, key, ctx->sep);
+ }
+
+ /* write value list */
+ if (key->nvalues > 0) {
+ write_values(ctx->odb, key, ctx->sep);
+ }
+
+ /* write sd */
+ if (key->sd) {
+ write_sd(ctx->odb, key, ctx->sep);
+ }
+
+ talloc_free(frame);
+ return 0;
+}
+
+static int fix_tree_action(struct db_record *rec, void *check_ctx)
+{
+ struct check_ctx *ctx = (struct check_ctx*)check_ctx;
+ TDB_DATA rec_key = dbwrap_record_get_key(rec);
+ TDB_DATA rec_val = dbwrap_record_get_value(rec);
+ struct regkey* key = *(struct regkey**)rec_val.dptr;
+ if (ctx->opt.verbose) {
+ printf("Check Tree: %s\n", key->path);
+ }
+
+ assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
+
+ /* assert(dbwrap_exists(ctx->db, string_term_tdb_data(key->path)) */
+ /* == key->exists); */
+
+ if (key->needs_update) {
+ printf("Update key: \"%s\"\n", key->path);
+ if ((ctx->version > 2) || (key->nsubkeys > 0)) {
+ write_subkeylist(ctx->odb, key, ctx->sep);
+ }
+ if ((ctx->version <= 2) && (key->nsubkeys > 0)) {
+ write_sorted(ctx->odb, key, ctx->sep);
+ }
+ if (key->nvalues > 0) {
+ write_values(ctx->odb, key, ctx->sep);
+ }
+ if (key->sd) {
+ write_sd(ctx->odb, key, ctx->sep);
+ }
+ } else if (!key->has_subkeylist) {
+ if ((ctx->version > 2) || (key->nsubkeys > 0)) {
+ printf("Missing subkeylist: %s\n", key->path);
+ write_subkeylist(ctx->odb, key, ctx->sep);
+ }
+ }
+
+ if (key->name == NULL && key->parent->has_subkeylist) {
+ printf("Key not referenced by the its parents subkeylist: %s\n",
+ key->path);
+ write_subkeylist(ctx->odb, key->parent, ctx->sep);
+ }
+
+/* XXX check that upcase(name) matches last part of path ??? */
+
+ return 0;
+}
+
+
+/* give the same warnings as fix_tree_action */
+static int check_tree_action(struct db_record *rec, void *check_ctx)
+{
+ struct check_ctx *ctx = (struct check_ctx*)check_ctx;
+ TDB_DATA rec_key = dbwrap_record_get_key(rec);
+ TDB_DATA rec_val = dbwrap_record_get_value(rec);
+ struct regkey* key = *(struct regkey**)rec_val.dptr;
+ if (ctx->opt.verbose) {
+ printf("Check Tree: %s\n", key->path);
+ }
+
+ assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0);
+
+ if (!key->has_subkeylist) {
+ if ((ctx->version > 2) || (key->nsubkeys > 0)) {
+ printf("Missing subkeylist: %s\n", key->path);
+ }
+ }
+
+ if (key->name == NULL && key->parent->has_subkeylist) {
+ printf("Key not referenced by the its parents subkeylist: %s\n",
+ key->path);
+ }
+
+ return 0;
+}
+
+static int delete_invalid_action(struct db_record *rec, void* check_ctx)
+{
+ NTSTATUS status;
+ struct check_ctx *ctx = (struct check_ctx*)check_ctx;
+ TDB_DATA rec_key = dbwrap_record_get_key(rec);
+ TDB_DATA rec_val = dbwrap_record_get_value(rec);
+
+
+ printf("Delete key: \"%.*s\"",(int)rec_key.dsize, rec_key.dptr);
+ if (rec_val.dsize > 0) {
+ printf(" in favour of \"%s\"\n", rec_val.dptr);
+ } else {
+ putc('\n', stdout);
+ }
+
+ status = dbwrap_delete(ctx->odb, rec_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("delete key \"%.*s\" failed!\n",
+ (int)rec_key.dsize, rec_key.dptr);
+ return -1;
+ }
+ return 0;
+}
+
+static bool check_ctx_check_tree(struct check_ctx *ctx) {
+ NTSTATUS status;
+
+ status = dbwrap_traverse(ctx->reg, check_tree_action, ctx, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("check traverse failed: %s\n",
+ nt_errstr(status)));
+ return false;
+ }
+ return true;
+}
+static bool check_ctx_fix_inplace(struct check_ctx *ctx) {
+ NTSTATUS status;
+ status = dbwrap_traverse(ctx->reg, fix_tree_action, ctx, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("fix traverse failed: %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ status = dbwrap_traverse(ctx->del, delete_invalid_action, ctx, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("delete traverse failed: %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ if (!dbwrap_store_uint32_verbose(ctx->odb, "INFO/version", ctx->version)) {
+ DEBUG(0, ("storing version failed: %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_ctx_write_new_db(struct check_ctx *ctx) {
+ NTSTATUS status;
+
+ assert(ctx->odb);
+
+ if (ctx->opt.wipe) {
+ int ret = dbwrap_wipe(ctx->odb);
+ if (ret != 0) {
+ DEBUG(0, ("wiping %s failed\n", ctx->opt.output));
+ return false;
+ }
+ }
+
+ status = dbwrap_traverse(ctx->reg, check_write_db_action, ctx, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("traverse2 failed: %s\n", nt_errstr(status)));
+ return false;
+ }
+
+ status = dbwrap_store_uint32_bystring(ctx->odb, "INFO/version",
+ ctx->version);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("write version failed: %s\n", nt_errstr(status)));
+ return false;
+ }
+ return true;
+}
+
+int net_registry_check_db(const char *name, const struct check_options *opt)
+{
+ NTSTATUS status;
+ int ret = -1;
+ struct check_ctx *ctx = check_ctx_create(talloc_tos(), name, opt);
+ if (ctx==NULL) {
+ goto done;
+ }
+
+ d_printf("Check database: %s\n", name);
+
+ /* 1. open output RW */
+ if (!check_ctx_open_output(ctx)) {
+ goto done;
+ }
+
+ /* 2. open input RO */
+ if (!check_ctx_open_input(ctx)) {
+ goto done;
+ }
+
+ if (opt->lock && !check_ctx_transaction_start(ctx)) {
+ goto done;
+ }
+
+ if (!get_version(ctx)) {
+ goto done;
+ }
+
+ status = dbwrap_traverse_read(ctx->idb, check_tdb_action, ctx, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("check traverse failed: %s\n", nt_errstr(status)));
+ goto done;
+ }
+
+ if (!opt->lock && !check_ctx_transaction_start(ctx)) {
+ goto done;
+ }
+
+ if (ctx->opt.repair && !ctx->opt.wipe) {
+ if (!check_ctx_fix_inplace(ctx)) {
+ goto done;
+ }
+ } else {
+ if (!check_ctx_check_tree(ctx)) {
+ goto done;
+ }
+ if (ctx->odb) {
+ if (!check_ctx_write_new_db(ctx)) {
+ goto done;
+ }
+ }
+ }
+ ret = 0;
+done:
+ check_ctx_transaction_stop(ctx, ret == 0);
+
+ talloc_free(ctx);
+ return ret;
+}
+
+/*Local Variables:*/
+/*mode: c*/
+/*End:*/
diff --git a/source3/utils/net_registry_check.h b/source3/utils/net_registry_check.h
new file mode 100644
index 0000000..6ed68d8
--- /dev/null
+++ b/source3/utils/net_registry_check.h
@@ -0,0 +1,52 @@
+/*
+ * Samba Unix/Linux SMB client library
+ *
+ * Copyright (C) Gregor Beck 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * @brief Check the registry database.
+ * @author Gregor Beck <gb@sernet.de>
+ * @date Jun 2011
+ */
+
+#ifndef NET_REGISTRY_CHECK_H
+#define NET_REGISTRY_CHECK_H
+
+#include <stdbool.h>
+
+struct net_context;
+
+struct check_options {
+ bool test;
+ bool verbose;
+ bool lock;
+ bool automatic;
+ bool force;
+ bool repair;
+ int version;
+ const char *output;
+ bool wipe;
+ bool implicit_db;
+};
+
+int net_registry_check_db(const char* db, const struct check_options* opts);
+
+#endif /* NET_REGISTRY_CHECK_H */
+
+/*Local Variables:*/
+/*mode: c*/
+/*End:*/
diff --git a/source3/utils/net_registry_util.c b/source3/utils/net_registry_util.c
new file mode 100644
index 0000000..50b8a8a
--- /dev/null
+++ b/source3/utils/net_registry_util.c
@@ -0,0 +1,177 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * registry utility functions
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "registry.h"
+#include "utils/net_registry_util.h"
+#include "utils/net.h"
+#include "../libcli/registry/util_reg.h"
+
+void print_registry_key(const char *keyname, NTTIME *modtime)
+{
+ const char *ts = _("None");
+ char *freeme = NULL;
+
+ if (modtime != 0) {
+ freeme = http_timestring(talloc_tos(),
+ nt_time_to_unix(*modtime));
+ ts = freeme;
+ }
+
+ d_printf(_("Keyname = %s\n"), keyname);
+ d_printf(_("Modtime = %s\n"), ts);
+ d_printf("\n");
+
+ TALLOC_FREE(freeme);
+}
+
+void print_registry_value(const struct registry_value *valvalue, bool raw)
+{
+ if (!raw) {
+ d_printf(_("Type = %s\n"),
+ str_regtype(valvalue->type));
+ }
+ switch(valvalue->type) {
+ case REG_DWORD: {
+ uint32_t v = 0;
+ if (valvalue->data.length >= 4) {
+ v = IVAL(valvalue->data.data, 0);
+ }
+ if (!raw) {
+ d_printf(_("Value = "));
+ }
+ d_printf("%u\n", v);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *s;
+
+ if (!pull_reg_sz(talloc_tos(), &valvalue->data, &s)) {
+ break;
+ }
+ if (!raw) {
+ d_printf(_("Value = \""));
+ }
+ d_printf("%s", s);
+ if (!raw) {
+ d_printf("\"");
+ }
+ d_printf("\n");
+ break;
+ }
+ case REG_MULTI_SZ: {
+ uint32_t j;
+ const char **a;
+
+ if (!pull_reg_multi_sz(talloc_tos(), &valvalue->data, &a)) {
+ break;
+ }
+ for (j = 0; a[j] != NULL; j++) {
+ if (!raw) {
+ d_printf(_("Value[%3.3d] = \""), j);
+ }
+ d_printf("%s", a[j]);
+ if (!raw) {
+ d_printf("\"");
+ }
+ d_printf("\n");
+ }
+ break;
+ }
+ case REG_BINARY:
+ if (!raw) {
+ d_printf(_("Value = "));
+ }
+ d_printf(_("%d bytes\n"), (int)valvalue->data.length);
+ break;
+ default:
+ if (!raw) {
+ d_printf(_("Value = "));
+ }
+ d_printf(_("<unprintable>\n"));
+ break;
+ }
+}
+
+void print_registry_value_with_name(const char *valname,
+ const struct registry_value *valvalue)
+{
+ d_printf(_("Valuename = %s\n"), valname);
+ print_registry_value(valvalue, false);
+ d_printf("\n");
+}
+
+/**
+ * Split path into hive name and subkeyname
+ * normalizations performed:
+ * - if the path contains no '\\' characters,
+ * assume that the legacy format of using '/'
+ * as a separator is used and convert '/' to '\\'
+ * - strip trailing '\\' chars
+ */
+WERROR split_hive_key(TALLOC_CTX *ctx, const char *path, char **hivename,
+ char **subkeyname)
+{
+ char *p;
+ const char *tmp_subkeyname;
+
+ if ((path == NULL) || (hivename == NULL) || (subkeyname == NULL)) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (strlen(path) == 0) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ if (strchr(path, '\\') == NULL) {
+ *hivename = talloc_string_sub(ctx, path, "/", "\\");
+ } else {
+ *hivename = talloc_strdup(ctx, path);
+ }
+
+ if (*hivename == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* strip trailing '\\' chars */
+ p = strrchr(*hivename, '\\');
+ while ((p != NULL) && (p[1] == '\0')) {
+ *p = '\0';
+ p = strrchr(*hivename, '\\');
+ }
+
+ p = strchr(*hivename, '\\');
+
+ if ((p == NULL) || (*p == '\0')) {
+ /* just the hive - no subkey given */
+ tmp_subkeyname = "";
+ } else {
+ *p = '\0';
+ tmp_subkeyname = p+1;
+ }
+ *subkeyname = talloc_strdup(ctx, tmp_subkeyname);
+ if (*subkeyname == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ return WERR_OK;
+}
diff --git a/source3/utils/net_registry_util.h b/source3/utils/net_registry_util.h
new file mode 100644
index 0000000..61fd834
--- /dev/null
+++ b/source3/utils/net_registry_util.h
@@ -0,0 +1,41 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * registry utility functions
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __NET_REGISTRY_UTIL_H__
+#define __NET_REGISTRY_UTIL_H__
+
+void print_registry_key(const char *keyname, NTTIME *modtime);
+
+void print_registry_value(const struct registry_value *valvalue, bool raw);
+
+void print_registry_value_with_name(const char *valname,
+ const struct registry_value *valvalue);
+
+/**
+ * Split path into hive name and subkeyname
+ * normalizations performed:
+ * - convert '/' to '\\'
+ * - strip trailing '\\' chars
+ */
+WERROR split_hive_key(TALLOC_CTX *ctx, const char *path, char **hivename,
+ char **subkeyname);
+
+#endif
diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c
new file mode 100644
index 0000000..2a12b1a
--- /dev/null
+++ b/source3/utils/net_rpc.c
@@ -0,0 +1,8408 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2004,2008 Guenther Deschner (gd@samba.org)
+ Copyright (C) 2005 Jeremy Allison (jra@samba.org)
+ Copyright (C) 2006 Jelmer Vernooij (jelmer@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "libsmb/namequery.h"
+#include "rpc_client/cli_pipe.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "rpc_client/cli_samr.h"
+#include "rpc_client/init_samr.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../librpc/gen_ndr/ndr_netlogon_c.h"
+#include "../librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "../librpc/gen_ndr/ndr_spoolss.h"
+#include "../librpc/gen_ndr/ndr_initshutdown_c.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "secrets.h"
+#include "lib/netapi/netapi.h"
+#include "lib/netapi/netapi_net.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "clirap2.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "passdb.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "libsmb/dsgetdcname.h"
+#include "lib/util/string_wrappers.h"
+
+static int net_mode_share;
+static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask);
+
+/**
+ * @file net_rpc.c
+ *
+ * @brief RPC based subcommands for the 'net' utility.
+ *
+ * This file should contain much of the functionality that used to
+ * be found in rpcclient, except that the commands should change
+ * less often, and the functionality should be sane (the user is not
+ * expected to know a rid/sid before they conduct an operation etc.)
+ *
+ * @todo Perhaps eventually these should be split out into a number
+ * of files, as this could get quite big.
+ **/
+
+
+/**
+ * Many of the RPC functions need the domain sid. This function gets
+ * it at the start of every run
+ *
+ * @param cli A cli_state already connected to the remote machine
+ *
+ * @return The Domain SID of the remote machine.
+ **/
+
+NTSTATUS net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx,
+ struct dom_sid **domain_sid,
+ const char **domain_name)
+{
+ struct rpc_pipe_client *lsa_pipe = NULL;
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ union lsa_PolicyInformation *info = NULL;
+ struct dcerpc_binding_handle *b;
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not initialise lsa pipe\n"));
+ return status;
+ }
+
+ b = lsa_pipe->binding_handle;
+
+ status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_policy %s: %s\n",
+ _("failed"),
+ nt_errstr(status));
+ return status;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ d_fprintf(stderr, "lsaquery %s: %s\n",
+ _("failed"),
+ nt_errstr(status));
+ return status;
+ }
+
+ *domain_name = info->account_domain.name.string;
+ *domain_sid = info->account_domain.sid;
+
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+ TALLOC_FREE(lsa_pipe);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Run a single RPC command, from start to finish.
+ *
+ * @param pipe_name the pipe to connect to (usually a PIPE_ constant)
+ * @param conn_flag a NET_FLAG_ combination. Passed to
+ * net_make_ipc_connection.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ * @return A shell status integer (0 for success).
+ */
+
+int run_rpc_command(struct net_context *c,
+ struct cli_state *cli_arg,
+ const struct ndr_interface_table *table,
+ int conn_flags,
+ rpc_command_fn fn,
+ int argc,
+ const char **argv)
+{
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ struct dom_sid *domain_sid;
+ const char *domain_name;
+ int ret = -1;
+
+ /* make use of cli_state handed over as an argument, if possible */
+ if (!cli_arg) {
+ nt_status = net_make_ipc_connection(c, conn_flags, &cli);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("failed to make ipc connection: %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+ } else {
+ cli = cli_arg;
+ }
+
+ if (!cli) {
+ return -1;
+ }
+
+ /* Create mem_ctx */
+
+ if (!(mem_ctx = talloc_init("run_rpc_command"))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ goto fail;
+ }
+
+ nt_status = net_get_remote_domain_sid(cli, mem_ctx, &domain_sid,
+ &domain_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ goto fail;
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (lp_client_schannel()
+ && (ndr_syntax_id_equal(&table->syntax_id,
+ &ndr_table_netlogon.syntax_id))) {
+ const char *remote_name =
+ smbXcli_conn_remote_name(cli->conn);
+ const struct sockaddr_storage *remote_sockaddr =
+ smbXcli_conn_remote_sockaddr(cli->conn);
+
+ /* Always try and create an schannel netlogon pipe. */
+ TALLOC_FREE(c->netlogon_creds);
+ nt_status = cli_rpc_pipe_open_schannel(
+ cli, c->msg_ctx, table, NCACN_NP,
+ domain_name,
+ remote_name,
+ remote_sockaddr,
+ &pipe_hnd, c, &c->netlogon_creds);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise schannel netlogon pipe. Error was %s\n",
+ nt_errstr(nt_status) ));
+ goto fail;
+ }
+ } else {
+ if (conn_flags & NET_FLAGS_SEAL) {
+ nt_status = cli_rpc_pipe_open_with_creds(
+ cli, table,
+ (conn_flags & NET_FLAGS_TCP) ?
+ NCACN_IP_TCP : NCACN_NP,
+ DCERPC_AUTH_TYPE_NTLMSSP,
+ DCERPC_AUTH_LEVEL_PRIVACY,
+ smbXcli_conn_remote_name(cli->conn),
+ smbXcli_conn_remote_sockaddr(cli->conn),
+ c->creds, &pipe_hnd);
+ } else {
+ nt_status = cli_rpc_pipe_open_noauth(
+ cli, table,
+ &pipe_hnd);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise pipe %s. Error was %s\n",
+ table->name,
+ nt_errstr(nt_status) ));
+ goto fail;
+ }
+ }
+ }
+
+ nt_status = fn(c, domain_sid, domain_name, cli, pipe_hnd, mem_ctx, argc, argv);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status)));
+ } else {
+ ret = 0;
+ DEBUG(5, ("rpc command function succeeded\n"));
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (pipe_hnd) {
+ TALLOC_FREE(pipe_hnd);
+ }
+ }
+
+fail:
+ /* close the connection only if it was opened here */
+ if (!cli_arg) {
+ cli_shutdown(cli);
+ }
+
+ talloc_destroy(mem_ctx);
+ return ret;
+}
+
+/**
+ * Force a change of the trust account password.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_changetrustpw_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS status;
+ const char *dcname = NULL;
+
+ if (cli == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ dcname = smbXcli_conn_remote_name(cli->conn);
+
+ status = trust_pw_change(c->netlogon_creds,
+ c->msg_ctx,
+ pipe_hnd->binding_handle,
+ c->opt_target_workgroup,
+ dcname,
+ true); /* force */
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Failed to change machine account password: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Force a change of the trust account password.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+int net_rpc_changetrustpw(struct net_context *c, int argc, const char **argv)
+{
+ int conn_flags = NET_FLAGS_PDC;
+
+ if (!c->opt_user_specified && !c->opt_kerberos) {
+ conn_flags |= NET_FLAGS_ANONYMOUS;
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc changetrustpw\n"
+ " %s\n",
+ _("Usage:"),
+ _("Change the machine trust password"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon,
+ conn_flags,
+ rpc_changetrustpw_internals,
+ argc, argv);
+}
+
+/**
+ * Join a domain, the old way. This function exists to allow
+ * the message to be displayed when oldjoin was explicitly
+ * requested, but not when it was implied by "net rpc join".
+ *
+ * This uses 'machinename' as the initial password, and changes it.
+ *
+ * The password should be created with 'server manager' or equiv first.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int net_rpc_oldjoin(struct net_context *c, int argc, const char **argv)
+{
+ struct libnet_JoinCtx *r = NULL;
+ TALLOC_CTX *mem_ctx;
+ WERROR werr;
+ const char *domain = lp_workgroup(); /* FIXME */
+ bool modify_config = lp_config_backend_is_registry();
+ enum netr_SchannelType sec_chan_type;
+ char *pw = NULL;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc oldjoin\n"
+ " Join a domain the old way\n");
+ return 0;
+ }
+
+ net_warn_member_options();
+
+ mem_ctx = talloc_init("net_rpc_oldjoin");
+ if (!mem_ctx) {
+ return -1;
+ }
+
+ werr = libnet_init_JoinCtx(mem_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /*
+ check what type of join - if the user wants to join as
+ a BDC, the server must agree that we are a BDC.
+ */
+ if (argc >= 0) {
+ sec_chan_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_chan_type = get_sec_channel_type(NULL);
+ }
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ werr = WERR_ACCESS_DENIED;
+ goto fail;
+ }
+
+ pw = talloc_strndup(r, lp_netbios_name(), 14);
+ if (pw == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+
+ r->in.msg_ctx = c->msg_ctx;
+ r->in.domain_name = domain;
+ r->in.secure_channel_type = sec_chan_type;
+ r->in.dc_name = c->opt_host;
+ r->in.admin_account = "";
+ r->in.admin_password = strlower_talloc(r, pw);
+ if (r->in.admin_password == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ r->in.debug = true;
+ r->in.modify_config = modify_config;
+ r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_JOIN_UNSECURE |
+ WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED;
+
+ werr = libnet_Join(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* Check the short name of the domain */
+
+ if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
+ d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
+ d_printf("domain name obtained from the server.\n");
+ d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
+ d_printf("You should set \"workgroup = %s\" in %s.\n",
+ r->out.netbios_domain_name, get_dyn_CONFIGFILE());
+ }
+
+ d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
+
+ if (r->out.dns_domain_name) {
+ d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
+ r->out.dns_domain_name);
+ } else {
+ d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
+ r->out.netbios_domain_name);
+ }
+
+ /* print out informative error string in case there is one */
+ if (r->out.error_string != NULL) {
+ d_printf("%s\n", r->out.error_string);
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+
+fail:
+ if (c->opt_flags & NET_FLAGS_EXPECT_FALLBACK) {
+ goto cleanup;
+ }
+
+ /* issue an overall failure message at the end. */
+ d_fprintf(stderr, _("Failed to join domain: %s\n"),
+ r && r->out.error_string ? r->out.error_string :
+ get_friendly_werror_msg(werr));
+
+cleanup:
+ TALLOC_FREE(mem_ctx);
+
+ return -1;
+}
+
+/**
+ * check that a join is OK
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+int net_rpc_testjoin(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ const char *domain = c->opt_target_workgroup;
+ const char *dc = c->opt_host;
+
+ if (c->display_usage) {
+ d_printf("Usage\n"
+ "net rpc testjoin\n"
+ " Test if a join is OK\n");
+ return 0;
+ }
+
+ net_warn_member_options();
+
+ mem_ctx = talloc_init("net_rpc_testjoin");
+ if (!mem_ctx) {
+ return -1;
+ }
+
+ if (!dc) {
+ struct netr_DsRGetDCNameInfo *info;
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ status = dsgetdcname(mem_ctx,
+ c->msg_ctx,
+ domain,
+ NULL,
+ NULL,
+ DS_RETURN_DNS_NAME,
+ &info);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ dc = strip_hostname(info->dc_unc);
+ }
+
+ /* Display success or failure */
+ status = libnet_join_ok(c->msg_ctx,
+ c->opt_workgroup,
+ dc,
+ c->opt_kerberos);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,"Join to domain '%s' is not valid: %s\n",
+ domain, nt_errstr(status));
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ printf("Join to '%s' is OK\n",domain);
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+/**
+ * Join a domain using the administrator username and password
+ *
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped. Currently not used.
+ * @return A shell status integer (0 for success)
+ *
+ **/
+
+static int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv)
+{
+ struct libnet_JoinCtx *r = NULL;
+ TALLOC_CTX *mem_ctx;
+ WERROR werr;
+ const char *domain = lp_workgroup(); /* FIXME */
+ bool modify_config = lp_config_backend_is_registry();
+ enum netr_SchannelType sec_chan_type;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc join\n"
+ " Join a domain the new way\n");
+ return 0;
+ }
+
+ net_warn_member_options();
+
+ mem_ctx = talloc_init("net_rpc_join_newstyle");
+ if (!mem_ctx) {
+ return -1;
+ }
+
+ werr = libnet_init_JoinCtx(mem_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /*
+ check what type of join - if the user wants to join as
+ a BDC, the server must agree that we are a BDC.
+ */
+ if (argc >= 0) {
+ sec_chan_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_chan_type = get_sec_channel_type(NULL);
+ }
+
+ if (!c->msg_ctx) {
+ d_fprintf(stderr, _("Could not initialise message context. "
+ "Try running as root\n"));
+ werr = WERR_ACCESS_DENIED;
+ goto fail;
+ }
+
+ r->in.msg_ctx = c->msg_ctx;
+ r->in.domain_name = domain;
+ r->in.secure_channel_type = sec_chan_type;
+ r->in.dc_name = c->opt_host;
+ r->in.admin_account = c->opt_user_name;
+ r->in.admin_password = net_prompt_pass(c, c->opt_user_name);
+ r->in.debug = true;
+ r->in.use_kerberos = c->opt_kerberos;
+ r->in.modify_config = modify_config;
+ r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
+ WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
+
+ werr = libnet_Join(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* Check the short name of the domain */
+
+ if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) {
+ d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE());
+ d_printf("domain name obtained from the server.\n");
+ d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name);
+ d_printf("You should set \"workgroup = %s\" in %s.\n",
+ r->out.netbios_domain_name, get_dyn_CONFIGFILE());
+ }
+
+ d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name);
+
+ if (r->out.dns_domain_name) {
+ d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name,
+ r->out.dns_domain_name);
+ } else {
+ d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name,
+ r->out.netbios_domain_name);
+ }
+
+ /* print out informative error string in case there is one */
+ if (r->out.error_string != NULL) {
+ d_printf("%s\n", r->out.error_string);
+ }
+
+ TALLOC_FREE(mem_ctx);
+
+ return 0;
+
+fail:
+ /* issue an overall failure message at the end. */
+ d_printf("Failed to join domain: %s\n",
+ r && r->out.error_string ? r->out.error_string :
+ get_friendly_werror_msg(werr));
+
+ TALLOC_FREE(mem_ctx);
+
+ return -1;
+}
+
+/**
+ * 'net rpc join' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * Main 'net_rpc_join()' (where the admin username/password is used) is
+ * in net_rpc_join.c.
+ * Try to just change the password, but if that doesn't work, use/prompt
+ * for a username/password.
+ **/
+
+int net_rpc_join(struct net_context *c, int argc, const char **argv)
+{
+ int ret;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc join -U <username>[%%password] <type>\n"
+ " Join a domain\n"
+ " username\tName of the admin user"
+ " password\tPassword of the admin user, will "
+ "prompt if not specified\n"
+ " type\tCan be one of the following:\n"
+ "\t\tMEMBER\tJoin as member server (default)\n"
+ "\t\tBDC\tJoin as BDC\n"
+ "\t\tPDC\tJoin as PDC\n"));
+ return 0;
+ }
+
+ if (lp_server_role() == ROLE_STANDALONE) {
+ d_printf(_("cannot join as standalone machine\n"));
+ return -1;
+ }
+
+ net_warn_member_options();
+
+ if (strlen(lp_netbios_name()) > 15) {
+ d_printf(_("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n"),
+ lp_netbios_name(), (unsigned int)strlen(lp_netbios_name()));
+ return -1;
+ }
+
+ c->opt_flags |= NET_FLAGS_EXPECT_FALLBACK;
+ ret = net_rpc_oldjoin(c, argc, argv);
+ c->opt_flags &= ~NET_FLAGS_EXPECT_FALLBACK;
+ if (ret == 0) {
+ return 0;
+ }
+
+ return net_rpc_join_newstyle(c, argc, argv);
+}
+
+/**
+ * display info about a rpc domain
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_info_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ union samr_DomainInfo *info = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* Get sam policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not connect to SAM: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Could not connect to SAM: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not open domain: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Could not open domain: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 2,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = result;
+ if (NT_STATUS_IS_OK(result)) {
+ struct dom_sid_buf sid_str;
+
+ d_printf(_("Domain Name: %s\n"),
+ info->general.domain_name.string);
+ d_printf(_("Domain SID: %s\n"),
+ dom_sid_str_buf(domain_sid, &sid_str));
+ d_printf(_("Sequence number: %llu\n"),
+ (unsigned long long)info->general.sequence_num);
+ d_printf(_("Num users: %u\n"), info->general.num_users);
+ d_printf(_("Num domain groups: %u\n"),info->general.num_groups);
+ d_printf(_("Num local groups: %u\n"),info->general.num_aliases);
+ }
+
+ done:
+ return status;
+}
+
+/**
+ * 'net rpc info' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_info(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc info\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display information about the domain"));
+ return 0;
+ }
+
+ net_warn_member_options();
+
+ return run_rpc_command(c, NULL, &ndr_table_samr,
+ NET_FLAGS_PDC, rpc_info_internals,
+ argc, argv);
+}
+
+/**
+ * Fetch domain SID into the local secrets.tdb.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_getsid_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dom_sid_buf sid_str;
+
+ d_printf(_("Storing SID %s for Domain %s in secrets.tdb\n"),
+ dom_sid_str_buf(domain_sid, &sid_str),
+ domain_name);
+
+ if (!secrets_store_domain_sid(domain_name, domain_sid)) {
+ DEBUG(0,("Can't store domain SID\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * 'net rpc getsid' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_getsid(struct net_context *c, int argc, const char **argv)
+{
+ int conn_flags = NET_FLAGS_PDC;
+
+ if (!c->opt_user_specified && !c->opt_kerberos) {
+ conn_flags |= NET_FLAGS_ANONYMOUS;
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc getsid\n"
+ " %s\n",
+ _("Usage:"),
+ _("Fetch domain SID into local secrets.tdb"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_samr,
+ conn_flags,
+ rpc_getsid_internals,
+ argc, argv);
+}
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc user'.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_user_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_user_usage(c, argc, argv);
+}
+
+/**
+ * Add a new user to a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_user_add(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct USER_INFO_1 info1;
+ uint32_t parm_error = 0;
+
+ if (argc < 1 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ ZERO_STRUCT(info1);
+
+ info1.usri1_name = argv[0];
+ if (argc == 2) {
+ info1.usri1_password = argv[1];
+ }
+
+ status = NetUserAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error);
+
+ if (status != 0) {
+ d_fprintf(stderr,_("Failed to add user '%s' with error: %s.\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ } else {
+ d_printf(_("Added user '%s'.\n"), argv[0]);
+ }
+
+ return 0;
+}
+
+/**
+ * Rename a user on a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_user_rename(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct USER_INFO_0 u0;
+ uint32_t parm_err = 0;
+
+ if (argc != 2 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ u0.usri0_name = argv[1];
+
+ status = NetUserSetInfo(c->opt_host, argv[0],
+ 0, (uint8_t *)&u0, &parm_err);
+ if (status) {
+ d_fprintf(stderr,
+ _("Failed to rename user from %s to %s - %s\n"),
+ argv[0], argv[1],
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ } else {
+ d_printf(_("Renamed user from %s to %s\n"), argv[0], argv[1]);
+ }
+
+ return status;
+}
+
+/**
+ * Set a user's primary group
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_user_setprimarygroup(struct net_context *c, int argc,
+ const char **argv)
+{
+ NET_API_STATUS status;
+ uint8_t *buffer;
+ struct GROUP_INFO_2 *g2;
+ struct USER_INFO_1051 u1051;
+ uint32_t parm_err = 0;
+
+ if (argc != 2 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ status = NetGroupGetInfo(c->opt_host, argv[1], 2, &buffer);
+ if (status) {
+ d_fprintf(stderr, _("Failed to find group name %s -- %s\n"),
+ argv[1],
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ return status;
+ }
+ g2 = (struct GROUP_INFO_2 *)buffer;
+
+ u1051.usri1051_primary_group_id = g2->grpi2_group_id;
+
+ NetApiBufferFree(buffer);
+
+ status = NetUserSetInfo(c->opt_host, argv[0], 1051,
+ (uint8_t *)&u1051, &parm_err);
+ if (status) {
+ d_fprintf(stderr,
+ _("Failed to set user's primary group %s to %s - "
+ "%s\n"), argv[0], argv[1],
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ } else {
+ d_printf(_("Set primary group of user %s to %s\n"), argv[0],
+ argv[1]);
+ }
+ return status;
+}
+
+/**
+ * Delete a user from a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_user_delete(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ if (argc < 1 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ status = NetUserDel(c->opt_host, argv[0]);
+
+ if (status != 0) {
+ d_fprintf(stderr, _("Failed to delete user '%s' with: %s.\n"),
+ argv[0],
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ return -1;
+ } else {
+ d_printf(_("Deleted user '%s'.\n"), argv[0]);
+ }
+
+ return 0;
+}
+
+/**
+ * Set a user's password on a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_user_password(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ char *prompt = NULL;
+ struct USER_INFO_1003 u1003;
+ uint32_t parm_err = 0;
+ int ret;
+
+ if (argc < 1 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (argv[1]) {
+ u1003.usri1003_password = argv[1];
+ } else {
+ char pwd[256] = {0};
+ ret = asprintf(&prompt, _("Enter new password for %s:"),
+ argv[0]);
+ if (ret == -1) {
+ return -1;
+ }
+
+ ret = samba_getpass(prompt, pwd, sizeof(pwd), false, false);
+ SAFE_FREE(prompt);
+ if (ret < 0) {
+ return -1;
+ }
+
+ u1003.usri1003_password = talloc_strdup(c, pwd);
+ if (u1003.usri1003_password == NULL) {
+ return -1;
+ }
+ }
+
+ status = NetUserSetInfo(c->opt_host, argv[0], 1003, (uint8_t *)&u1003, &parm_err);
+
+ /* Display results */
+ if (status != 0) {
+ d_fprintf(stderr,
+ _("Failed to set password for '%s' with error: %s.\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * List a user's groups from a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success)
+ **/
+
+static int rpc_user_info(struct net_context *c, int argc, const char **argv)
+
+{
+ NET_API_STATUS status;
+ struct GROUP_USERS_INFO_0 *u0 = NULL;
+ uint32_t entries_read = 0;
+ uint32_t total_entries = 0;
+ uint32_t i;
+
+
+ if (argc < 1 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ status = NetUserGetGroups(c->opt_host,
+ argv[0],
+ 0,
+ (uint8_t **)(void *)&u0,
+ (uint32_t)-1,
+ &entries_read,
+ &total_entries);
+ if (status != 0) {
+ d_fprintf(stderr,
+ _("Failed to get groups for '%s' with error: %s.\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ }
+
+ for (i=0; i < entries_read; i++) {
+ printf("%s\n", u0->grui0_name);
+ u0++;
+ }
+
+ return 0;
+}
+
+/**
+ * List users on a remote RPC server.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static int rpc_user_list(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ uint32_t start_idx=0, num_entries, i, loop_count = 0;
+ struct NET_DISPLAY_USER *info = NULL;
+ void *buffer = NULL;
+
+ /* Query domain users */
+ if (c->opt_long_list_entries)
+ d_printf(_("\nUser name Comment"
+ "\n-----------------------------\n"));
+ do {
+ uint32_t max_entries, max_size;
+
+ dcerpc_get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ status = NetQueryDisplayInformation(c->opt_host,
+ 1,
+ start_idx,
+ max_entries,
+ max_size,
+ &num_entries,
+ &buffer);
+ if (status != 0 && status != ERROR_MORE_DATA) {
+ return status;
+ }
+
+ info = (struct NET_DISPLAY_USER *)buffer;
+
+ for (i = 0; i < num_entries; i++) {
+
+ if (c->opt_long_list_entries)
+ printf("%-21.21s %s\n", info->usri1_name,
+ info->usri1_comment);
+ else
+ printf("%s\n", info->usri1_name);
+ info++;
+ }
+
+ NetApiBufferFree(buffer);
+
+ loop_count++;
+ start_idx += num_entries;
+
+ } while (status == ERROR_MORE_DATA);
+
+ return status;
+}
+
+/**
+ * 'net rpc user' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_user(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "add",
+ rpc_user_add,
+ NET_TRANSPORT_RPC,
+ N_("Add specified user"),
+ N_("net rpc user add\n"
+ " Add specified user")
+ },
+ {
+ "info",
+ rpc_user_info,
+ NET_TRANSPORT_RPC,
+ N_("List domain groups of user"),
+ N_("net rpc user info\n"
+ " List domain groups of user")
+ },
+ {
+ "delete",
+ rpc_user_delete,
+ NET_TRANSPORT_RPC,
+ N_("Remove specified user"),
+ N_("net rpc user delete\n"
+ " Remove specified user")
+ },
+ {
+ "password",
+ rpc_user_password,
+ NET_TRANSPORT_RPC,
+ N_("Change user password"),
+ N_("net rpc user password\n"
+ " Change user password")
+ },
+ {
+ "rename",
+ rpc_user_rename,
+ NET_TRANSPORT_RPC,
+ N_("Rename specified user"),
+ N_("net rpc user rename\n"
+ " Rename specified user")
+ },
+ {
+ "setprimarygroup",
+ rpc_user_setprimarygroup,
+ NET_TRANSPORT_RPC,
+ "Set a user's primary group",
+ "net rpc user setprimarygroup\n"
+ " Set a user's primary group"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc user\n"
+ " %s\n",
+ _("Usage:"),
+ _("List all users"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ return rpc_user_list(c, argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc user", func);
+}
+
+static NTSTATUS rpc_sh_user_list(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return werror_to_ntstatus(W_ERROR(rpc_user_list(c, argc, argv)));
+}
+
+static NTSTATUS rpc_sh_user_info(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return werror_to_ntstatus(W_ERROR(rpc_user_info(c, argc, argv)));
+}
+
+static NTSTATUS rpc_sh_handle_user(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv,
+ NTSTATUS (*fn)(
+ struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *user_hnd,
+ int argc, const char **argv))
+{
+ struct policy_handle connect_pol, domain_pol, user_pol;
+ NTSTATUS status, result;
+ struct dom_sid sid;
+ uint32_t rid;
+ enum lsa_SidType type;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc == 0) {
+ d_fprintf(stderr, "%s %s <username>\n", _("Usage:"),
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(connect_pol);
+ ZERO_STRUCT(domain_pol);
+ ZERO_STRUCT(user_pol);
+
+ status = net_rpc_lookup_name(c, mem_ctx, ctx->cli,
+ argv[0], NULL, NULL, &sid, &type);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not lookup %s: %s\n"), argv[0],
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (type != SID_NAME_USER) {
+ d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0],
+ sid_type_lookup(type));
+ status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ if (!sid_peek_check_rid(ctx->domain_sid, &sid, &rid)) {
+ d_fprintf(stderr, _("%s is not in our domain\n"), argv[0]);
+ status = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ ctx->domain_sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenUser(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &user_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = fn(c, mem_ctx, ctx, pipe_hnd, &user_pol, argc-1, argv+1);
+
+ done:
+ if (is_valid_policy_hnd(&user_pol)) {
+ dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
+ }
+ if (is_valid_policy_hnd(&domain_pol)) {
+ dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
+ }
+ if (is_valid_policy_hnd(&connect_pol)) {
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ }
+ return status;
+}
+
+static NTSTATUS rpc_sh_user_show_internals(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS status, result;
+ union samr_UserInfo *info = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 0) {
+ d_fprintf(stderr, "%s %s show <username>\n", _("Usage:"),
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_samr_QueryUserInfo(b, mem_ctx,
+ user_hnd,
+ 21,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ d_printf(_("user rid: %d, group rid: %d\n"),
+ info->info21.rid,
+ info->info21.primary_gid);
+
+ return result;
+}
+
+static NTSTATUS rpc_sh_user_show(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ rpc_sh_user_show_internals);
+}
+
+#define FETCHSTR(name, rec) \
+do { if (strequal(ctx->thiscmd, name)) { \
+ oldval = talloc_strdup(mem_ctx, info->info21.rec.string); } \
+} while (0);
+
+#define SETSTR(name, rec, flag) \
+do { if (strequal(ctx->thiscmd, name)) { \
+ init_lsa_String(&(info->info21.rec), argv[0]); \
+ info->info21.fields_present |= SAMR_FIELD_##flag; } \
+} while (0);
+
+static NTSTATUS rpc_sh_user_str_edit_internals(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS status, result;
+ const char *username;
+ const char *oldval = "";
+ union samr_UserInfo *info = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc > 1) {
+ d_fprintf(stderr, "%s %s <username> [new value|NULL]\n",
+ _("Usage:"), ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_samr_QueryUserInfo(b, mem_ctx,
+ user_hnd,
+ 21,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ username = talloc_strdup(mem_ctx, info->info21.account_name.string);
+
+ FETCHSTR("fullname", full_name);
+ FETCHSTR("homedir", home_directory);
+ FETCHSTR("homedrive", home_drive);
+ FETCHSTR("logonscript", logon_script);
+ FETCHSTR("profilepath", profile_path);
+ FETCHSTR("description", description);
+
+ if (argc == 0) {
+ d_printf(_("%s's %s: [%s]\n"), username, ctx->thiscmd, oldval);
+ goto done;
+ }
+
+ if (strcmp(argv[0], "NULL") == 0) {
+ argv[0] = "";
+ }
+
+ ZERO_STRUCT(info->info21);
+
+ SETSTR("fullname", full_name, FULL_NAME);
+ SETSTR("homedir", home_directory, HOME_DIRECTORY);
+ SETSTR("homedrive", home_drive, HOME_DRIVE);
+ SETSTR("logonscript", logon_script, LOGON_SCRIPT);
+ SETSTR("profilepath", profile_path, PROFILE_PATH);
+ SETSTR("description", description, DESCRIPTION);
+
+ status = dcerpc_samr_SetUserInfo(b, mem_ctx,
+ user_hnd,
+ 21,
+ info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = result;
+
+ d_printf(_("Set %s's %s from [%s] to [%s]\n"), username,
+ ctx->thiscmd, oldval, argv[0]);
+
+ done:
+
+ return status;
+}
+
+#define HANDLEFLG(name, rec) \
+do { if (strequal(ctx->thiscmd, name)) { \
+ oldval = (oldflags & ACB_##rec) ? "yes" : "no"; \
+ if (newval) { \
+ newflags = oldflags | ACB_##rec; \
+ } else { \
+ newflags = oldflags & ~ACB_##rec; \
+ } } } while (0);
+
+static NTSTATUS rpc_sh_user_str_edit(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ rpc_sh_user_str_edit_internals);
+}
+
+static NTSTATUS rpc_sh_user_flag_edit_internals(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS status, result;
+ const char *username;
+ const char *oldval = "unknown";
+ uint32_t oldflags, newflags;
+ bool newval;
+ union samr_UserInfo *info = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if ((argc > 1) ||
+ ((argc == 1) && !strequal(argv[0], "yes") &&
+ !strequal(argv[0], "no"))) {
+ /* TRANSLATORS: The yes|no here are program keywords. Please do
+ not translate. */
+ d_fprintf(stderr, _("Usage: %s <username> [yes|no]\n"),
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ newval = strequal(argv[0], "yes");
+
+ status = dcerpc_samr_QueryUserInfo(b, mem_ctx,
+ user_hnd,
+ 21,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ username = talloc_strdup(mem_ctx, info->info21.account_name.string);
+ oldflags = info->info21.acct_flags;
+ newflags = info->info21.acct_flags;
+
+ HANDLEFLG("disabled", DISABLED);
+ HANDLEFLG("pwnotreq", PWNOTREQ);
+ HANDLEFLG("autolock", AUTOLOCK);
+ HANDLEFLG("pwnoexp", PWNOEXP);
+
+ if (argc == 0) {
+ d_printf(_("%s's %s flag: %s\n"), username, ctx->thiscmd,
+ oldval);
+ goto done;
+ }
+
+ ZERO_STRUCT(info->info21);
+
+ info->info21.acct_flags = newflags;
+ info->info21.fields_present = SAMR_FIELD_ACCT_FLAGS;
+
+ status = dcerpc_samr_SetUserInfo(b, mem_ctx,
+ user_hnd,
+ 21,
+ info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = result;
+ if (NT_STATUS_IS_OK(result)) {
+ d_printf(_("Set %s's %s flag from [%s] to [%s]\n"), username,
+ ctx->thiscmd, oldval, argv[0]);
+ }
+
+ done:
+
+ return status;
+}
+
+static NTSTATUS rpc_sh_user_flag_edit(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ rpc_sh_user_flag_edit_internals);
+}
+
+struct rpc_sh_cmd *net_rpc_user_edit_cmds(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx)
+{
+ static struct rpc_sh_cmd cmds[] = {
+
+ { "fullname", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's full name") },
+
+ { "homedir", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's home directory") },
+
+ { "homedrive", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's home drive") },
+
+ { "logonscript", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's logon script") },
+
+ { "profilepath", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's profile path") },
+
+ { "description", NULL, &ndr_table_samr, rpc_sh_user_str_edit,
+ N_("Show/Set a user's description") },
+
+ { "disabled", NULL, &ndr_table_samr, rpc_sh_user_flag_edit,
+ N_("Show/Set whether a user is disabled") },
+
+ { "autolock", NULL, &ndr_table_samr, rpc_sh_user_flag_edit,
+ N_("Show/Set whether a user locked out") },
+
+ { "pwnotreq", NULL, &ndr_table_samr, rpc_sh_user_flag_edit,
+ N_("Show/Set whether a user does not need a password") },
+
+ { "pwnoexp", NULL, &ndr_table_samr, rpc_sh_user_flag_edit,
+ N_("Show/Set whether a user's password does not expire") },
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ return cmds;
+}
+
+struct rpc_sh_cmd *net_rpc_user_cmds(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx)
+{
+ static struct rpc_sh_cmd cmds[] = {
+
+ { "list", NULL, &ndr_table_samr, rpc_sh_user_list,
+ N_("List available users") },
+
+ { "info", NULL, &ndr_table_samr, rpc_sh_user_info,
+ N_("List the domain groups a user is member of") },
+
+ { "show", NULL, &ndr_table_samr, rpc_sh_user_show,
+ N_("Show info about a user") },
+
+ { "edit", net_rpc_user_edit_cmds, 0, NULL,
+ N_("Show/Modify a user's fields") },
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ return cmds;
+}
+
+/****************************************************************************/
+
+/**
+ * Basic usage function for 'net rpc group'.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+static int rpc_group_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_group_usage(c, argc, argv);
+}
+
+/**
+ * Delete group on a remote RPC server.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_group_delete_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle connect_pol, domain_pol, group_pol, user_pol;
+ bool group_is_primary = false;
+ NTSTATUS status, result;
+ uint32_t group_rid;
+ struct samr_RidAttrArray *rids = NULL;
+ /* char **names; */
+ uint32_t i;
+ /* struct samr_RidWithAttribute *user_gids; */
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct samr_Ids group_rids, name_types;
+ struct lsa_String lsa_acct_name;
+ union samr_UserInfo *info = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ rpc_group_usage(c, argc,argv);
+ return NT_STATUS_OK; /* ok? */
+ }
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Request samr_Connect2 failed\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Request samr_Connect2 failed\n"));
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Request open_domain failed\n"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Request open_domain failed\n"));
+ goto done;
+ }
+
+ init_lsa_String(&lsa_acct_name, argv[0]);
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &group_rids,
+ &name_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]);
+ goto done;
+ }
+ if (group_rids.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+ if (name_types.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ switch (name_types.ids[0])
+ {
+ case SID_NAME_DOM_GRP:
+ status = dcerpc_samr_OpenGroup(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rids.ids[0],
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Request open_group failed"));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Request open_group failed"));
+ goto done;
+ }
+
+ group_rid = group_rids.ids[0];
+
+ status = dcerpc_samr_QueryGroupMember(b, mem_ctx,
+ &group_pol,
+ &rids,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to query group members of %s"),
+ argv[0]);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr,
+ _("Unable to query group members of %s"),
+ argv[0]);
+ goto done;
+ }
+
+ if (c->opt_verbose) {
+ d_printf(
+ _("Domain Group %s (rid: %d) has %d members\n"),
+ argv[0],group_rid, rids->count);
+ }
+
+ /* Check if group is anyone's primary group */
+ for (i = 0; i < rids->count; i++)
+ {
+ status = dcerpc_samr_OpenUser(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rids->rids[i],
+ &user_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to open group member %d\n"),
+ rids->rids[i]);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr,
+ _("Unable to open group member %d\n"),
+ rids->rids[i]);
+ goto done;
+ }
+
+ status = dcerpc_samr_QueryUserInfo(b, mem_ctx,
+ &user_pol,
+ 21,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Unable to lookup userinfo for group "
+ "member %d\n"),
+ rids->rids[i]);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr,
+ _("Unable to lookup userinfo for group "
+ "member %d\n"),
+ rids->rids[i]);
+ goto done;
+ }
+
+ if (info->info21.primary_gid == group_rid) {
+ if (c->opt_verbose) {
+ d_printf(_("Group is primary group "
+ "of %s\n"),
+ info->info21.account_name.string);
+ }
+ group_is_primary = true;
+ }
+
+ dcerpc_samr_Close(b, mem_ctx, &user_pol, &result);
+ }
+
+ if (group_is_primary) {
+ d_fprintf(stderr, _("Unable to delete group because "
+ "some of it's members have it as primary "
+ "group\n"));
+ status = NT_STATUS_MEMBERS_PRIMARY_GROUP;
+ goto done;
+ }
+
+ /* remove all group members */
+ for (i = 0; i < rids->count; i++)
+ {
+ if (c->opt_verbose)
+ d_printf(_("Remove group member %d..."),
+ rids->rids[i]);
+ status = dcerpc_samr_DeleteGroupMember(b, mem_ctx,
+ &group_pol,
+ rids->rids[i],
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = result;
+ if (NT_STATUS_IS_OK(result)) {
+ if (c->opt_verbose)
+ d_printf(_("ok\n"));
+ } else {
+ if (c->opt_verbose)
+ d_printf("%s\n", _("failed"));
+ goto done;
+ }
+ }
+
+ status = dcerpc_samr_DeleteDomainGroup(b, mem_ctx,
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ status = result;
+
+ break;
+ /* removing a local group is easier... */
+ case SID_NAME_ALIAS:
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rids.ids[0],
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Request open_alias failed\n"));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Request open_alias failed\n"));
+ goto done;
+ }
+
+ status = dcerpc_samr_DeleteDomAlias(b, mem_ctx,
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+
+ status = result;
+
+ break;
+ default:
+ d_fprintf(stderr, _("%s is of type %s. This command is only "
+ "for deleting local or global groups\n"),
+ argv[0],sid_type_lookup(name_types.ids[0]));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ if (c->opt_verbose)
+ d_printf(_("Deleted %s '%s'\n"),
+ sid_type_lookup(name_types.ids[0]), argv[0]);
+ } else {
+ d_fprintf(stderr, _("Deleting of %s failed: %s\n"), argv[0],
+ get_friendly_nt_error_msg(status));
+ }
+
+ done:
+ return status;
+
+}
+
+static int rpc_group_delete(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_delete_internals, argc,argv);
+}
+
+static int rpc_group_add_internals(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct GROUP_INFO_1 info1;
+ uint32_t parm_error = 0;
+
+ if (argc != 1 || c->display_usage) {
+ rpc_group_usage(c, argc, argv);
+ return 0;
+ }
+
+ ZERO_STRUCT(info1);
+
+ info1.grpi1_name = argv[0];
+ if (c->opt_comment && strlen(c->opt_comment) > 0) {
+ info1.grpi1_comment = c->opt_comment;
+ }
+
+ status = NetGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error);
+
+ if (status != 0) {
+ d_fprintf(stderr,
+ _("Failed to add group '%s' with error: %s.\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ } else {
+ d_printf(_("Added group '%s'.\n"), argv[0]);
+ }
+
+ return 0;
+}
+
+static int rpc_alias_add_internals(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct LOCALGROUP_INFO_1 info1;
+ uint32_t parm_error = 0;
+
+ if (argc != 1 || c->display_usage) {
+ rpc_group_usage(c, argc, argv);
+ return 0;
+ }
+
+ ZERO_STRUCT(info1);
+
+ info1.lgrpi1_name = argv[0];
+ if (c->opt_comment && strlen(c->opt_comment) > 0) {
+ info1.lgrpi1_comment = c->opt_comment;
+ }
+
+ status = NetLocalGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error);
+
+ if (status != 0) {
+ d_fprintf(stderr,
+ _("Failed to add alias '%s' with error: %s.\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ } else {
+ d_printf(_("Added alias '%s'.\n"), argv[0]);
+ }
+
+ return 0;
+}
+
+static int rpc_group_add(struct net_context *c, int argc, const char **argv)
+{
+ if (c->opt_localgroup)
+ return rpc_alias_add_internals(c, argc, argv);
+
+ return rpc_group_add_internals(c, argc, argv);
+}
+
+static NTSTATUS get_sid_from_name(struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *name,
+ struct dom_sid *sid,
+ enum lsa_SidType *type)
+{
+ struct dom_sid *sids = NULL;
+ enum lsa_SidType *types = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct policy_handle lsa_pol;
+ NTSTATUS status, result;
+ struct dcerpc_binding_handle *b;
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ b = pipe_hnd->binding_handle;
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, false,
+ SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &lsa_pol, 1,
+ &name, NULL, 1, &sids, &types);
+
+ if (NT_STATUS_IS_OK(status)) {
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+ }
+
+ dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result);
+
+ done:
+ if (pipe_hnd) {
+ TALLOC_FREE(pipe_hnd);
+ }
+
+ if (!NT_STATUS_IS_OK(status) && (strncasecmp_m(name, "S-", 2) == 0)) {
+
+ /* Try as S-1-5-whatever */
+
+ struct dom_sid tmp_sid;
+
+ if (string_to_sid(&tmp_sid, name)) {
+ sid_copy(sid, &tmp_sid);
+ *type = SID_NAME_UNKNOWN;
+ status = NT_STATUS_OK;
+ }
+ }
+
+ return status;
+}
+
+static NTSTATUS rpc_add_groupmem(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ const char *member)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ uint32_t group_rid;
+ struct policy_handle group_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+
+ struct dom_sid sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get sam policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ init_lsa_String(&lsa_acct_name, member);
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ goto done;
+ }
+ if (rids.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+ if (rid_types.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenGroup(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid,
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_AddGroupMember(b, mem_ctx,
+ &group_pol,
+ rids.ids[0],
+ 0x0005, /* unknown flags */
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = result;
+
+ done:
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ return status;
+}
+
+static NTSTATUS rpc_add_aliasmem(struct rpc_pipe_client *pipe_hnd,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *alias_sid,
+ const char *member)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ uint32_t alias_rid;
+ struct policy_handle alias_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct dom_sid member_sid;
+ enum lsa_SidType member_type;
+
+ struct dom_sid sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ result = get_sid_from_name(cli, mem_ctx,
+ member, &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid,
+ &alias_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ status = dcerpc_samr_AddAliasMember(b, mem_ctx,
+ &alias_pol,
+ &member_sid,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = result;
+
+ done:
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ return status;
+}
+
+static NTSTATUS rpc_group_addmem_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dom_sid group_sid;
+ enum lsa_SidType group_type;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc group addmem <group> <member>\n"
+ " Add a member to a group\n"
+ " group\tGroup to add member to\n"
+ " member\tMember to add to group\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_fprintf(stderr, _("Could not lookup group name %s\n"),
+ argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_add_groupmem(pipe_hnd, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not add %s to %s: %s\n"),
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_add_aliasmem(pipe_hnd, cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not add %s to %s: %s\n"),
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_fprintf(stderr, _("Can only add members to global or local groups "
+ "which %s is not\n"), argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_addmem(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_addmem_internals,
+ argc, argv);
+}
+
+static NTSTATUS rpc_del_groupmem(struct net_context *c,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *group_sid,
+ const char *member)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ uint32_t group_rid;
+ struct policy_handle group_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+
+ struct dom_sid sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ init_lsa_String(&lsa_acct_name, member);
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ goto done;
+ }
+ if (rids.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+ if (rid_types.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenGroup(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid,
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_DeleteGroupMember(b, mem_ctx,
+ &group_pol,
+ rids.ids[0],
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = result;
+ done:
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ return status;
+}
+
+static NTSTATUS rpc_del_aliasmem(struct rpc_pipe_client *pipe_hnd,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const struct dom_sid *alias_sid,
+ const char *member)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ uint32_t alias_rid;
+ struct policy_handle alias_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct dom_sid member_sid;
+ enum lsa_SidType member_type;
+
+ struct dom_sid sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(cli, mem_ctx,
+ member, &member_sid, &member_type);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not lookup up group member %s\n"),
+ member);
+ return result;
+ }
+
+ /* Get sam policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid,
+ &alias_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ status = dcerpc_samr_DeleteAliasMember(b, mem_ctx,
+ &alias_pol,
+ &member_sid,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = result;
+
+ done:
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ return status;
+}
+
+static NTSTATUS rpc_group_delmem_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dom_sid group_sid;
+ enum lsa_SidType group_type;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc group delmem <group> <member>\n"
+ " Delete a member from a group\n"
+ " group\tGroup to delete member from\n"
+ " member\tMember to delete from group\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0],
+ &group_sid, &group_type))) {
+ d_fprintf(stderr, _("Could not lookup group name %s\n"),
+ argv[0]);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (group_type == SID_NAME_DOM_GRP) {
+ NTSTATUS result = rpc_del_groupmem(c, pipe_hnd, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not del %s from %s: %s\n"),
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ if (group_type == SID_NAME_ALIAS) {
+ NTSTATUS result = rpc_del_aliasmem(pipe_hnd, cli, mem_ctx,
+ &group_sid, argv[1]);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Could not del %s from %s: %s\n"),
+ argv[1], argv[0], nt_errstr(result));
+ }
+ return result;
+ }
+
+ d_fprintf(stderr, _("Can only delete members from global or local "
+ "groups which %s is not\n"), argv[0]);
+
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+static int rpc_group_delmem(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_delmem_internals,
+ argc, argv);
+}
+
+/**
+ * List groups on a remote RPC server.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passes through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_group_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ uint32_t start_idx=0, max_entries=250, num_entries, i, loop_count = 0;
+ struct samr_SamArray *groups = NULL;
+ bool global = false;
+ bool local = false;
+ bool builtin = false;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc group list [global] [local] [builtin]\n"
+ " List groups on RPC server\n"
+ " global\tList global groups\n"
+ " local\tList local groups\n"
+ " builtin\tList builtin groups\n"
+ " If none of global, local or builtin is "
+ "specified, all three options are considered "
+ "set\n"));
+ return NT_STATUS_OK;
+ }
+
+ if (argc == 0) {
+ global = true;
+ local = true;
+ builtin = true;
+ }
+
+ for (i=0; i<argc; i++) {
+ if (strequal(argv[i], "global"))
+ global = true;
+
+ if (strequal(argv[i], "local"))
+ local = true;
+
+ if (strequal(argv[i], "builtin"))
+ builtin = true;
+ }
+
+ /* Get sam policy handle */
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Query domain groups */
+ if (c->opt_long_list_entries)
+ d_printf(_("\nGroup name Comment"
+ "\n-----------------------------\n"));
+ do {
+ uint32_t max_size, total_size, returned_size;
+ union samr_DispInfo info;
+
+ if (!global) break;
+
+ dcerpc_get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ status = dcerpc_samr_QueryDisplayInfo(b, mem_ctx,
+ &domain_pol,
+ 3,
+ start_idx,
+ max_entries,
+ max_size,
+ &total_size,
+ &returned_size,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ num_entries = info.info3.count;
+ start_idx += info.info3.count;
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ const char *group = NULL;
+ const char *desc = NULL;
+
+ group = info.info3.entries[i].account_name.string;
+ desc = info.info3.entries[i].description.string;
+
+ if (c->opt_long_list_entries)
+ printf("%-21.21s %-50.50s\n",
+ group, desc);
+ else
+ printf("%s\n", group);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ /* query domain aliases */
+ start_idx = 0;
+ do {
+ if (!local) break;
+
+ status = dcerpc_samr_EnumDomainAliases(b, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ 0xffff,
+ &num_entries,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES))
+ break;
+
+ for (i = 0; i < num_entries; i++) {
+
+ const char *description = NULL;
+
+ if (c->opt_long_list_entries) {
+
+ struct policy_handle alias_pol;
+ union samr_AliasInfo *info = NULL;
+ NTSTATUS _result;
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups->entries[i].idx,
+ &alias_pol,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ status = dcerpc_samr_QueryAliasInfo(b, mem_ctx,
+ &alias_pol,
+ 3,
+ &info,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ status = dcerpc_samr_Close(b, mem_ctx,
+ &alias_pol,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ description = info->description.string;
+ }
+ }
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups->entries[i].name.string,
+ description);
+ } else {
+ printf("%s\n", groups->entries[i].name.string);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+ dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
+ /* Get builtin policy handle */
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, &global_sid_Builtin),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* query builtin aliases */
+ start_idx = 0;
+ do {
+ if (!builtin) break;
+
+ status = dcerpc_samr_EnumDomainAliases(b, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ max_entries,
+ &num_entries,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ break;
+ }
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) {
+ status = result;
+ break;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+
+ const char *description = NULL;
+
+ if (c->opt_long_list_entries) {
+
+ struct policy_handle alias_pol;
+ union samr_AliasInfo *info = NULL;
+ NTSTATUS _result;
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups->entries[i].idx,
+ &alias_pol,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ status = dcerpc_samr_QueryAliasInfo(b, mem_ctx,
+ &alias_pol,
+ 3,
+ &info,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ status = dcerpc_samr_Close(b, mem_ctx,
+ &alias_pol,
+ &_result);
+ if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) {
+ description = info->description.string;
+ }
+ }
+ }
+ }
+
+ if (description != NULL) {
+ printf("%-21.21s %-50.50s\n",
+ groups->entries[i].name.string,
+ description);
+ } else {
+ printf("%s\n", groups->entries[i].name.string);
+ }
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ status = result;
+
+ done:
+ return status;
+}
+
+static int rpc_group_list(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_list_internals,
+ argc, argv);
+}
+
+static NTSTATUS rpc_list_group_members(struct net_context *c,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name,
+ const struct dom_sid *domain_sid,
+ struct policy_handle *domain_pol,
+ uint32_t rid)
+{
+ NTSTATUS result, status;
+ struct policy_handle group_pol;
+ uint32_t num_members, *group_rids;
+ uint32_t i;
+ struct samr_RidAttrArray *rids = NULL;
+ struct lsa_Strings names;
+ struct samr_Ids types;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_samr_OpenGroup(b, mem_ctx,
+ domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &group_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ status = dcerpc_samr_QueryGroupMember(b, mem_ctx,
+ &group_pol,
+ &rids,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ num_members = rids->count;
+ group_rids = rids->rids;
+
+ while (num_members > 0) {
+ uint32_t this_time = 512;
+
+ if (num_members < this_time)
+ this_time = num_members;
+
+ status = dcerpc_samr_LookupRids(b, mem_ctx,
+ domain_pol,
+ this_time,
+ group_rids,
+ &names,
+ &types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+ if (names.count != this_time) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (types.count != this_time) {
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ /* We only have users as members, but make the output
+ the same as the output of alias members */
+
+ for (i = 0; i < this_time; i++) {
+
+ if (c->opt_long_list_entries) {
+ struct dom_sid sid;
+ struct dom_sid_buf sid_str;
+
+ sid_compose(&sid, domain_sid, group_rids[i]);
+
+ printf("%s %s\\%s %d\n",
+ dom_sid_str_buf(&sid, &sid_str),
+ domain_name,
+ names.names[i].string,
+ SID_NAME_USER);
+ } else {
+ printf("%s\\%s\n", domain_name,
+ names.names[i].string);
+ }
+ }
+
+ num_members -= this_time;
+ group_rids += 512;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpc_list_alias_members(struct net_context *c,
+ struct rpc_pipe_client *pipe_hnd,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *domain_pol,
+ uint32_t rid)
+{
+ NTSTATUS result, status;
+ struct rpc_pipe_client *lsa_pipe;
+ struct policy_handle alias_pol, lsa_pol;
+ uint32_t num_members;
+ struct dom_sid *alias_sids;
+ char **domains;
+ char **names;
+ enum lsa_SidType *types;
+ uint32_t i;
+ struct lsa_SidArray sid_array;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &alias_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ status = dcerpc_samr_GetMembersInAlias(b, mem_ctx,
+ &alias_pol,
+ &sid_array,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Couldn't list alias members\n"));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Couldn't list alias members\n"));
+ return result;
+ }
+
+ num_members = sid_array.num_sids;
+
+ if (num_members == 0) {
+ return NT_STATUS_OK;
+ }
+
+ result = cli_rpc_pipe_open_noauth(cli,
+ &ndr_table_lsarpc,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Couldn't open LSA pipe. Error was %s\n"),
+ nt_errstr(result) );
+ return result;
+ }
+
+ result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Couldn't open LSA policy handle\n"));
+ TALLOC_FREE(lsa_pipe);
+ return result;
+ }
+
+ alias_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_members);
+ if (!alias_sids) {
+ d_fprintf(stderr, _("Out of memory\n"));
+ TALLOC_FREE(lsa_pipe);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<num_members; i++) {
+ sid_copy(&alias_sids[i], sid_array.sids[i].sid);
+ }
+
+ result = rpccli_lsa_lookup_sids(lsa_pipe, mem_ctx, &lsa_pol,
+ num_members, alias_sids,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) {
+ d_fprintf(stderr, _("Couldn't lookup SIDs\n"));
+ TALLOC_FREE(lsa_pipe);
+ return result;
+ }
+
+ for (i = 0; i < num_members; i++) {
+ struct dom_sid_buf sid_str;
+ dom_sid_str_buf(&alias_sids[i], &sid_str);
+
+ if (c->opt_long_list_entries) {
+ printf("%s %s\\%s %d\n", sid_str.buf,
+ domains[i] ? domains[i] : _("*unknown*"),
+ names[i] ? names[i] : _("*unknown*"), types[i]);
+ } else {
+ if (domains[i])
+ printf("%s\\%s\n", domains[i], names[i]);
+ else
+ printf("%s\n", sid_str.buf);
+ }
+ }
+
+ TALLOC_FREE(lsa_pipe);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpc_group_members_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS result, status;
+ struct policy_handle connect_pol, domain_pol;
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* Get sam policy handle */
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* Get domain policy handle */
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ init_lsa_String(&lsa_acct_name, argv[0]); /* sure? */
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Ok, did not find it in the global sam, try with builtin */
+
+ struct dom_sid sid_Builtin;
+
+ dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
+
+ sid_copy(&sid_Builtin, &global_sid_Builtin);
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid_Builtin,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Couldn't find group %s\n"),
+ argv[0]);
+ return result;
+ }
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, _("Couldn't find group %s\n"),
+ argv[0]);
+ return result;
+ }
+ }
+
+ if (rids.count != 1) {
+ d_fprintf(stderr, _("Couldn't find group %s\n"),
+ argv[0]);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+ if (rid_types.count != 1) {
+ d_fprintf(stderr, _("Couldn't find group %s\n"),
+ argv[0]);
+ return NT_STATUS_INVALID_NETWORK_RESPONSE;
+ }
+
+
+ if (rid_types.ids[0] == SID_NAME_DOM_GRP) {
+ return rpc_list_group_members(c, pipe_hnd, mem_ctx, domain_name,
+ domain_sid, &domain_pol,
+ rids.ids[0]);
+ }
+
+ if (rid_types.ids[0] == SID_NAME_ALIAS) {
+ return rpc_list_alias_members(c, pipe_hnd, cli, mem_ctx, &domain_pol,
+ rids.ids[0]);
+ }
+
+ return NT_STATUS_NO_SUCH_GROUP;
+}
+
+static int rpc_group_members(struct net_context *c, int argc, const char **argv)
+{
+ if (argc != 1 || c->display_usage) {
+ return rpc_group_usage(c, argc, argv);
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_members_internals,
+ argc, argv);
+}
+
+static int rpc_group_rename_internals(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct GROUP_INFO_0 g0;
+ uint32_t parm_err;
+
+ if (argc != 2) {
+ d_printf(_("Usage:\n"));
+ d_printf("net rpc group rename group newname\n");
+ return -1;
+ }
+
+ g0.grpi0_name = argv[1];
+
+ status = NetGroupSetInfo(c->opt_host,
+ argv[0],
+ 0,
+ (uint8_t *)&g0,
+ &parm_err);
+
+ if (status != 0) {
+ d_fprintf(stderr, _("Renaming group %s failed with: %s\n"),
+ argv[0], libnetapi_get_error_string(c->netapi_ctx,
+ status));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rpc_group_rename(struct net_context *c, int argc, const char **argv)
+{
+ if (argc != 2 || c->display_usage) {
+ return rpc_group_usage(c, argc, argv);
+ }
+
+ return rpc_group_rename_internals(c, argc, argv);
+}
+
+/**
+ * 'net rpc group' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_group(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "add",
+ rpc_group_add,
+ NET_TRANSPORT_RPC,
+ N_("Create specified group"),
+ N_("net rpc group add\n"
+ " Create specified group")
+ },
+ {
+ "delete",
+ rpc_group_delete,
+ NET_TRANSPORT_RPC,
+ N_("Delete specified group"),
+ N_("net rpc group delete\n"
+ " Delete specified group")
+ },
+ {
+ "addmem",
+ rpc_group_addmem,
+ NET_TRANSPORT_RPC,
+ N_("Add member to group"),
+ N_("net rpc group addmem\n"
+ " Add member to group")
+ },
+ {
+ "delmem",
+ rpc_group_delmem,
+ NET_TRANSPORT_RPC,
+ N_("Remove member from group"),
+ N_("net rpc group delmem\n"
+ " Remove member from group")
+ },
+ {
+ "list",
+ rpc_group_list,
+ NET_TRANSPORT_RPC,
+ N_("List groups"),
+ N_("net rpc group list\n"
+ " List groups")
+ },
+ {
+ "members",
+ rpc_group_members,
+ NET_TRANSPORT_RPC,
+ N_("List group members"),
+ N_("net rpc group members\n"
+ " List group members")
+ },
+ {
+ "rename",
+ rpc_group_rename,
+ NET_TRANSPORT_RPC,
+ N_("Rename group"),
+ N_("net rpc group rename\n"
+ " Rename group")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rpc group\n"
+ " Alias for net rpc group list global "
+ "local builtin\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_group_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc group", func);
+}
+
+/****************************************************************************/
+
+static int rpc_share_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_share_usage(c, argc, argv);
+}
+
+/**
+ * Add a share on a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_share_add(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ char *sharename;
+ char *path;
+ uint32_t type = STYPE_DISKTREE; /* only allow disk shares to be added */
+ uint32_t num_users=0, perms=0;
+ char *password=NULL; /* don't allow a share password */
+ struct SHARE_INFO_2 i2;
+ uint32_t parm_error = 0;
+
+ if ((argc < 1) || !strchr(argv[0], '=') || c->display_usage) {
+ return rpc_share_usage(c, argc, argv);
+ }
+
+ if ((sharename = talloc_strdup(c, argv[0])) == NULL) {
+ return -1;
+ }
+
+ path = strchr(sharename, '=');
+ if (!path) {
+ return -1;
+ }
+
+ *path++ = '\0';
+
+ i2.shi2_netname = sharename;
+ i2.shi2_type = type;
+ i2.shi2_remark = c->opt_comment;
+ i2.shi2_permissions = perms;
+ i2.shi2_max_uses = c->opt_maxusers;
+ i2.shi2_current_uses = num_users;
+ i2.shi2_path = path;
+ i2.shi2_passwd = password;
+
+ status = NetShareAdd(c->opt_host,
+ 2,
+ (uint8_t *)&i2,
+ &parm_error);
+ if (status != 0) {
+ printf(_("NetShareAdd failed with: %s\n"),
+ libnetapi_get_error_string(c->netapi_ctx, status));
+ }
+
+ return status;
+}
+
+/**
+ * Delete a share on a remote RPC server.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_share_delete(struct net_context *c, int argc, const char **argv)
+{
+ if (argc < 1 || c->display_usage) {
+ return rpc_share_usage(c, argc, argv);
+ }
+
+ return NetShareDel(c->opt_host, argv[0], 0);
+}
+
+/**
+ * Formatted print of share info
+ *
+ * @param r pointer to SHARE_INFO_1 to format
+ **/
+
+static void display_share_info_1(struct net_context *c,
+ struct SHARE_INFO_1 *r)
+{
+ if (c->opt_long_list_entries) {
+ d_printf("%-12s %-8.8s %-50s\n",
+ r->shi1_netname,
+ net_share_type_str(r->shi1_type & ~(STYPE_TEMPORARY|STYPE_HIDDEN)),
+ r->shi1_remark);
+ } else {
+ d_printf("%s\n", r->shi1_netname);
+ }
+}
+
+static WERROR get_share_info(struct net_context *c,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ uint32_t level,
+ int argc,
+ const char **argv,
+ struct srvsvc_NetShareInfoCtr *info_ctr)
+{
+ WERROR result;
+ NTSTATUS status;
+ union srvsvc_NetShareInfo info;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* no specific share requested, enumerate all */
+ if (argc == 0) {
+
+ uint32_t preferred_len = 0xffffffff;
+ uint32_t total_entries = 0;
+ uint32_t resume_handle = 0;
+
+ info_ctr->level = level;
+
+ status = dcerpc_srvsvc_NetShareEnumAll(b, mem_ctx,
+ pipe_hnd->desthost,
+ info_ctr,
+ preferred_len,
+ &total_entries,
+ &resume_handle,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return ntstatus_to_werror(status);
+ }
+ return result;
+ }
+
+ /* request just one share */
+ status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx,
+ pipe_hnd->desthost,
+ argv[0],
+ level,
+ &info,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ /* construct ctr */
+ ZERO_STRUCTP(info_ctr);
+
+ info_ctr->level = level;
+
+ switch (level) {
+ case 1:
+ {
+ struct srvsvc_NetShareCtr1 *ctr1;
+
+ ctr1 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ ctr1->count = 1;
+ ctr1->array = info.info1;
+
+ info_ctr->ctr.ctr1 = ctr1;
+
+ break;
+ }
+ case 2:
+ {
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ ctr2 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ ctr2->count = 1;
+ ctr2->array = info.info2;
+
+ info_ctr->ctr.ctr2 = ctr2;
+
+ break;
+ }
+ case 502:
+ {
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ ctr502 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ ctr502->count = 1;
+ ctr502->array = info.info502;
+
+ info_ctr->ctr.ctr502 = ctr502;
+
+ break;
+ }
+ } /* switch */
+done:
+ return result;
+}
+
+/***
+ * 'net rpc share list' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+static int rpc_share_list(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ struct SHARE_INFO_1 *i1 = NULL;
+ uint32_t entries_read = 0;
+ uint32_t total_entries = 0;
+ uint32_t resume_handle = 0;
+ uint32_t i, level = 1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc share list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List shares on remote server"));
+ return 0;
+ }
+
+ status = NetShareEnum(c->opt_host,
+ level,
+ (uint8_t **)(void *)&i1,
+ (uint32_t)-1,
+ &entries_read,
+ &total_entries,
+ &resume_handle);
+ if (status != 0) {
+ goto done;
+ }
+
+ /* Display results */
+
+ if (c->opt_long_list_entries) {
+ d_printf(_(
+ "\nEnumerating shared resources (exports) on remote server:\n\n"
+ "\nShare name Type Description\n"
+ "---------- ---- -----------\n"));
+ }
+ for (i = 0; i < entries_read; i++)
+ display_share_info_1(c, &i1[i]);
+ done:
+ return status;
+}
+
+static bool check_share_availability(struct cli_state *cli, const char *netname)
+{
+ NTSTATUS status;
+
+ status = cli_tree_connect(cli, netname, "A:", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("skipping [%s]: not a file share.\n"), netname);
+ return false;
+ }
+
+ status = cli_tdis(cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("cli_tdis returned %s\n"), nt_errstr(status));
+ return false;
+ }
+
+ return true;
+}
+
+static bool check_share_sanity(struct net_context *c, struct cli_state *cli,
+ const char *netname, uint32_t type)
+{
+ /* only support disk shares */
+ if (! ( type == STYPE_DISKTREE || type == (STYPE_DISKTREE | STYPE_HIDDEN)) ) {
+ printf(_("share [%s] is not a diskshare (type: %x)\n"), netname,
+ type);
+ return false;
+ }
+
+ /* skip builtin shares */
+ /* FIXME: should print$ be added too ? */
+ if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$") ||
+ strequal(netname,"global"))
+ return false;
+
+ if (c->opt_exclude && in_list(netname, c->opt_exclude, false)) {
+ printf(_("excluding [%s]\n"), netname);
+ return false;
+ }
+
+ return check_share_availability(cli, netname);
+}
+
+/**
+ * Migrate shares from a remote RPC server to the local RPC server.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_share_migrate_shares_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct srvsvc_NetShareInfoCtr ctr_src;
+ uint32_t i;
+ struct rpc_pipe_client *srvsvc_pipe = NULL;
+ struct cli_state *cli_dst = NULL;
+ uint32_t level = 502; /* includes secdesc */
+ uint32_t parm_error = 0;
+ struct dcerpc_binding_handle *b;
+
+ result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv,
+ &ctr_src);
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* connect destination PI_SRVSVC */
+ nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe,
+ &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ b = srvsvc_pipe->binding_handle;
+
+ for (i = 0; i < ctr_src.ctr.ctr502->count; i++) {
+
+ union srvsvc_NetShareInfo info;
+ struct srvsvc_NetShareInfo502 info502 =
+ ctr_src.ctr.ctr502->array[i];
+
+ /* reset error-code */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!check_share_sanity(c, cli, info502.name, info502.type))
+ continue;
+
+ /* finally add the share on the dst server */
+
+ printf(_("migrating: [%s], path: %s, comment: %s, without "
+ "share-ACLs\n"),
+ info502.name, info502.path, info502.comment);
+
+ info.info502 = &info502;
+
+ nt_status = dcerpc_srvsvc_NetShareAdd(b, mem_ctx,
+ srvsvc_pipe->desthost,
+ 502,
+ &info,
+ &parm_error,
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("cannot add share: %s\n"),
+ nt_errstr(nt_status));
+ goto done;
+ }
+ if (W_ERROR_V(result) == W_ERROR_V(WERR_FILE_EXISTS)) {
+ printf(_(" [%s] does already exist\n"),
+ info502.name);
+ continue;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ nt_status = werror_to_ntstatus(result);
+ printf(_("cannot add share: %s\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+
+ return nt_status;
+
+}
+
+/**
+ * Migrate shares from a RPC server to another.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_share_migrate_shares(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc share migrate shares\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate shares to local server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_shares_internals,
+ argc, argv);
+}
+
+/**
+ * Copy a file/dir
+ *
+ * @param f file_info
+ * @param mask current search mask
+ * @param state arg-pointer
+ *
+ **/
+static NTSTATUS copy_fn(struct file_info *f,
+ const char *mask, void *state)
+{
+ static NTSTATUS nt_status;
+ static struct copy_clistate *local_state;
+ static fstring filename, new_mask;
+ fstring dir;
+ char *old_dir;
+ struct net_context *c;
+
+ local_state = (struct copy_clistate *)state;
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ c = local_state->c;
+
+ if (strequal(f->name, ".") || strequal(f->name, ".."))
+ return NT_STATUS_OK;
+
+ DEBUG(3,("got mask: %s, name: %s\n", mask, f->name));
+
+ /* DIRECTORY */
+ if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
+
+ DEBUG(3,("got dir: %s\n", f->name));
+
+ fstrcpy(dir, local_state->cwd);
+ fstrcat(dir, "\\");
+ fstrcat(dir, f->name);
+
+ switch (net_mode_share)
+ {
+ case NET_MODE_SHARE_MIGRATE:
+ /* create that directory */
+ nt_status = net_copy_file(c, local_state->mem_ctx,
+ local_state->cli_share_src,
+ local_state->cli_share_dst,
+ dir, dir,
+ c->opt_acls? true : false,
+ c->opt_attrs? true : false,
+ c->opt_timestamps? true:false,
+ false);
+ break;
+ default:
+ d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("could not handle dir %s: %s\n"),
+ dir, nt_errstr(nt_status));
+ return nt_status;
+ }
+
+ /* search below that directory */
+ if (strlcpy(new_mask, dir, sizeof(new_mask)) >= sizeof(new_mask)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ if (strlcat(new_mask, "\\*", sizeof(new_mask)) >= sizeof(new_mask)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ old_dir = local_state->cwd;
+ local_state->cwd = dir;
+ nt_status = sync_files(local_state, new_mask);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("could not handle files\n"));
+ }
+ local_state->cwd = old_dir;
+
+ return nt_status;
+ }
+
+
+ /* FILE */
+ fstrcpy(filename, local_state->cwd);
+ fstrcat(filename, "\\");
+ fstrcat(filename, f->name);
+
+ DEBUG(3,("got file: %s\n", filename));
+
+ switch (net_mode_share)
+ {
+ case NET_MODE_SHARE_MIGRATE:
+ nt_status = net_copy_file(c, local_state->mem_ctx,
+ local_state->cli_share_src,
+ local_state->cli_share_dst,
+ filename, filename,
+ c->opt_acls? true : false,
+ c->opt_attrs? true : false,
+ c->opt_timestamps? true: false,
+ true);
+ break;
+ default:
+ d_fprintf(stderr, _("Unsupported file mode %d\n"),
+ net_mode_share);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ printf(_("could not handle file %s: %s\n"),
+ filename, nt_errstr(nt_status));
+ return nt_status;
+}
+
+/**
+ * sync files, can be called recursively to list files
+ * and then call copy_fn for each file
+ *
+ * @param cp_clistate pointer to the copy_clistate we work with
+ * @param mask the current search mask
+ *
+ * @return Boolean result
+ **/
+static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask)
+{
+ struct cli_state *targetcli;
+ char *targetpath = NULL;
+ NTSTATUS status;
+
+ DEBUG(3,("calling cli_list with mask: %s\n", mask));
+
+ status = cli_resolve_path(talloc_tos(), "", NULL,
+ cp_clistate->cli_share_src,
+ mask, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("cli_resolve_path %s failed with error: "
+ "%s\n"),
+ mask, nt_errstr(status));
+ return status;
+ }
+
+ status = cli_list(targetcli, targetpath, cp_clistate->attribute,
+ copy_fn, cp_clistate);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("listing %s failed with error: %s\n"),
+ mask, nt_errstr(status));
+ }
+
+ return status;
+}
+
+
+/**
+ * Set the top level directory permissions before we do any further copies.
+ * Should set up ACL inheritance.
+ **/
+
+bool copy_top_level_perms(struct net_context *c,
+ struct copy_clistate *cp_clistate,
+ const char *sharename)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ switch (net_mode_share) {
+ case NET_MODE_SHARE_MIGRATE:
+ DEBUG(3,("calling net_copy_fileattr for '.' directory in share %s\n", sharename));
+ nt_status = net_copy_fileattr(c,
+ cp_clistate->mem_ctx,
+ cp_clistate->cli_share_src,
+ cp_clistate->cli_share_dst,
+ "\\", "\\",
+ c->opt_acls? true : false,
+ c->opt_attrs? true : false,
+ c->opt_timestamps? true: false,
+ false);
+ break;
+ default:
+ d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share);
+ break;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("Could handle directory attributes for top level "
+ "directory of share %s. Error %s\n"),
+ sharename, nt_errstr(nt_status));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Sync all files inside a remote share to another share (over smb).
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_share_migrate_files_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct srvsvc_NetShareInfoCtr ctr_src;
+ uint32_t i;
+ uint32_t level = 502;
+ struct copy_clistate cp_clistate;
+ bool got_src_share = false;
+ bool got_dst_share = false;
+ const char *mask = "\\*";
+ char *dst = NULL;
+
+ dst = SMB_STRDUP(c->opt_destination?c->opt_destination:"127.0.0.1");
+ if (dst == NULL) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv,
+ &ctr_src);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ for (i = 0; i < ctr_src.ctr.ctr502->count; i++) {
+
+ struct srvsvc_NetShareInfo502 info502 =
+ ctr_src.ctr.ctr502->array[i];
+
+ if (!check_share_sanity(c, cli, info502.name, info502.type))
+ continue;
+
+ /* one might not want to mirror whole discs :) */
+ if (strequal(info502.name, "print$") || info502.name[1] == '$') {
+ d_printf(_("skipping [%s]: builtin/hidden share\n"),
+ info502.name);
+ continue;
+ }
+
+ switch (net_mode_share)
+ {
+ case NET_MODE_SHARE_MIGRATE:
+ printf("syncing");
+ break;
+ default:
+ d_fprintf(stderr, _("Unsupported mode %d\n"),
+ net_mode_share);
+ break;
+ }
+ printf(_(" [%s] files and directories %s ACLs, %s DOS "
+ "Attributes %s\n"),
+ info502.name,
+ c->opt_acls ? _("including") : _("without"),
+ c->opt_attrs ? _("including") : _("without"),
+ c->opt_timestamps ? _("(preserving timestamps)") : "");
+
+ cp_clistate.mem_ctx = mem_ctx;
+ cp_clistate.cli_share_src = NULL;
+ cp_clistate.cli_share_dst = NULL;
+ cp_clistate.cwd = NULL;
+ cp_clistate.attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY;
+ cp_clistate.c = c;
+
+ /* open share source */
+ nt_status = connect_to_service(c, &cp_clistate.cli_share_src,
+ smbXcli_conn_remote_sockaddr(cli->conn),
+ smbXcli_conn_remote_name(cli->conn),
+ info502.name, "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+ got_src_share = true;
+
+ if (net_mode_share == NET_MODE_SHARE_MIGRATE) {
+ /* open share destination */
+ nt_status = connect_to_service(c, &cp_clistate.cli_share_dst,
+ NULL, dst, info502.name, "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+ got_dst_share = true;
+ }
+
+ if (!copy_top_level_perms(c, &cp_clistate, info502.name)) {
+ d_fprintf(stderr, _("Could not handle the top level "
+ "directory permissions for the "
+ "share: %s\n"), info502.name);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ nt_status = sync_files(&cp_clistate, mask);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, _("could not handle files for share: "
+ "%s\n"), info502.name);
+ goto done;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (got_src_share)
+ cli_shutdown(cp_clistate.cli_share_src);
+
+ if (got_dst_share)
+ cli_shutdown(cp_clistate.cli_share_dst);
+
+ SAFE_FREE(dst);
+ return nt_status;
+
+}
+
+static int rpc_share_migrate_files(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net share migrate files\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate files to local server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_files_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate share-ACLs from a remote RPC server to the local RPC server.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_share_migrate_security_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ struct srvsvc_NetShareInfoCtr ctr_src;
+ union srvsvc_NetShareInfo info;
+ uint32_t i;
+ struct rpc_pipe_client *srvsvc_pipe = NULL;
+ struct cli_state *cli_dst = NULL;
+ uint32_t level = 502; /* includes secdesc */
+ uint32_t parm_error = 0;
+ struct dcerpc_binding_handle *b;
+
+ result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv,
+ &ctr_src);
+
+ if (!W_ERROR_IS_OK(result))
+ goto done;
+
+ /* connect destination PI_SRVSVC */
+ nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe,
+ &ndr_table_srvsvc);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ b = srvsvc_pipe->binding_handle;
+
+ for (i = 0; i < ctr_src.ctr.ctr502->count; i++) {
+
+ struct srvsvc_NetShareInfo502 info502 =
+ ctr_src.ctr.ctr502->array[i];
+
+ /* reset error-code */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ if (!check_share_sanity(c, cli, info502.name, info502.type))
+ continue;
+
+ printf(_("migrating: [%s], path: %s, comment: %s, including "
+ "share-ACLs\n"),
+ info502.name, info502.path, info502.comment);
+
+ if (c->opt_verbose)
+ display_sec_desc(info502.sd_buf.sd);
+
+ /* FIXME: shouldn't we be able to just set the security descriptor ? */
+ info.info502 = &info502;
+
+ /* finally modify the share on the dst server */
+ nt_status = dcerpc_srvsvc_NetShareSetInfo(b, mem_ctx,
+ srvsvc_pipe->desthost,
+ info502.name,
+ level,
+ &info,
+ &parm_error,
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("cannot set share-acl: %s\n"),
+ nt_errstr(nt_status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ nt_status = werror_to_ntstatus(result);
+ printf(_("cannot set share-acl: %s\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+
+ return nt_status;
+
+}
+
+/**
+ * Migrate share-acls from a RPC server to another.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_share_migrate_security(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc share migrate security\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate share-acls to local server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_security_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate shares (including share-definitions, share-acls and files with acls/attrs)
+ * from one server to another.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ *
+ **/
+static int rpc_share_migrate_all(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc share migrate all\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrates shares including all share settings"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ /* order is important. we don't want to be locked out by the share-acl
+ * before copying files - gd */
+
+ ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_shares_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_files_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_migrate_security_internals, argc,
+ argv);
+}
+
+
+/**
+ * 'net rpc share migrate' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+static int rpc_share_migrate(struct net_context *c, int argc, const char **argv)
+{
+
+ struct functable func[] = {
+ {
+ "all",
+ rpc_share_migrate_all,
+ NET_TRANSPORT_RPC,
+ N_("Migrate shares from remote to local server"),
+ N_("net rpc share migrate all\n"
+ " Migrate shares from remote to local server")
+ },
+ {
+ "files",
+ rpc_share_migrate_files,
+ NET_TRANSPORT_RPC,
+ N_("Migrate files from remote to local server"),
+ N_("net rpc share migrate files\n"
+ " Migrate files from remote to local server")
+ },
+ {
+ "security",
+ rpc_share_migrate_security,
+ NET_TRANSPORT_RPC,
+ N_("Migrate share-ACLs from remote to local server"),
+ N_("net rpc share migrate security\n"
+ " Migrate share-ACLs from remote to local server")
+ },
+ {
+ "shares",
+ rpc_share_migrate_shares,
+ NET_TRANSPORT_RPC,
+ N_("Migrate shares from remote to local server"),
+ N_("net rpc share migrate shares\n"
+ " Migrate shares from remote to local server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ net_mode_share = NET_MODE_SHARE_MIGRATE;
+
+ return net_run_function(c, argc, argv, "net rpc share migrate", func);
+}
+
+struct full_alias {
+ struct dom_sid sid;
+ uint32_t num_members;
+ struct dom_sid *members;
+};
+
+static int num_server_aliases;
+static struct full_alias *server_aliases;
+
+/*
+ * Add an alias to the static list.
+ */
+static void push_alias(struct full_alias *alias)
+{
+ size_t array_size;
+
+ if (server_aliases == NULL) {
+ server_aliases = talloc_array(NULL, struct full_alias, 100);
+ if (server_aliases == NULL) {
+ smb_panic("talloc_array failed");
+ }
+ }
+
+ array_size = talloc_array_length(server_aliases);
+ if (array_size == num_server_aliases) {
+ server_aliases = talloc_realloc(NULL, server_aliases,
+ struct full_alias, array_size + 100);
+ if (server_aliases == NULL) {
+ smb_panic("talloc_realloc failed");
+ }
+ }
+
+ server_aliases[num_server_aliases] = *alias;
+ num_server_aliases += 1;
+}
+
+/*
+ * For a specific domain on the server, fetch all the aliases
+ * and their members. Add all of them to the server_aliases.
+ */
+
+static NTSTATUS rpc_fetch_domain_aliases(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *connect_pol,
+ const struct dom_sid *domain_sid)
+{
+ uint32_t start_idx, max_entries, num_entries, i;
+ struct samr_SamArray *groups = NULL;
+ NTSTATUS result, status;
+ struct policy_handle domain_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* Get domain policy handle */
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ start_idx = 0;
+ max_entries = 250;
+
+ do {
+ status = dcerpc_samr_EnumDomainAliases(b, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ max_entries,
+ &num_entries,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ for (i = 0; i < num_entries; i++) {
+
+ struct policy_handle alias_pol;
+ struct full_alias alias;
+ struct lsa_SidArray sid_array;
+ int j;
+ NTSTATUS _result;
+
+ status = dcerpc_samr_OpenAlias(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ groups->entries[i].idx,
+ &alias_pol,
+ &_result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(_result)) {
+ status = _result;
+ goto done;
+ }
+
+ status = dcerpc_samr_GetMembersInAlias(b, mem_ctx,
+ &alias_pol,
+ &sid_array,
+ &_result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(_result)) {
+ status = _result;
+ goto done;
+ }
+
+ alias.num_members = sid_array.num_sids;
+
+ status = dcerpc_samr_Close(b, mem_ctx, &alias_pol, &_result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(_result)) {
+ status = _result;
+ goto done;
+ }
+
+ alias.members = NULL;
+
+ if (alias.num_members > 0) {
+ alias.members = SMB_MALLOC_ARRAY(struct dom_sid, alias.num_members);
+ if (alias.members == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (j = 0; j < alias.num_members; j++)
+ sid_copy(&alias.members[j],
+ sid_array.sids[j].sid);
+ }
+
+ sid_compose(&alias.sid, domain_sid,
+ groups->entries[i].idx);
+
+ push_alias(&alias);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ status = NT_STATUS_OK;
+
+ done:
+ dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
+
+ return status;
+}
+
+/*
+ * Dump server_aliases as names for debugging purposes.
+ */
+
+static NTSTATUS rpc_aliaslist_dump(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ uint32_t i;
+ NTSTATUS result;
+ struct policy_handle lsa_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &lsa_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (i=0; i<num_server_aliases; i++) {
+ char **names;
+ char **domains;
+ enum lsa_SidType *types;
+ int j;
+
+ struct full_alias *alias = &server_aliases[i];
+
+ result = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &lsa_pol, 1,
+ &alias->sid,
+ &domains, &names, &types);
+ if (!NT_STATUS_IS_OK(result))
+ continue;
+
+ DEBUG(1, ("%s\\%s %d: ", domains[0], names[0], types[0]));
+
+ if (alias->num_members == 0) {
+ DEBUG(1, ("\n"));
+ continue;
+ }
+
+ result = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &lsa_pol,
+ alias->num_members,
+ alias->members,
+ &domains, &names, &types);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED))
+ continue;
+
+ for (j=0; j<alias->num_members; j++)
+ DEBUG(1, ("%s\\%s (%d); ",
+ domains[j] ? domains[j] : "*unknown*",
+ names[j] ? names[j] : "*unknown*",types[j]));
+ DEBUG(1, ("\n"));
+ }
+
+ dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ * Fetch a list of all server aliases and their members into
+ * server_aliases.
+ */
+
+static NTSTATUS rpc_aliaslist_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS result, status;
+ struct policy_handle connect_pol;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol,
+ &global_sid_Builtin);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol,
+ domain_sid);
+
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ done:
+ return status;
+}
+
+struct user_token {
+ fstring name;
+ struct security_token *token;
+};
+
+static void add_sid_to_token(struct security_token *token, const struct dom_sid *sid)
+{
+ NTSTATUS status = add_sid_to_array_unique(token, sid,
+ &token->sids, &token->num_sids);
+ /*
+ * This is both very unlikely and mostly harmless in a command
+ * line tool
+ */
+ SMB_ASSERT(NT_STATUS_IS_OK(status));
+}
+
+static void init_user_token(struct user_token *token_list,
+ struct security_token **token,
+ struct dom_sid *user_sid)
+{
+ /*
+ * This token is not from the auth stack, only has user SIDs
+ * and must fail if conditional ACEs are found in the security
+ * descriptor
+ */
+ *token = security_token_initialise(token_list, CLAIMS_EVALUATION_INVALID_STATE);
+ SMB_ASSERT(*token);
+
+ add_sid_to_token(*token,
+ user_sid);
+
+ add_sid_to_token(*token,
+ &global_sid_World);
+
+ add_sid_to_token(*token,
+ &global_sid_Network);
+
+ add_sid_to_token(*token,
+ &global_sid_Authenticated_Users);
+}
+
+static void dump_user_token(struct user_token *token)
+{
+ uint32_t i;
+
+ d_printf("%s\n", token->name);
+
+ for (i=0; i<token->token->num_sids; i++) {
+ struct dom_sid_buf buf;
+ d_printf(" %s\n",
+ dom_sid_str_buf(&token->token->sids[i], &buf));
+ }
+}
+
+static bool is_alias_member(struct dom_sid *sid, struct full_alias *alias)
+{
+ uint32_t i;
+
+ for (i=0; i<alias->num_members; i++) {
+ if (dom_sid_equal(sid, &alias->members[i])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void collect_sid_memberships(struct security_token *token, struct dom_sid sid)
+{
+ int i;
+
+ for (i=0; i<num_server_aliases; i++) {
+ if (is_alias_member(&sid, &server_aliases[i]))
+ add_sid_to_token(token, &server_aliases[i].sid);
+ }
+}
+
+/*
+ * We got a user token with all the SIDs we can know about without asking the
+ * server directly. These are the user and domain group sids. All of these can
+ * be members of aliases. So scan the list of aliases for each of the SIDs and
+ * add them to the token.
+ */
+
+static void collect_alias_memberships(struct security_token *token)
+{
+ int num_global_sids = token->num_sids;
+ int i;
+
+ for (i=0; i<num_global_sids; i++) {
+ collect_sid_memberships(token, token->sids[i]);
+ }
+}
+
+static bool get_user_sids(const char *domain, const char *user,
+ struct user_token *token_list,
+ struct security_token **token)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ enum wbcSidType type;
+ fstring full_name;
+ struct wbcDomainSid wsid;
+ char sid_str[WBC_SID_STRING_BUFLEN];
+ struct dom_sid user_sid;
+ uint32_t num_groups;
+ gid_t *groups = NULL;
+ uint32_t i;
+
+ fstr_sprintf(full_name, "%s%c%s",
+ domain, *lp_winbind_separator(), user);
+
+ /* First let's find out the user sid */
+
+ wbc_status = wbcLookupName(domain, user, &wsid, &type);
+
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, ("winbind could not find %s: %s\n",
+ full_name, wbcErrorString(wbc_status)));
+ return false;
+ }
+
+ wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str));
+
+ if (type != WBC_SID_NAME_USER) {
+ DEBUG(1, ("%s is not a user\n", full_name));
+ return false;
+ }
+
+ if (!string_to_sid(&user_sid, sid_str)) {
+ DEBUG(1,("Could not convert sid %s from string\n", sid_str));
+ return false;
+ }
+
+ init_user_token(token_list, token, &user_sid);
+
+ /* And now the groups winbind knows about */
+
+ wbc_status = wbcGetGroups(full_name, &num_groups, &groups);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, ("winbind could not get groups of %s: %s\n",
+ full_name, wbcErrorString(wbc_status)));
+ return false;
+ }
+
+ for (i = 0; i < num_groups; i++) {
+ gid_t gid = groups[i];
+ struct dom_sid sid;
+ bool ok;
+
+ wbc_status = wbcGidToSid(gid, &wsid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, ("winbind could not find SID of gid %u: %s\n",
+ (unsigned int)gid, wbcErrorString(wbc_status)));
+ wbcFreeMemory(groups);
+ return false;
+ }
+
+ wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str));
+
+ DEBUG(3, (" %s\n", sid_str));
+
+ ok = string_to_sid(&sid, sid_str);
+ if (!ok) {
+ DEBUG(1, ("Failed to convert string to SID\n"));
+ wbcFreeMemory(groups);
+ return false;
+ }
+ add_sid_to_token(*token, &sid);
+ }
+ wbcFreeMemory(groups);
+
+ return true;
+}
+
+/**
+ * Get a list of all user tokens we want to look at
+ **/
+
+static bool get_user_tokens(struct net_context *c, int *num_tokens,
+ struct user_token **user_tokens)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ uint32_t i, num_users;
+ const char **users;
+ struct user_token *result;
+ TALLOC_CTX *frame = NULL;
+
+ if (lp_winbind_use_default_domain() &&
+ (c->opt_target_workgroup == NULL)) {
+ d_fprintf(stderr, _("winbind use default domain = yes set, "
+ "please specify a workgroup\n"));
+ return false;
+ }
+
+ /* Send request to winbind daemon */
+
+ wbc_status = wbcListUsers(NULL, &num_users, &users);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, (_("winbind could not list users: %s\n"),
+ wbcErrorString(wbc_status)));
+ return false;
+ }
+
+ result = talloc_zero_array(NULL, struct user_token, num_users);
+
+ if (result == NULL) {
+ DBG_ERR("Could not talloc token array\n");
+ wbcFreeMemory(users);
+ return false;
+ }
+
+ frame = talloc_stackframe();
+ for (i=0; i < num_users; i++) {
+ fstring domain, user;
+ char *p;
+
+ fstrcpy(result[i].name, users[i]);
+
+ p = strchr(users[i], *lp_winbind_separator());
+
+ DEBUG(3, ("%s\n", users[i]));
+
+ if (p == NULL) {
+ fstrcpy(domain, c->opt_target_workgroup);
+ fstrcpy(user, users[i]);
+ } else {
+ *p++ = '\0';
+ fstrcpy(domain, users[i]);
+ if (!strupper_m(domain)) {
+ DEBUG(1, ("strupper_m %s failed\n", domain));
+ wbcFreeMemory(users);
+ return false;
+ }
+ fstrcpy(user, p);
+ }
+
+ get_user_sids(domain, user, result, &(result[i].token));
+ }
+ TALLOC_FREE(frame);
+ wbcFreeMemory(users);
+
+ *num_tokens = num_users;
+ *user_tokens = result;
+
+ return true;
+}
+
+static bool get_user_tokens_from_file(FILE *f,
+ int *num_tokens,
+ struct user_token **tokens)
+{
+ struct user_token *token = NULL;
+
+ while (!feof(f)) {
+ fstring line;
+
+ if (fgets(line, sizeof(line)-1, f) == NULL) {
+ return true;
+ }
+
+ if ((strlen(line) > 0) && (line[strlen(line)-1] == '\n')) {
+ line[strlen(line)-1] = '\0';
+ }
+
+ if (line[0] == ' ') {
+ /* We have a SID */
+
+ struct dom_sid sid;
+ if(!string_to_sid(&sid, &line[1])) {
+ DEBUG(1,("get_user_tokens_from_file: Could "
+ "not convert sid %s \n",&line[1]));
+ return false;
+ }
+
+ if (token == NULL) {
+ DEBUG(0, ("File does not begin with username\n"));
+ return false;
+ }
+
+ add_sid_to_token(token->token, &sid);
+ continue;
+ }
+
+ /* And a new user... */
+
+ *num_tokens += 1;
+ *tokens = talloc_realloc(NULL,
+ *tokens,
+ struct user_token,
+ *num_tokens);
+ if (*tokens == NULL) {
+ DBG_ERR("Could not talloc_realloc tokens\n");
+ return false;
+ }
+
+ token = &((*tokens)[*num_tokens-1]);
+
+ if (strlcpy(token->name, line, sizeof(token->name)) >= sizeof(token->name)) {
+ return false;
+ }
+ token->token
+ = security_token_initialise(*tokens,
+ CLAIMS_EVALUATION_INVALID_STATE);
+ if (token->token == NULL) {
+ DBG_ERR("security_token_initialise() failed: "
+ "Could not allocate security_token with \n");
+ return false;
+ }
+
+ continue;
+ }
+
+ return false;
+}
+
+
+/*
+ * Show the list of all users that have access to a share
+ */
+
+static void show_userlist(struct rpc_pipe_client *pipe_hnd,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *netname,
+ int num_tokens,
+ struct user_token *tokens)
+{
+ uint16_t fnum;
+ struct security_descriptor *share_sd = NULL;
+ struct security_descriptor *root_sd = NULL;
+ int i;
+ union srvsvc_NetShareInfo info;
+ WERROR result;
+ NTSTATUS status;
+ struct smbXcli_tcon *orig_tcon = NULL;
+ char *orig_share = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx,
+ pipe_hnd->desthost,
+ netname,
+ 502,
+ &info,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ DEBUG(1, ("Could not query secdesc for share %s\n",
+ netname));
+ return;
+ }
+
+ share_sd = info.info502->sd_buf.sd;
+ if (share_sd == NULL) {
+ DEBUG(1, ("Got no secdesc for share %s\n",
+ netname));
+ }
+
+ if (cli_state_has_tcon(cli)) {
+ cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
+ }
+
+ if (!NT_STATUS_IS_OK(cli_tree_connect(cli, netname, "A:", NULL))) {
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(cli_ntcreate(cli, "\\", 0, READ_CONTROL_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x0, 0x0, &fnum, NULL))) {
+ cli_query_secdesc(cli, fnum, mem_ctx, &root_sd);
+ }
+
+ for (i=0; i<num_tokens; i++) {
+ uint32_t acc_granted;
+
+ if (share_sd != NULL) {
+ status = se_access_check(share_sd, tokens[i].token,
+ 1, &acc_granted);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not check share_sd for "
+ "user %s\n",
+ tokens[i].name));
+ continue;
+ }
+ }
+
+ if (root_sd == NULL) {
+ d_printf(" %s\n", tokens[i].name);
+ continue;
+ }
+
+ status = se_access_check(root_sd, tokens[i].token,
+ 1, &acc_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not check root_sd for user %s\n",
+ tokens[i].name));
+ continue;
+ }
+ d_printf(" %s\n", tokens[i].name);
+ }
+
+ if (fnum != (uint16_t)-1)
+ cli_close(cli, fnum);
+ cli_tdis(cli);
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+
+ return;
+}
+
+/**
+ * List shares on a remote RPC server, including the security descriptors.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_share_allowedusers_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ bool r;
+ FILE *f;
+ NTSTATUS nt_status = NT_STATUS_OK;
+ uint32_t total_entries = 0;
+ uint32_t resume_handle = 0;
+ uint32_t preferred_len = 0xffffffff;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = NULL;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 ctr1;
+ WERROR result;
+
+ struct user_token *tokens = NULL;
+ int num_tokens = 0;
+
+ if (argc == 0) {
+ f = stdin;
+ } else {
+ if (strequal(argv[0], "-")) {
+ f = stdin;
+ } else {
+ f = fopen(argv[0], "r");
+ }
+ argv++;
+ argc--;
+ }
+
+ if (f == NULL) {
+ DEBUG(0, ("Could not open userlist: %s\n", strerror(errno)));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ r = get_user_tokens_from_file(f, &num_tokens, &tokens);
+
+ if (f != stdin)
+ fclose(f);
+
+ if (!r) {
+ DEBUG(0, ("Could not read users from file\n"));
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ for (i=0; i<num_tokens; i++)
+ collect_alias_memberships(tokens[i].token);
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(ctr1);
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ b = pipe_hnd->binding_handle;
+
+ if (argc != 0) {
+ /* Show results only for shares listed on the command line. */
+ while (*argv) {
+ const char *netname = *argv++;
+ d_printf("%s\n", netname);
+ show_userlist(pipe_hnd, cli, mem_ctx, netname,
+ num_tokens, tokens);
+ }
+ goto done;
+ }
+
+ /* Issue the NetShareEnum RPC call and retrieve the response */
+ nt_status = dcerpc_srvsvc_NetShareEnumAll(b,
+ talloc_tos(),
+ pipe_hnd->desthost,
+ &info_ctr,
+ preferred_len,
+ &total_entries,
+ &resume_handle,
+ &result);
+
+ /* Was it successful? */
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ /* Nope. Go clean up. */
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ /* Nope. Go clean up. */
+ nt_status = werror_to_ntstatus(result);
+ goto done;
+ }
+
+ if (total_entries == 0) {
+ goto done;
+ }
+
+ /* For each returned entry... */
+ for (i = 0; i < info_ctr.ctr.ctr1->count; i++) {
+ const char *netname = info_ctr.ctr.ctr1->array[i].name;
+
+ if (info_ctr.ctr.ctr1->array[i].type != STYPE_DISKTREE) {
+ continue;
+ }
+
+ d_printf("%s\n", netname);
+
+ show_userlist(pipe_hnd, cli, mem_ctx, netname,
+ num_tokens, tokens);
+ }
+ done:
+ TALLOC_FREE(tokens);
+ TALLOC_FREE(server_aliases);
+
+ return nt_status;
+}
+
+static int rpc_share_allowedusers(struct net_context *c, int argc,
+ const char **argv)
+{
+ int result;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc share allowedusers\n"
+ " %s\n",
+ _("Usage:"),
+ _("List allowed users"));
+ return 0;
+ }
+
+ result = run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_aliaslist_internals,
+ argc, argv);
+ if (result != 0)
+ return result;
+
+ result = run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_aliaslist_dump,
+ argc, argv);
+ if (result != 0)
+ return result;
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0,
+ rpc_share_allowedusers_internals,
+ argc, argv);
+}
+
+int net_usersidlist(struct net_context *c, int argc, const char **argv)
+{
+ int num_tokens = 0;
+ struct user_token *tokens = NULL;
+ int i;
+
+ if (argc != 0) {
+ net_usersidlist_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (!get_user_tokens(c, &num_tokens, &tokens)) {
+ DEBUG(0, ("Could not get the user/sid list\n"));
+ return -1;
+ }
+
+ for (i=0; i<num_tokens; i++) {
+ dump_user_token(&tokens[i]);
+ }
+
+ TALLOC_FREE(tokens);
+ return 0;
+}
+
+int net_usersidlist_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net usersidlist\n"
+ "\tprints out a list of all users the running winbind knows\n"
+ "\tabout, together with all their SIDs. This is used as\n"
+ "\tinput to the 'net rpc share allowedusers' command.\n\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+/**
+ * 'net rpc share' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_share(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "add",
+ rpc_share_add,
+ NET_TRANSPORT_RPC,
+ N_("Add share"),
+ N_("net rpc share add\n"
+ " Add share")
+ },
+ {
+ "delete",
+ rpc_share_delete,
+ NET_TRANSPORT_RPC,
+ N_("Remove share"),
+ N_("net rpc share delete\n"
+ " Remove share")
+ },
+ {
+ "allowedusers",
+ rpc_share_allowedusers,
+ NET_TRANSPORT_RPC,
+ N_("List allowed users"),
+ N_("net rpc share allowedusers\n"
+ " List allowed users")
+ },
+ {
+ "migrate",
+ rpc_share_migrate,
+ NET_TRANSPORT_RPC,
+ N_("Migrate share to local server"),
+ N_("net rpc share migrate\n"
+ " Migrate share to local server")
+ },
+ {
+ "list",
+ rpc_share_list,
+ NET_TRANSPORT_RPC,
+ N_("List shares"),
+ N_("net rpc share list\n"
+ " List shares")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc share\n"
+ " List shares\n"
+ " Alias for net rpc share list\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ return rpc_share_list(c, argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc share", func);
+}
+
+static NTSTATUS rpc_sh_share_list(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+
+ return werror_to_ntstatus(W_ERROR(rpc_share_list(c, argc, argv)));
+}
+
+static NTSTATUS rpc_sh_share_add(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ uint32_t parm_err = 0;
+ struct SHARE_INFO_2 i2;
+
+ if ((argc < 2) || (argc > 3)) {
+ d_fprintf(stderr, _("Usage: %s <share> <path> [comment]\n"),
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ i2.shi2_netname = argv[0];
+ i2.shi2_type = STYPE_DISKTREE;
+ i2.shi2_remark = (argc == 3) ? argv[2] : "";
+ i2.shi2_permissions = 0;
+ i2.shi2_max_uses = 0;
+ i2.shi2_current_uses = 0;
+ i2.shi2_path = argv[1];
+ i2.shi2_passwd = NULL;
+
+ status = NetShareAdd(pipe_hnd->desthost,
+ 2,
+ (uint8_t *)&i2,
+ &parm_err);
+
+ return werror_to_ntstatus(W_ERROR(status));
+}
+
+static NTSTATUS rpc_sh_share_delete(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, "%s %s <share>\n", _("Usage:"), ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return werror_to_ntstatus(W_ERROR(NetShareDel(pipe_hnd->desthost, argv[0], 0)));
+}
+
+static NTSTATUS rpc_sh_share_info(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ union srvsvc_NetShareInfo info;
+ WERROR result;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1) {
+ d_fprintf(stderr, "%s %s <share>\n", _("Usage:"), ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx,
+ pipe_hnd->desthost,
+ argv[0],
+ 2,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ d_printf(_("Name: %s\n"), info.info2->name);
+ d_printf(_("Comment: %s\n"), info.info2->comment);
+ d_printf(_("Path: %s\n"), info.info2->path);
+ d_printf(_("Password: %s\n"), info.info2->password);
+
+ done:
+ return werror_to_ntstatus(result);
+}
+
+struct rpc_sh_cmd *net_rpc_share_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx)
+{
+ static struct rpc_sh_cmd cmds[] = {
+
+ { "list", NULL, &ndr_table_srvsvc, rpc_sh_share_list,
+ N_("List available shares") },
+
+ { "add", NULL, &ndr_table_srvsvc, rpc_sh_share_add,
+ N_("Add a share") },
+
+ { "delete", NULL, &ndr_table_srvsvc, rpc_sh_share_delete,
+ N_("Delete a share") },
+
+ { "info", NULL, &ndr_table_srvsvc, rpc_sh_share_info,
+ N_("Get information about a share") },
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ return cmds;
+}
+
+/****************************************************************************/
+
+static int rpc_file_usage(struct net_context *c, int argc, const char **argv)
+{
+ return net_file_usage(c, argc, argv);
+}
+
+/**
+ * Close a file on a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_file_close(struct net_context *c, int argc, const char **argv)
+{
+ if (argc < 1 || c->display_usage) {
+ return rpc_file_usage(c, argc, argv);
+ }
+
+ return NetFileClose(c->opt_host, atoi(argv[0]));
+}
+
+/**
+ * Formatted print of open file info
+ *
+ * @param r struct FILE_INFO_3 contents
+ **/
+
+static void display_file_info_3(struct FILE_INFO_3 *r)
+{
+ d_printf("%-7.1" PRIu32 " %-20.20s 0x%-4.2x %-6.1u %s\n",
+ r->fi3_id, r->fi3_username, r->fi3_permissions,
+ r->fi3_num_locks, r->fi3_pathname);
+}
+
+/**
+ * List files for a user on a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success)..
+ **/
+
+static int rpc_file_user(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+ uint32_t preferred_len = 0xffffffff, i;
+ char *username=NULL;
+ uint32_t total_entries = 0;
+ uint32_t entries_read = 0;
+ uint32_t resume_handle = 0;
+ struct FILE_INFO_3 *i3 = NULL;
+
+ if (c->display_usage) {
+ return rpc_file_usage(c, argc, argv);
+ }
+
+ /* if argc > 0, must be user command */
+ if (argc > 0) {
+ username = smb_xstrdup(argv[0]);
+ }
+
+ status = NetFileEnum(c->opt_host,
+ NULL,
+ username,
+ 3,
+ (uint8_t **)(void *)&i3,
+ preferred_len,
+ &entries_read,
+ &total_entries,
+ &resume_handle);
+
+ if (status != 0) {
+ goto done;
+ }
+
+ /* Display results */
+
+ d_printf(_(
+ "\nEnumerating open files on remote server:\n\n"
+ "\nFileId Opened by Perms Locks Path"
+ "\n------ --------- ----- ----- ---- \n"));
+ for (i = 0; i < entries_read; i++) {
+ display_file_info_3(&i3[i]);
+ }
+ done:
+ SAFE_FREE(username);
+ return status;
+}
+
+/**
+ * 'net rpc file' entrypoint.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc_file(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "close",
+ rpc_file_close,
+ NET_TRANSPORT_RPC,
+ N_("Close opened file"),
+ N_("net rpc file close\n"
+ " Close opened file")
+ },
+ {
+ "user",
+ rpc_file_user,
+ NET_TRANSPORT_RPC,
+ N_("List files opened by user"),
+ N_("net rpc file user\n"
+ " List files opened by user")
+ },
+#if 0
+ {
+ "info",
+ rpc_file_info,
+ NET_TRANSPORT_RPC,
+ N_("Display information about opened file"),
+ N_("net rpc file info\n"
+ " Display information about opened file")
+ },
+#endif
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rpc file\n"
+ " List opened files\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ return rpc_file_user(c, argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc file", func);
+}
+
+/**
+ * ABORT the shutdown of a remote RPC Server, over initshutdown pipe.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_shutdown_abort_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ WERROR result;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_initshutdown_Abort(b, mem_ctx, NULL, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (W_ERROR_IS_OK(result)) {
+ d_printf(_("\nShutdown successfully aborted\n"));
+ DEBUG(5,("cmd_shutdown_abort: query succeeded\n"));
+ } else
+ DEBUG(5,("cmd_shutdown_abort: query failed\n"));
+
+ return werror_to_ntstatus(result);
+}
+
+/**
+ * ABORT the shutdown of a remote RPC Server, over winreg pipe.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_reg_shutdown_abort_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ WERROR werr;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ result = dcerpc_winreg_AbortSystemShutdown(b, mem_ctx, NULL, &werr);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+ return result;
+ }
+ if (W_ERROR_IS_OK(werr)) {
+ d_printf(_("\nShutdown successfully aborted\n"));
+ DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n"));
+ } else
+ DEBUG(5,("cmd_reg_abort_shutdown: query failed\n"));
+
+ return werror_to_ntstatus(werr);
+}
+
+/**
+ * ABORT the shutdown of a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_shutdown_abort(struct net_context *c, int argc,
+ const char **argv)
+{
+ int rc = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc abortshutdown\n"
+ " %s\n",
+ _("Usage:"),
+ _("Abort a scheduled shutdown"));
+ return 0;
+ }
+
+ rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0,
+ rpc_shutdown_abort_internals, argc, argv);
+
+ if (rc == 0)
+ return rc;
+
+ DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n"));
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_reg_shutdown_abort_internals,
+ argc, argv);
+}
+
+/**
+ * Shut down a remote RPC Server via initshutdown pipe.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_init_shutdown_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ WERROR result;
+ const char *msg = N_("This machine will be shutdown shortly");
+ uint32_t timeout = 20;
+ struct lsa_StringLarge msg_string;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (c->opt_comment) {
+ msg = c->opt_comment;
+ }
+ if (c->opt_timeout) {
+ timeout = c->opt_timeout;
+ }
+
+ msg_string.string = msg;
+
+ /* create an entry */
+ status = dcerpc_initshutdown_Init(b, mem_ctx, NULL,
+ &msg_string, timeout, c->opt_force, c->opt_reboot,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (W_ERROR_IS_OK(result)) {
+ d_printf(_("\nShutdown of remote machine succeeded\n"));
+ DEBUG(5,("Shutdown of remote machine succeeded\n"));
+ } else {
+ DEBUG(1,("Shutdown of remote machine failed!\n"));
+ }
+ return werror_to_ntstatus(result);
+}
+
+/**
+ * Shut down a remote RPC Server via winreg pipe.
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the remote server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_reg_shutdown_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ const char *msg = N_("This machine will be shutdown shortly");
+ uint32_t timeout = 20;
+ struct lsa_StringLarge msg_string;
+ NTSTATUS result;
+ WERROR werr;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (c->opt_comment) {
+ msg = c->opt_comment;
+ }
+ msg_string.string = msg;
+
+ if (c->opt_timeout) {
+ timeout = c->opt_timeout;
+ }
+
+ /* create an entry */
+ result = dcerpc_winreg_InitiateSystemShutdown(b, mem_ctx, NULL,
+ &msg_string, timeout, c->opt_force, c->opt_reboot,
+ &werr);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "\nShutdown of remote machine failed\n");
+ return result;
+ }
+
+ if (W_ERROR_IS_OK(werr)) {
+ d_printf(_("\nShutdown of remote machine succeeded\n"));
+ } else {
+ d_fprintf(stderr, "\nShutdown of remote machine failed\n");
+ if ( W_ERROR_EQUAL(werr, WERR_MACHINE_LOCKED) )
+ d_fprintf(stderr, "\nMachine locked, use -f switch to force\n");
+ else
+ d_fprintf(stderr, "\nresult was: %s\n", win_errstr(werr));
+ }
+
+ return werror_to_ntstatus(werr);
+}
+
+/**
+ * Shut down a remote RPC server.
+ *
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+
+static int rpc_shutdown(struct net_context *c, int argc, const char **argv)
+{
+ int rc = -1;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc shutdown\n"
+ " %s\n",
+ _("Usage:"),
+ _("Shut down a remote RPC server"));
+ return 0;
+ }
+
+ rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0,
+ rpc_init_shutdown_internals, argc, argv);
+
+ if (rc) {
+ DEBUG(1, ("initshutdown pipe failed, trying winreg pipe\n"));
+ rc = run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_reg_shutdown_internals, argc, argv);
+ }
+
+ return rc;
+}
+
+/***************************************************************************
+ NT Domain trusts code (i.e. 'net rpc trustdom' functionality)
+ ***************************************************************************/
+
+/**
+ * Add interdomain trust account to the RPC server.
+ * All parameters (except for argc and argv) are passed by run_rpc_command
+ * function.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return normal NTSTATUS return code.
+ */
+
+static NTSTATUS rpc_trustdom_add_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle connect_pol, domain_pol, user_pol;
+ NTSTATUS status, result;
+ char *acct_name;
+ struct lsa_String lsa_acct_name;
+ uint32_t acb_info;
+ uint32_t acct_flags=0;
+ uint32_t user_rid;
+ uint32_t access_granted = 0;
+ union samr_UserInfo info;
+ unsigned int orig_timeout;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ DATA_BLOB session_key = data_blob_null;
+ TALLOC_CTX *frame = NULL;
+
+ if (argc != 2) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc trustdom add <domain_name> "
+ "<trust password>\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ frame = talloc_stackframe();
+
+ /*
+ * Make valid trusting domain account (ie. uppercased and with '$' appended)
+ */
+
+ if (asprintf(&acct_name, "%s$", argv[0]) < 0) {
+ status = NT_STATUS_NO_MEMORY;
+ }
+
+ if (!strupper_m(acct_name)) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto done;
+ }
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ status = cli_get_session_key(frame, pipe_hnd, &session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Error getting session_key of SAM pipe. Error was %s\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ /* Get samr policy handle */
+ status = dcerpc_samr_Connect2(b, frame,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, frame,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* This call can take a long time - allow the server to time out.
+ * 35 seconds should do it. */
+
+ orig_timeout = rpccli_set_timeout(pipe_hnd, 35000);
+
+ /* Create trusting domain's account */
+ acb_info = ACB_NORMAL;
+ acct_flags = SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE |
+ SEC_STD_WRITE_DAC | SEC_STD_DELETE |
+ SAMR_USER_ACCESS_SET_PASSWORD |
+ SAMR_USER_ACCESS_GET_ATTRIBUTES |
+ SAMR_USER_ACCESS_SET_ATTRIBUTES;
+
+ status = dcerpc_samr_CreateUser2(b, frame,
+ &domain_pol,
+ &lsa_acct_name,
+ acb_info,
+ acct_flags,
+ &user_pol,
+ &access_granted,
+ &user_rid,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ /* And restore our original timeout. */
+ rpccli_set_timeout(pipe_hnd, orig_timeout);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_printf(_("net rpc trustdom add: create user %s failed %s\n"),
+ acct_name, nt_errstr(result));
+ goto done;
+ }
+
+ {
+ struct samr_CryptPassword crypt_pwd;
+
+ ZERO_STRUCT(info.info23);
+
+ status = init_samr_CryptPassword(argv[1],
+ &session_key,
+ &crypt_pwd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ info.info23.info.fields_present = SAMR_FIELD_ACCT_FLAGS |
+ SAMR_FIELD_NT_PASSWORD_PRESENT;
+ info.info23.info.acct_flags = ACB_DOMTRUST;
+ info.info23.password = crypt_pwd;
+
+ status = dcerpc_samr_SetUserInfo2(b, frame,
+ &user_pol,
+ 23,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ DEBUG(0,("Could not set trust account password: %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ status = NT_STATUS_OK;
+ done:
+ SAFE_FREE(acct_name);
+ data_blob_clear_free(&session_key);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/**
+ * Create interdomain trust account for a remote domain.
+ *
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ **/
+
+static int rpc_trustdom_add(struct net_context *c, int argc, const char **argv)
+{
+ if (argc > 0 && !c->display_usage) {
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_trustdom_add_internals, argc, argv);
+ } else {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc trustdom add <domain_name> <trust "
+ "password>\n"));
+ return -1;
+ }
+}
+
+
+/**
+ * Remove interdomain trust account from the RPC server.
+ * All parameters (except for argc and argv) are passed by run_rpc_command
+ * function.
+ *
+ * @param c A net_context structure.
+ * @param domain_sid The domain sid acquired from the server.
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return normal NTSTATUS return code.
+ */
+
+static NTSTATUS rpc_trustdom_del_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle connect_pol, domain_pol, user_pol;
+ NTSTATUS status, result;
+ char *acct_name;
+ struct dom_sid trust_acct_sid;
+ struct samr_Ids user_rids, name_types;
+ struct lsa_String lsa_acct_name;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc trustdom del <domain_name>\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Make valid trusting domain account (ie. uppercased and with '$' appended)
+ */
+ acct_name = talloc_asprintf(mem_ctx, "%s$", argv[0]);
+
+ if (acct_name == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ if (!strupper_m(acct_name)) {
+ TALLOC_FREE(acct_name);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* Get samr policy handle */
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ discard_const_p(struct dom_sid2, domain_sid),
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ status = dcerpc_samr_LookupNames(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &user_rids,
+ &name_types,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("net rpc trustdom del: LookupNames on user %s "
+ "failed %s\n"),
+ acct_name, nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_printf(_("net rpc trustdom del: LookupNames on user %s "
+ "failed %s\n"),
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+ if (user_rids.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+ if (name_types.count != 1) {
+ status = NT_STATUS_INVALID_NETWORK_RESPONSE;
+ goto done;
+ }
+
+ status = dcerpc_samr_OpenUser(b, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids.ids[0],
+ &user_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("net rpc trustdom del: OpenUser on user %s failed "
+ "%s\n"),
+ acct_name, nt_errstr(status) );
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_printf(_("net rpc trustdom del: OpenUser on user %s failed "
+ "%s\n"),
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+
+ /* append the rid to the domain sid */
+ if (!sid_compose(&trust_acct_sid, domain_sid, user_rids.ids[0])) {
+ goto done;
+ }
+
+ /* remove the sid */
+
+ status = dcerpc_samr_RemoveMemberFromForeignDomain(b, mem_ctx,
+ &user_pol,
+ &trust_acct_sid,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain"
+ " on user %s failed %s\n"),
+ acct_name, nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain"
+ " on user %s failed %s\n"),
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+
+
+ /* Delete user */
+
+ status = dcerpc_samr_DeleteUser(b, mem_ctx,
+ &user_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("net rpc trustdom del: DeleteUser on user %s failed "
+ "%s\n"),
+ acct_name, nt_errstr(status));
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ result = status;
+ d_printf(_("net rpc trustdom del: DeleteUser on user %s failed "
+ "%s\n"),
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf(_("Could not set trust account password: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ done:
+ return status;
+}
+
+/**
+ * Delete interdomain trust account for a remote domain.
+ *
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ **/
+
+static int rpc_trustdom_del(struct net_context *c, int argc, const char **argv)
+{
+ if (argc > 0 && !c->display_usage) {
+ return run_rpc_command(c, NULL, &ndr_table_samr, 0,
+ rpc_trustdom_del_internals, argc, argv);
+ } else {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc trustdom del <domain>\n"));
+ return -1;
+ }
+}
+
+static NTSTATUS rpc_trustdom_get_pdc(struct net_context *c,
+ struct cli_state *cli,
+ TALLOC_CTX *mem_ctx,
+ const char *domain_name)
+{
+ char *dc_name = NULL;
+ const char *buffer = NULL;
+ struct rpc_pipe_client *netr;
+ NTSTATUS status;
+ WERROR result;
+ struct dcerpc_binding_handle *b;
+
+ /* Use NetServerEnum2 */
+
+ if (cli_get_pdc_name(cli, domain_name, &dc_name)) {
+ SAFE_FREE(dc_name);
+ return NT_STATUS_OK;
+ }
+
+ DEBUG(1,("NetServerEnum2 error: Couldn't find primary domain controller "
+ "for domain %s\n", domain_name));
+
+ /* Try netr_GetDcName */
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon,
+ &netr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ b = netr->binding_handle;
+
+ status = dcerpc_netr_GetDcName(b, mem_ctx,
+ netr->desthost,
+ domain_name,
+ &buffer,
+ &result);
+ TALLOC_FREE(netr);
+
+ if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(result)) {
+ return status;
+ }
+
+ DEBUG(1,("netr_GetDcName error: Couldn't find primary domain controller "
+ "for domain %s\n", domain_name));
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/**
+ * Establish trust relationship to a trusting domain.
+ * Interdomain account must already be created on remote PDC.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ **/
+
+static int rpc_trustdom_establish(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct cli_state *cli = NULL;
+ struct sockaddr_storage server_ss;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct policy_handle connect_hnd;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status, result;
+ struct dom_sid *domain_sid;
+ char* domain_name;
+ char* acct_name;
+ const char *pwd = NULL;
+ fstring pdc_name;
+ union lsa_PolicyInformation *info = NULL;
+ struct dcerpc_binding_handle *b;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ /*
+ * Connect to \\server\ipc$ as 'our domain' account with password
+ */
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc trustdom establish <domain_name>\n"));
+ return -1;
+ }
+
+ domain_name = smb_xstrdup(argv[0]);
+ if (!strupper_m(domain_name)) {
+ SAFE_FREE(domain_name);
+ return -1;
+ }
+
+ /* account name used at first is our domain's name with '$' */
+ if (asprintf(&acct_name, "%s$", lp_workgroup()) == -1) {
+ return -1;
+ }
+ if (!strupper_m(acct_name)) {
+ SAFE_FREE(domain_name);
+ SAFE_FREE(acct_name);
+ return -1;
+ }
+ cli_credentials_set_username(c->creds, acct_name, CRED_SPECIFIED);
+
+ /*
+ * opt_workgroup will be used by connection functions further,
+ * hence it should be set to remote domain name instead of ours
+ */
+ if (c->opt_workgroup) {
+ c->opt_workgroup = smb_xstrdup(domain_name);
+ };
+
+ /* find the domain controller */
+ if (!net_find_pdc(&server_ss, pdc_name, domain_name)) {
+ DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name));
+ return -1;
+ }
+
+ /* connect to ipc$ as username/password */
+ nt_status = connect_to_ipc(c, &cli, &server_ss, pdc_name);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) {
+
+ /* Is it trusting domain account for sure ? */
+ DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n",
+ nt_errstr(nt_status)));
+ return -1;
+ }
+
+ /* store who we connected to */
+
+ saf_store( domain_name, pdc_name );
+
+ /*
+ * Connect to \\server\ipc$ again (this time anonymously)
+ */
+
+ nt_status = connect_to_ipc_anonymous(c, &cli, &server_ss,
+ (char*)pdc_name);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n",
+ domain_name, nt_errstr(nt_status)));
+ return -1;
+ }
+
+ if (!(mem_ctx = talloc_init("establishing trust relationship to "
+ "domain %s", domain_name))) {
+ DEBUG(0, ("talloc_init() failed\n"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ /* Make sure we're talking to a proper server */
+
+ nt_status = rpc_trustdom_get_pdc(c, cli, mem_ctx, domain_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Call LsaOpenPolicy and LsaQueryInfo
+ */
+
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ b = pipe_hnd->binding_handle;
+
+ nt_status = dcerpc_lsa_open_policy_fallback(b,
+ mem_ctx,
+ pipe_hnd->srv_name_slash,
+ true,
+ KEY_QUERY_VALUE,
+ &out_version,
+ &out_revision_info,
+ &connect_hnd,
+ &result);
+ if (any_nt_status_not_ok(nt_status, result, &nt_status)) {
+ DBG_ERR("Couldn't open policy handle: %s\n",
+ nt_errstr(nt_status));
+ cli_shutdown(cli);
+ talloc_free(mem_ctx);
+ return -1;
+ }
+
+ /* Querying info level 5 */
+
+ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info,
+ &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+ if (NT_STATUS_IS_ERR(result)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ domain_sid = info->account_domain.sid;
+
+ /* There should be actually query info level 3 (following nt serv behaviour),
+ but I still don't know if it's _really_ necessary */
+
+ /*
+ * Store the password in secrets db
+ */
+
+ pwd = cli_credentials_get_password(c->creds);
+
+ if (!pdb_set_trusteddom_pw(domain_name, pwd, domain_sid)) {
+ DEBUG(0, ("Storing password for trusted domain failed.\n"));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Close the pipes and clean up
+ */
+
+ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+
+ d_printf(_("Trust to domain %s established\n"), domain_name);
+ return 0;
+}
+
+/**
+ * Revoke trust relationship to the remote domain.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ **/
+
+static int rpc_trustdom_revoke(struct net_context *c, int argc,
+ const char **argv)
+{
+ char* domain_name;
+ int rc = -1;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc trustdom revoke <domain_name>\n"
+ " Revoke trust relationship\n"
+ " domain_name\tName of domain to revoke trust\n"));
+ return -1;
+ }
+
+ /* generate upper cased domain name */
+ domain_name = smb_xstrdup(argv[0]);
+ if (!strupper_m(domain_name)) {
+ SAFE_FREE(domain_name);
+ return -1;
+ }
+
+ /* delete password of the trust */
+ if (!pdb_del_trusteddom_pw(domain_name)) {
+ DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n",
+ domain_name));
+ goto done;
+ };
+
+ rc = 0;
+done:
+ SAFE_FREE(domain_name);
+ return rc;
+}
+
+static NTSTATUS rpc_query_domain_sid(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dom_sid_buf sid_str;
+ d_printf("%s\n", dom_sid_str_buf(domain_sid, &sid_str));
+ return NT_STATUS_OK;
+}
+
+static void print_trusted_domain(struct dom_sid *dom_sid, const char *trusted_dom_name)
+{
+ struct dom_sid_buf sid_str;
+
+ d_printf("%-20s%s\n",
+ trusted_dom_name,
+ dom_sid_str_buf(dom_sid, &sid_str));
+}
+
+static NTSTATUS vampire_trusted_domain(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *pol,
+ struct dom_sid dom_sid,
+ const char *trusted_dom_name)
+{
+ NTSTATUS nt_status, result;
+ union lsa_TrustedDomainInfo *info = NULL;
+ char *cleartextpwd = NULL;
+ DATA_BLOB session_key;
+ DATA_BLOB data = data_blob_null;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ nt_status = dcerpc_lsa_QueryTrustedDomainInfoBySid(b, mem_ctx,
+ pol,
+ &dom_sid,
+ LSA_TRUSTED_DOMAIN_INFO_PASSWORD,
+ &info,
+ &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0,("Could not query trusted domain info. Error was %s\n",
+ nt_errstr(nt_status)));
+ goto done;
+ }
+ if (NT_STATUS_IS_ERR(result)) {
+ nt_status = result;
+ DEBUG(0,("Could not query trusted domain info. Error was %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+
+ data = data_blob(info->password.password->data,
+ info->password.password->length);
+
+ nt_status = cli_get_session_key(mem_ctx, pipe_hnd, &session_key);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not retrieve session key: %s\n", nt_errstr(nt_status)));
+ goto done;
+ }
+
+ cleartextpwd = sess_decrypt_string(mem_ctx, &data, &session_key);
+ data_blob_free(&session_key);
+
+ if (cleartextpwd == NULL) {
+ DEBUG(0,("retrieved NULL password\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!pdb_set_trusteddom_pw(trusted_dom_name, cleartextpwd, &dom_sid)) {
+ DEBUG(0, ("Storing password for trusted domain failed.\n"));
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+#ifdef DEBUG_PASSWORD
+ {
+ struct dom_sid_buf buf;
+ DEBUG(100,("successfully vampired trusted domain [%s], "
+ "sid: [%s], password: [%s]\n",
+ trusted_dom_name,
+ dom_sid_str_buf(&dom_sid, &buf),
+ cleartextpwd));
+ }
+#endif
+
+done:
+ SAFE_FREE(cleartextpwd);
+ data_blob_free(&data);
+
+ return nt_status;
+}
+
+static int rpc_trustdom_vampire(struct net_context *c, int argc,
+ const char **argv)
+{
+ /* common variables */
+ TALLOC_CTX* mem_ctx;
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS nt_status, result;
+ const char *domain_name = NULL;
+ struct policy_handle connect_hnd;
+ union lsa_PolicyInformation *info = NULL;
+
+ /* trusted domains listing variables */
+ unsigned int enum_ctx = 0;
+ struct lsa_DomainList dom_list;
+ fstring pdc_name;
+ struct dcerpc_binding_handle *b;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc trustdom vampire\n"
+ " %s\n",
+ _("Usage:"),
+ _("Vampire trust relationship from remote server"));
+ return 0;
+ }
+
+ /*
+ * Listing trusted domains (stored in secrets.tdb, if local)
+ */
+
+ mem_ctx = talloc_init("trust relationships vampire");
+
+ /*
+ * set domain and pdc name to local samba server (default)
+ * or to remote one given in command line
+ */
+
+ if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) {
+ domain_name = c->opt_workgroup;
+ c->opt_target_workgroup = c->opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, lp_netbios_name());
+ domain_name = talloc_strdup(mem_ctx, lp_workgroup());
+ c->opt_target_workgroup = domain_name;
+ };
+
+ /* open \PIPE\lsarpc and open policy handle */
+ nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't connect to domain controller: %s\n",
+ nt_errstr(nt_status)));
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n",
+ nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ b = pipe_hnd->binding_handle;
+
+ nt_status = dcerpc_lsa_open_policy_fallback(b,
+ mem_ctx,
+ pipe_hnd->srv_name_slash,
+ false,
+ KEY_QUERY_VALUE,
+ &out_version,
+ &out_revision_info,
+ &connect_hnd,
+ &result);
+ if (any_nt_status_not_ok(nt_status, result, &nt_status)) {
+ DBG_ERR("Couldn't open policy handle: %s\n",
+ nt_errstr(nt_status));
+ cli_shutdown(cli);
+ talloc_free(mem_ctx);
+ return -1;
+ }
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info,
+ &result);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+ if (NT_STATUS_IS_ERR(result)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /*
+ * Keep calling LsaEnumTrustdom over opened pipe until
+ * the end of enumeration is reached
+ */
+
+ d_printf(_("Vampire trusted domains:\n\n"));
+
+ do {
+ uint32_t i;
+
+ nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx,
+ &connect_hnd,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t)-1,
+ &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+ if (NT_STATUS_IS_ERR(result)) {
+ nt_status = result;
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+
+ for (i = 0; i < dom_list.count; i++) {
+
+ print_trusted_domain(dom_list.domains[i].sid,
+ dom_list.domains[i].name.string);
+
+ nt_status = vampire_trusted_domain(pipe_hnd, mem_ctx, &connect_hnd,
+ *dom_list.domains[i].sid,
+ dom_list.domains[i].name.string);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+ };
+
+ /*
+ * in case of no trusted domains say something rather
+ * than just display blank line
+ */
+ if (!dom_list.count) d_printf(_("none\n"));
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close this connection before doing next one */
+ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ /* close lsarpc pipe and connection to IPC$ */
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+static int rpc_trustdom_list(struct net_context *c, int argc, const char **argv)
+{
+ /* common variables */
+ TALLOC_CTX* mem_ctx;
+ struct cli_state *cli = NULL, *remote_cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS nt_status, result;
+ const char *domain_name = NULL;
+ struct dom_sid *queried_dom_sid;
+ int ascii_dom_name_len;
+ struct policy_handle connect_hnd;
+ union lsa_PolicyInformation *info = NULL;
+ struct dcerpc_binding_handle *b = NULL;
+
+ /* trusted domains listing variables */
+ unsigned int num_domains, enum_ctx = 0;
+ uint32_t i;
+ struct lsa_DomainList dom_list;
+ fstring pdc_name;
+ bool found_domain;
+
+ /* trusting domains listing variables */
+ struct policy_handle domain_hnd;
+ struct samr_SamArray *trusts = NULL;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc trustdom list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List incoming and outgoing trust relationships"));
+ return 0;
+ }
+
+ /*
+ * Listing trusted domains (stored in secrets.tdb, if local)
+ */
+
+ mem_ctx = talloc_init("trust relationships listing");
+
+ /*
+ * set domain and pdc name to local samba server (default)
+ * or to remote one given in command line
+ */
+
+ if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) {
+ domain_name = c->opt_workgroup;
+ c->opt_target_workgroup = c->opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, lp_netbios_name());
+ domain_name = talloc_strdup(mem_ctx, lp_workgroup());
+ c->opt_target_workgroup = domain_name;
+ };
+
+ /* open \PIPE\lsarpc and open policy handle */
+ nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't connect to domain controller: %s\n",
+ nt_errstr(nt_status)));
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n",
+ nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ b = pipe_hnd->binding_handle;
+
+ nt_status = dcerpc_lsa_open_policy_fallback(b,
+ mem_ctx,
+ pipe_hnd->srv_name_slash,
+ true,
+ KEY_QUERY_VALUE,
+ &out_version,
+ &out_revision_info,
+ &connect_hnd,
+ &result);
+ if (any_nt_status_not_ok(nt_status, result, &nt_status)) {
+ DBG_ERR("Couldn't open policy handle: %s\n",
+ nt_errstr(nt_status));
+ cli_shutdown(cli);
+ talloc_free(mem_ctx);
+ return -1;
+ }
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info,
+ &result);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+ if (NT_STATUS_IS_ERR(result)) {
+ DEBUG(0, ("LSA Query Info failed. Returned error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ queried_dom_sid = info->account_domain.sid;
+
+ /*
+ * Keep calling LsaEnumTrustdom over opened pipe until
+ * the end of enumeration is reached
+ */
+
+ d_printf(_("Trusted domains list:\n\n"));
+
+ found_domain = false;
+
+ do {
+ nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx,
+ &connect_hnd,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t)-1,
+ &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+ if (NT_STATUS_IS_ERR(result)) {
+ DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+
+ for (i = 0; i < dom_list.count; i++) {
+ print_trusted_domain(dom_list.domains[i].sid,
+ dom_list.domains[i].name.string);
+ found_domain = true;
+ };
+
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /*
+ * in case of no trusted domains say something rather
+ * than just display blank line
+ */
+ if (!found_domain) {
+ d_printf(_("none\n"));
+ }
+
+ /* close this connection before doing next one */
+ nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ TALLOC_FREE(pipe_hnd);
+
+ /*
+ * Listing trusting domains (stored in passdb backend, if local)
+ */
+
+ d_printf(_("\nTrusting domains list:\n\n"));
+
+ /*
+ * Open \PIPE\samr and get needed policy handles
+ */
+ nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise samr pipe. Error was %s\n", nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ b = pipe_hnd->binding_handle;
+
+ /* SamrConnect2 */
+ nt_status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ SAMR_ACCESS_LOOKUP_DOMAIN,
+ &connect_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+ if (!NT_STATUS_IS_OK(result)) {
+ nt_status = result;
+ DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ /* SamrOpenDomain - we have to open domain policy handle in order to be
+ able to enumerate accounts*/
+ nt_status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_hnd,
+ SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS,
+ queried_dom_sid,
+ &domain_hnd,
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't open domain object. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+ if (!NT_STATUS_IS_OK(result)) {
+ nt_status = result;
+ DEBUG(0, ("Couldn't open domain object. Error was %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ /*
+ * perform actual enumeration
+ */
+
+ found_domain = false;
+
+ enum_ctx = 0; /* reset enumeration context from last enumeration */
+ do {
+
+ nt_status = dcerpc_samr_EnumDomainUsers(b, mem_ctx,
+ &domain_hnd,
+ &enum_ctx,
+ ACB_DOMTRUST,
+ &trusts,
+ 0xffff,
+ &num_domains,
+ &result);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+ if (NT_STATUS_IS_ERR(result)) {
+ nt_status = result;
+ DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n",
+ nt_errstr(result)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+
+ char *str = discard_const_p(char, trusts->entries[i].name.string);
+
+ found_domain = true;
+
+ /*
+ * get each single domain's sid (do we _really_ need this ?):
+ * 1) connect to domain's pdc
+ * 2) query the pdc for domain's sid
+ */
+
+ /* get rid of '$' tail */
+ ascii_dom_name_len = strlen(str);
+ if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN)
+ str[ascii_dom_name_len - 1] = '\0';
+
+ /* set opt_* variables to remote domain */
+ if (!strupper_m(str)) {
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+ c->opt_workgroup = talloc_strdup(mem_ctx, str);
+ c->opt_target_workgroup = c->opt_workgroup;
+
+ d_printf("%-20s", str);
+
+ /* connect to remote domain controller */
+ nt_status = net_make_ipc_connection(c,
+ NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS,
+ &remote_cli);
+ if (NT_STATUS_IS_OK(nt_status)) {
+ /* query for domain's sid */
+ if (run_rpc_command(
+ c, remote_cli,
+ &ndr_table_lsarpc, 0,
+ rpc_query_domain_sid, argc,
+ argv))
+ d_printf(_("strange - couldn't get domain's sid\n"));
+
+ cli_shutdown(remote_cli);
+
+ } else {
+ d_fprintf(stderr, _("domain controller is not "
+ "responding: %s\n"),
+ nt_errstr(nt_status));
+ d_printf(_("couldn't get domain's sid\n"));
+ }
+ }
+
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ if (!found_domain) {
+ d_printf("none\n");
+ }
+
+ /* close opened samr and domain policy handles */
+ nt_status = dcerpc_samr_Close(b, mem_ctx, &domain_hnd, &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name));
+ };
+
+ nt_status = dcerpc_samr_Close(b, mem_ctx, &connect_hnd, &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name));
+ };
+
+ /* close samr pipe and connection to IPC$ */
+ cli_shutdown(cli);
+
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+/**
+ * Entrypoint for 'net rpc trustdom' code.
+ *
+ * @param argc Standard argc.
+ * @param argv Standard argv without initial components.
+ *
+ * @return Integer status (0 means success).
+ */
+
+static int rpc_trustdom(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ rpc_trustdom_add,
+ NET_TRANSPORT_RPC,
+ N_("Add trusting domain's account"),
+ N_("net rpc trustdom add\n"
+ " Add trusting domain's account")
+ },
+ {
+ "del",
+ rpc_trustdom_del,
+ NET_TRANSPORT_RPC,
+ N_("Remove trusting domain's account"),
+ N_("net rpc trustdom del\n"
+ " Remove trusting domain's account")
+ },
+ {
+ "establish",
+ rpc_trustdom_establish,
+ NET_TRANSPORT_RPC,
+ N_("Establish outgoing trust relationship"),
+ N_("net rpc trustdom establish\n"
+ " Establish outgoing trust relationship")
+ },
+ {
+ "revoke",
+ rpc_trustdom_revoke,
+ NET_TRANSPORT_RPC,
+ N_("Revoke outgoing trust relationship"),
+ N_("net rpc trustdom revoke\n"
+ " Revoke outgoing trust relationship")
+ },
+ {
+ "list",
+ rpc_trustdom_list,
+ NET_TRANSPORT_RPC,
+ N_("List in- and outgoing domain trusts"),
+ N_("net rpc trustdom list\n"
+ " List in- and outgoing domain trusts")
+ },
+ {
+ "vampire",
+ rpc_trustdom_vampire,
+ NET_TRANSPORT_RPC,
+ N_("Vampire trusts from remote server"),
+ N_("net rpc trustdom vampire\n"
+ " Vampire trusts from remote server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc trustdom", func);
+}
+
+/**
+ * Check if a server will take rpc commands
+ * @param flags Type of server to connect to (PDC, DMB, localhost)
+ * if the host is not explicitly specified
+ * @return bool (true means rpc supported)
+ */
+bool net_rpc_check(struct net_context *c, unsigned flags)
+{
+ struct cli_state *cli;
+ bool ret = false;
+ struct sockaddr_storage server_ss;
+ char *server_name = NULL;
+ NTSTATUS status;
+
+ /* flags (i.e. server type) may depend on command */
+ if (!net_find_server(c, NULL, flags, &server_ss, &server_name))
+ return false;
+
+ status = cli_connect_nb(server_name, &server_ss, 0, 0x20,
+ lp_netbios_name(), SMB_SIGNING_IPC_DEFAULT,
+ 0, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ DBG_ERR("NetBIOS support disabled, unable to connect\n");
+ }
+ return false;
+ }
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ lp_client_min_protocol(),
+ lp_client_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+ if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1)
+ goto done;
+
+ ret = true;
+ done:
+ cli_shutdown(cli);
+ return ret;
+}
+
+/* synchronise sam database via samsync rpc calls */
+static int rpc_vampire(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "keytab",
+ rpc_vampire_keytab,
+ NET_TRANSPORT_RPC,
+ N_("Dump remote SAM database to Kerberos Keytab"),
+ N_("net rpc vampire keytab\n"
+ " Dump remote SAM database to Kerberos keytab "
+ "file")
+ },
+ {
+ "passdb",
+ rpc_vampire_passdb,
+ NET_TRANSPORT_RPC,
+ N_("Dump remote SAM database to passdb"),
+ N_("net rpc vampire passdb\n"
+ " Dump remote SAM database to passdb")
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc vampire\n"
+ " %s\n",
+ _("Usage:"),
+ _("Vampire remote SAM database"));
+ return 0;
+ }
+
+ return rpc_vampire_passdb(c, argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc vampire", func);
+}
+
+/**
+ * Migrate everything from a print server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ *
+ * The order is important !
+ * To successfully add drivers the print queues have to exist !
+ * Applying ACLs should be the last step, because you're easily locked out.
+ *
+ **/
+static int rpc_printer_migrate_all(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate all\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate everything from a print server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_printers_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_drivers_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_forms_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_settings_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_security_internals, argc,
+ argv);
+
+}
+
+/**
+ * Migrate print drivers from a print server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_migrate_drivers(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate drivers\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate print-drivers from a print-server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_drivers_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate print-forms from a print-server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_migrate_forms(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate forms\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate print-forms from a print-server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_forms_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printers from a print-server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_migrate_printers(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate printers\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate printers from a print-server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_printers_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printer-ACLs from a print-server
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_migrate_security(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate security\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate printer-ACLs from a print-server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_security_internals,
+ argc, argv);
+}
+
+/**
+ * Migrate printer-settings from a print-server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_migrate_settings(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer migrate settings\n"
+ " %s\n",
+ _("Usage:"),
+ _("Migrate printer-settings from a "
+ "print-server"));
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf(_("no server to migrate\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_migrate_settings_internals,
+ argc, argv);
+}
+
+/**
+ * 'net rpc printer' entrypoint.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int rpc_printer_migrate(struct net_context *c, int argc, const char **argv)
+{
+
+ /* ouch: when addriver and setdriver are called from within
+ rpc_printer_migrate_drivers_internals, the printer-queue already
+ *has* to exist */
+
+ struct functable func[] = {
+ {
+ "all",
+ rpc_printer_migrate_all,
+ NET_TRANSPORT_RPC,
+ N_("Migrate all from remote to local print server"),
+ N_("net rpc printer migrate all\n"
+ " Migrate all from remote to local print server")
+ },
+ {
+ "drivers",
+ rpc_printer_migrate_drivers,
+ NET_TRANSPORT_RPC,
+ N_("Migrate drivers to local server"),
+ N_("net rpc printer migrate drivers\n"
+ " Migrate drivers to local server")
+ },
+ {
+ "forms",
+ rpc_printer_migrate_forms,
+ NET_TRANSPORT_RPC,
+ N_("Migrate forms to local server"),
+ N_("net rpc printer migrate forms\n"
+ " Migrate forms to local server")
+ },
+ {
+ "printers",
+ rpc_printer_migrate_printers,
+ NET_TRANSPORT_RPC,
+ N_("Migrate printers to local server"),
+ N_("net rpc printer migrate printers\n"
+ " Migrate printers to local server")
+ },
+ {
+ "security",
+ rpc_printer_migrate_security,
+ NET_TRANSPORT_RPC,
+ N_("Migrate printer ACLs to local server"),
+ N_("net rpc printer migrate security\n"
+ " Migrate printer ACLs to local server")
+ },
+ {
+ "settings",
+ rpc_printer_migrate_settings,
+ NET_TRANSPORT_RPC,
+ N_("Migrate printer settings to local server"),
+ N_("net rpc printer migrate settings\n"
+ " Migrate printer settings to local server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc printer migrate",func);
+}
+
+
+/**
+ * List printers on a remote RPC server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_list(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List printers on a remote RPC server"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_list_internals,
+ argc, argv);
+}
+
+/**
+ * List printer-drivers on a remote RPC server.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_driver_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer driver\n"
+ " %s\n",
+ _("Usage:"),
+ _("List printer-drivers on a remote RPC server"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_driver_list_internals,
+ argc, argv);
+}
+
+/**
+ * Publish printer in ADS via MSRPC.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_publish_publish(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer publish publish\n"
+ " %s\n",
+ _("Usage:"),
+ _("Publish printer in ADS via MSRPC"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_publish_publish_internals,
+ argc, argv);
+}
+
+/**
+ * Update printer in ADS via MSRPC.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_publish_update(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer publish update\n"
+ " %s\n",
+ _("Usage:"),
+ _("Update printer in ADS via MSRPC"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_publish_update_internals,
+ argc, argv);
+}
+
+/**
+ * UnPublish printer in ADS via MSRPC.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_publish_unpublish(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer publish unpublish\n"
+ " %s\n",
+ _("Usage:\n"),
+ _("UnPublish printer in ADS via MSRPC"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_publish_unpublish_internals,
+ argc, argv);
+}
+
+/**
+ * List published printers via MSRPC.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_publish_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc printer publish list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List published printers via MSRPC"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_publish_list_internals,
+ argc, argv);
+}
+
+
+/**
+ * Publish printer in ADS.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ *
+ * @return A shell status integer (0 for success).
+ **/
+static int rpc_printer_publish(struct net_context *c, int argc,
+ const char **argv)
+{
+
+ struct functable func[] = {
+ {
+ "publish",
+ rpc_printer_publish_publish,
+ NET_TRANSPORT_RPC,
+ N_("Publish printer in AD"),
+ N_("net rpc printer publish publish\n"
+ " Publish printer in AD")
+ },
+ {
+ "update",
+ rpc_printer_publish_update,
+ NET_TRANSPORT_RPC,
+ N_("Update printer in AD"),
+ N_("net rpc printer publish update\n"
+ " Update printer in AD")
+ },
+ {
+ "unpublish",
+ rpc_printer_publish_unpublish,
+ NET_TRANSPORT_RPC,
+ N_("Unpublish printer"),
+ N_("net rpc printer publish unpublish\n"
+ " Unpublish printer")
+ },
+ {
+ "list",
+ rpc_printer_publish_list,
+ NET_TRANSPORT_RPC,
+ N_("List published printers"),
+ N_("net rpc printer publish list\n"
+ " List published printers")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rpc printer publish\n"
+ " List published printers\n"
+ " Alias of net rpc printer publish "
+ "list\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_publish_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc printer publish",func);
+
+}
+
+
+/**
+ * Display rpc printer help page.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+int rpc_printer_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rpc printer LIST [printer] [misc. options] [targets]\n"
+ "\tlists all printers on print-server\n\n"));
+ d_printf(_("net rpc printer DRIVER [printer] [misc. options] [targets]\n"
+ "\tlists all printer-drivers on print-server\n\n"));
+ d_printf(_("net rpc printer PUBLISH action [printer] [misc. options] [targets]\n"
+ "\tpublishes printer settings in Active Directory\n"
+ "\taction can be one of PUBLISH, UPDATE, UNPUBLISH or LIST\n\n"));
+ d_printf(_("net rpc printer MIGRATE PRINTERS [printer] [misc. options] [targets]"
+ "\n\tmigrates printers from remote to local server\n\n"));
+ d_printf(_("net rpc printer MIGRATE SETTINGS [printer] [misc. options] [targets]"
+ "\n\tmigrates printer-settings from remote to local server\n\n"));
+ d_printf(_("net rpc printer MIGRATE DRIVERS [printer] [misc. options] [targets]"
+ "\n\tmigrates printer-drivers from remote to local server\n\n"));
+ d_printf(_("net rpc printer MIGRATE FORMS [printer] [misc. options] [targets]"
+ "\n\tmigrates printer-forms from remote to local server\n\n"));
+ d_printf(_("net rpc printer MIGRATE SECURITY [printer] [misc. options] [targets]"
+ "\n\tmigrates printer-ACLs from remote to local server\n\n"));
+ d_printf(_("net rpc printer MIGRATE ALL [printer] [misc. options] [targets]"
+ "\n\tmigrates drivers, forms, queues, settings and acls from\n"
+ "\tremote to local print-server\n\n"));
+ net_common_methods_usage(c, argc, argv);
+ net_common_flags_usage(c, argc, argv);
+ d_printf(_(
+ "\t-v or --verbose\t\t\tgive verbose output\n"
+ "\t --destination\t\tmigration target server (default: localhost)\n"));
+
+ return -1;
+}
+
+/**
+ * 'net rpc printer' entrypoint.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+int net_rpc_printer(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ rpc_printer_list,
+ NET_TRANSPORT_RPC,
+ N_("List all printers on print server"),
+ N_("net rpc printer list\n"
+ " List all printers on print server")
+ },
+ {
+ "migrate",
+ rpc_printer_migrate,
+ NET_TRANSPORT_RPC,
+ N_("Migrate printer to local server"),
+ N_("net rpc printer migrate\n"
+ " Migrate printer to local server")
+ },
+ {
+ "driver",
+ rpc_printer_driver_list,
+ NET_TRANSPORT_RPC,
+ N_("List printer drivers"),
+ N_("net rpc printer driver\n"
+ " List printer drivers")
+ },
+ {
+ "publish",
+ rpc_printer_publish,
+ NET_TRANSPORT_RPC,
+ N_("Publish printer in AD"),
+ N_("net rpc printer publish\n"
+ " Publish printer in AD")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf(_("Usage:\n"));
+ d_printf(_("net rpc printer\n"
+ " List printers\n"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+ return run_rpc_command(c, NULL, &ndr_table_spoolss, 0,
+ rpc_printer_list_internals,
+ argc, argv);
+ }
+
+ return net_run_function(c, argc, argv, "net rpc printer", func);
+}
+
+/**
+ * 'net rpc' entrypoint.
+ *
+ * @param c A net_context structure.
+ * @param argc Standard main() style argc.
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped.
+ **/
+
+int net_rpc(struct net_context *c, int argc, const char **argv)
+{
+ NET_API_STATUS status;
+
+ struct functable func[] = {
+ {
+ "audit",
+ net_rpc_audit,
+ NET_TRANSPORT_RPC,
+ N_("Modify global audit settings"),
+ N_("net rpc audit\n"
+ " Modify global audit settings")
+ },
+ {
+ "info",
+ net_rpc_info,
+ NET_TRANSPORT_RPC,
+ N_("Show basic info about a domain"),
+ N_("net rpc info\n"
+ " Show basic info about a domain")
+ },
+ {
+ "join",
+ net_rpc_join,
+ NET_TRANSPORT_RPC,
+ N_("Join a domain"),
+ N_("net rpc join\n"
+ " Join a domain")
+ },
+ {
+ "oldjoin",
+ net_rpc_oldjoin,
+ NET_TRANSPORT_RPC,
+ N_("Join a domain created in server manager"),
+ N_("net rpc oldjoin\n"
+ " Join a domain created in server manager")
+ },
+ {
+ "testjoin",
+ net_rpc_testjoin,
+ NET_TRANSPORT_RPC,
+ N_("Test that a join is valid"),
+ N_("net rpc testjoin\n"
+ " Test that a join is valid")
+ },
+ {
+ "user",
+ net_rpc_user,
+ NET_TRANSPORT_RPC,
+ N_("List/modify users"),
+ N_("net rpc user\n"
+ " List/modify users")
+ },
+ {
+ "password",
+ rpc_user_password,
+ NET_TRANSPORT_RPC,
+ N_("Change a user password"),
+ N_("net rpc password\n"
+ " Change a user password\n"
+ " Alias for net rpc user password")
+ },
+ {
+ "group",
+ net_rpc_group,
+ NET_TRANSPORT_RPC,
+ N_("List/modify groups"),
+ N_("net rpc group\n"
+ " List/modify groups")
+ },
+ {
+ "share",
+ net_rpc_share,
+ NET_TRANSPORT_RPC,
+ N_("List/modify shares"),
+ N_("net rpc share\n"
+ " List/modify shares")
+ },
+ {
+ "file",
+ net_rpc_file,
+ NET_TRANSPORT_RPC,
+ N_("List open files"),
+ N_("net rpc file\n"
+ " List open files")
+ },
+ {
+ "printer",
+ net_rpc_printer,
+ NET_TRANSPORT_RPC,
+ N_("List/modify printers"),
+ N_("net rpc printer\n"
+ " List/modify printers")
+ },
+ {
+ "changetrustpw",
+ net_rpc_changetrustpw,
+ NET_TRANSPORT_RPC,
+ N_("Change trust account password"),
+ N_("net rpc changetrustpw\n"
+ " Change trust account password")
+ },
+ {
+ "trustdom",
+ rpc_trustdom,
+ NET_TRANSPORT_RPC,
+ N_("Modify domain trusts"),
+ N_("net rpc trustdom\n"
+ " Modify domain trusts")
+ },
+ {
+ "abortshutdown",
+ rpc_shutdown_abort,
+ NET_TRANSPORT_RPC,
+ N_("Abort a remote shutdown"),
+ N_("net rpc abortshutdown\n"
+ " Abort a remote shutdown")
+ },
+ {
+ "shutdown",
+ rpc_shutdown,
+ NET_TRANSPORT_RPC,
+ N_("Shutdown a remote server"),
+ N_("net rpc shutdown\n"
+ " Shutdown a remote server")
+ },
+ {
+ "vampire",
+ rpc_vampire,
+ NET_TRANSPORT_RPC,
+ N_("Sync a remote NT PDC's data into local passdb"),
+ N_("net rpc vampire\n"
+ " Sync a remote NT PDC's data into local passdb")
+ },
+ {
+ "getsid",
+ net_rpc_getsid,
+ NET_TRANSPORT_RPC,
+ N_("Fetch the domain sid into local secrets.tdb"),
+ N_("net rpc getsid\n"
+ " Fetch the domain sid into local secrets.tdb")
+ },
+ {
+ "rights",
+ net_rpc_rights,
+ NET_TRANSPORT_RPC,
+ N_("Manage privileges assigned to SID"),
+ N_("net rpc rights\n"
+ " Manage privileges assigned to SID")
+ },
+ {
+ "service",
+ net_rpc_service,
+ NET_TRANSPORT_RPC,
+ N_("Start/stop/query remote services"),
+ N_("net rpc service\n"
+ " Start/stop/query remote services")
+ },
+ {
+ "registry",
+ net_rpc_registry,
+ NET_TRANSPORT_RPC,
+ N_("Manage registry hives"),
+ N_("net rpc registry\n"
+ " Manage registry hives")
+ },
+ {
+ "shell",
+ net_rpc_shell,
+ NET_TRANSPORT_RPC,
+ N_("Open interactive shell on remote server"),
+ N_("net rpc shell\n"
+ " Open interactive shell on remote server")
+ },
+ {
+ "trust",
+ net_rpc_trust,
+ NET_TRANSPORT_RPC,
+ N_("Manage trusts"),
+ N_("net rpc trust\n"
+ " Manage trusts")
+ },
+ {
+ "conf",
+ net_rpc_conf,
+ NET_TRANSPORT_RPC,
+ N_("Configure a remote samba server"),
+ N_("net rpc conf\n"
+ " Configure a remote samba server")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (status != 0) {
+ return -1;
+ }
+
+ return net_run_function(c, argc, argv, "net rpc", func);
+}
diff --git a/source3/utils/net_rpc_audit.c b/source3/utils/net_rpc_audit.c
new file mode 100644
index 0000000..336f6a8
--- /dev/null
+++ b/source3/utils/net_rpc_audit.c
@@ -0,0 +1,540 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2006,2008 Guenther Deschner
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+
+/********************************************************************
+********************************************************************/
+
+static int net_help_audit(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rpc audit list View configured Auditing policies\n"));
+ d_printf(_("net rpc audit enable Enable Auditing\n"));
+ d_printf(_("net rpc audit disable Disable Auditing\n"));
+ d_printf(_("net rpc audit get <category> View configured Auditing policy setting\n"));
+ d_printf(_("net rpc audit set <category> <policy> Set Auditing policies\n\n"));
+ d_printf(_("\tcategory can be one of: SYSTEM, LOGON, OBJECT, PRIVILEGE, PROCESS, POLICY, SAM, DIRECTORY or ACCOUNT\n"));
+ d_printf(_("\tpolicy can be one of: SUCCESS, FAILURE, ALL or NONE\n\n"));
+
+ return -1;
+}
+
+/********************************************************************
+********************************************************************/
+
+static void print_auditing_category(const char *policy, const char *value)
+{
+ if (policy == NULL) {
+ policy = N_("Unknown");
+ }
+ if (value == NULL) {
+ value = N_("Invalid");
+ }
+
+ d_printf(_("\t%-30s%s\n"), policy, value);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_get_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ union lsa_PolicyInformation *info = NULL;
+ int i;
+ uint32_t audit_category;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc < 1 || argc > 2) {
+ d_printf(_("insufficient arguments\n"));
+ net_help_audit(c, argc, argv);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!get_audit_category_from_param(argv[0], &audit_category)) {
+ d_printf(_("invalid auditing category: %s\n"), argv[0]);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ for (i=0; i < info->audit_events.count; i++) {
+
+ const char *val = NULL, *policy = NULL;
+
+ if (i != audit_category) {
+ continue;
+ }
+
+ val = audit_policy_str(mem_ctx, info->audit_events.settings[i]);
+ policy = audit_description_str(i);
+ print_auditing_category(policy, val);
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("failed to get auditing policy: %s\n"),
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_set_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ union lsa_PolicyInformation *info = NULL;
+ uint32_t audit_policy, audit_category;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc < 2 || argc > 3) {
+ d_printf(_("insufficient arguments\n"));
+ net_help_audit(c, argc, argv);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!get_audit_category_from_param(argv[0], &audit_category)) {
+ d_printf(_("invalid auditing category: %s\n"), argv[0]);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ audit_policy = LSA_AUDIT_POLICY_CLEAR;
+
+ if (strequal(argv[1], "Success")) {
+ audit_policy |= LSA_AUDIT_POLICY_SUCCESS;
+ } else if (strequal(argv[1], "Failure")) {
+ audit_policy |= LSA_AUDIT_POLICY_FAILURE;
+ } else if (strequal(argv[1], "All")) {
+ audit_policy |= LSA_AUDIT_POLICY_ALL;
+ } else if (strequal(argv[1], "None")) {
+ audit_policy = LSA_AUDIT_POLICY_CLEAR;
+ } else {
+ d_printf(_("invalid auditing policy: %s\n"), argv[1]);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ info->audit_events.settings[audit_category] = audit_policy;
+
+ status = dcerpc_lsa_SetInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = result;
+
+ {
+ const char *val = audit_policy_str(mem_ctx, info->audit_events.settings[audit_category]);
+ const char *policy = audit_description_str(audit_category);
+ print_auditing_category(policy, val);
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("failed to set audit policy: %s\n"),
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_enable_internal_ext(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ bool enable)
+{
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ union lsa_PolicyInformation *info = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ info->audit_events.auditing_mode = enable;
+
+ status = dcerpc_lsa_SetInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("%s: %s\n"),
+ enable ? _("failed to enable audit policy"):
+ _("failed to disable audit policy"),
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_disable_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_audit_enable_internal_ext(pipe_hnd, mem_ctx, argc, argv,
+ false);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_enable_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_audit_enable_internal_ext(pipe_hnd, mem_ctx, argc, argv,
+ true);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_list_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ union lsa_PolicyInformation *info = NULL;
+ int i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ printf(_("Auditing:\t\t"));
+ switch (info->audit_events.auditing_mode) {
+ case true:
+ printf(_("Enabled"));
+ break;
+ case false:
+ printf(_("Disabled"));
+ break;
+ default:
+ printf(_("unknown (%d)"),
+ info->audit_events.auditing_mode);
+ break;
+ }
+ printf("\n");
+
+ printf(_("Auditing categories:\t%d\n"), info->audit_events.count);
+ printf(_("Auditing settings:\n"));
+
+ for (i=0; i < info->audit_events.count; i++) {
+ const char *val = audit_policy_str(mem_ctx, info->audit_events.settings[i]);
+ const char *policy = audit_description_str(i);
+ print_auditing_category(policy, val);
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("failed to list auditing policies: %s\n"),
+ nt_errstr(status));
+ }
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_get(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc audit get\n"
+ " %s\n",
+ _("Usage:"),
+ _("View configured audit setting"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_audit_get_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_set(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc audit set\n"
+ " %s\n",
+ _("Usage:"),
+ _("Set audit policies"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_audit_set_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_enable(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc audit enable\n"
+ " %s\n",
+ _("Usage:"),
+ _("Enable auditing"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_audit_enable_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_disable(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc audit disable\n"
+ " %s\n",
+ _("Usage:"),
+ _("Disable auditing"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_audit_disable_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_list(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc audit list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List auditing settings"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_audit_list_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+int net_rpc_audit(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "get",
+ rpc_audit_get,
+ NET_TRANSPORT_RPC,
+ N_("View configured auditing settings"),
+ N_("net rpc audit get\n"
+ " View configured auditing settings")
+ },
+ {
+ "set",
+ rpc_audit_set,
+ NET_TRANSPORT_RPC,
+ N_("Set auditing policies"),
+ N_("net rpc audit set\n"
+ " Set auditing policies")
+ },
+ {
+ "enable",
+ rpc_audit_enable,
+ NET_TRANSPORT_RPC,
+ N_("Enable auditing"),
+ N_("net rpc audit enable\n"
+ " Enable auditing")
+ },
+ {
+ "disable",
+ rpc_audit_disable,
+ NET_TRANSPORT_RPC,
+ N_("Disable auditing"),
+ N_("net rpc audit disable\n"
+ " Disable auditing")
+ },
+ {
+ "list",
+ rpc_audit_list,
+ NET_TRANSPORT_RPC,
+ N_("List configured auditing settings"),
+ N_("net rpc audit list\n"
+ " List configured auditing settings")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc audit", func);
+}
diff --git a/source3/utils/net_rpc_conf.c b/source3/utils/net_rpc_conf.c
new file mode 100644
index 0000000..25a7b43
--- /dev/null
+++ b/source3/utils/net_rpc_conf.c
@@ -0,0 +1,2483 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Local configuration interface
+ * Copyright (C) Vicentiu Ciorbaru 2011
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * This is an interface to Samba's configuration.
+ *
+ * This tool supports local as well as remote interaction via rpc
+ * with the configuration stored in the registry.
+ */
+
+
+#include "includes.h"
+#include "utils/net.h"
+#include "utils/net_conf_util.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "rpc_client/init_samr.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "../libcli/registry/util_reg.h"
+#include "rpc_client/cli_winreg.h"
+#include "lib/smbconf/smbconf.h"
+#include "lib/smbconf/smbconf_init.h"
+#include "lib/smbconf/smbconf_reg.h"
+#include "lib/param/loadparm.h"
+
+
+
+/* internal functions */
+/**********************************************************
+ *
+ * usage functions
+ *
+ **********************************************************/
+const char confpath[100] = "Software\\Samba\\smbconf";
+
+static int rpc_conf_list_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s net rpc conf list\n", _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_listshares_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s net rpc conf listshares\n", _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_delshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc conf delshare <sharename>\n"));
+ return -1;
+}
+
+static int rpc_conf_addshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc conf addshare <sharename> <path> "
+ "[writeable={y|N} [guest_ok={y|N} [<comment>]]]\n"
+ "\t<sharename> the new share name.\n"
+ "\t<path> the path on the filesystem to export.\n"
+ "\twriteable={y|N} set \"writeable to \"yes\" or "
+ "\"no\" (default) on this share.\n"
+ "\tguest_ok={y|N} set \"guest ok\" to \"yes\" or "
+ "\"no\" (default) on this share.\n"
+ "\t<comment> optional comment for the new share.\n"));
+ return -1;
+
+}
+
+static int rpc_conf_import_usage(struct net_context *c, int argc,
+ const char**argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc conf import [--test|-T] <filename> "
+ "[<servicename>]\n"
+ "\t[--test|-T] testmode - do not act, just print "
+ "what would be done\n"
+ "\t<servicename> only import service <servicename>, "
+ "ignore the rest\n"));
+ return -1;
+}
+
+static int rpc_conf_showshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc conf showshare <sharename>\n"));
+ return -1;
+}
+
+static int rpc_conf_drop_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf drop\n", _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_getparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf getparm <sharename> <parameter>\n",
+ _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_setparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc conf setparm <section> <param> <value>\n"));
+ return -1;
+}
+
+static int rpc_conf_delparm_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf delparm <sharename> <parameter>\n",
+ _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_getincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf getincludes <sharename>\n",
+ _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_setincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf setincludes <sharename> [<filename>]*\n",
+ _("Usage:"));
+ return -1;
+}
+
+static int rpc_conf_delincludes_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("%s\nnet rpc conf delincludes <sharename>\n",
+ _("Usage:"));
+ return -1;
+}
+
+/**********************************************************
+ *
+ * helper functions
+ *
+ **********************************************************/
+
+/*
+ * The function deletes a registry value with the name 'value' from the share
+ * with the name 'share_name'. 'parent_hnd' is the handle for the smbconf key.
+ */
+static NTSTATUS rpc_conf_del_value(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *parent_hnd,
+ const char *share_name,
+ const char *value,
+ WERROR *werr)
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR result = WERR_OK;
+ WERROR _werr;
+
+ struct winreg_String keyname, valuename;
+ struct policy_handle child_hnd;
+
+ ZERO_STRUCT(child_hnd);
+ ZERO_STRUCT(keyname);
+ ZERO_STRUCT(valuename);
+
+ keyname.name = share_name;
+ valuename.name = value;
+
+ status = dcerpc_winreg_OpenKey(b, frame, parent_hnd, keyname, 0,
+ REG_KEY_WRITE, &child_hnd, &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to open key '%s': %s\n"),
+ keyname.name, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, _("Failed to open key '%s': %s\n"),
+ keyname.name, win_errstr(result));
+ goto error;
+ }
+
+ status = dcerpc_winreg_DeleteValue(b,
+ frame,
+ &child_hnd,
+ valuename,
+ &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to delete value %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(result))) {
+ if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)){
+ result = WERR_OK;
+ goto error;
+ }
+
+ d_fprintf(stderr, _("Failed to delete value %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+
+error:
+ *werr = result;
+
+ dcerpc_winreg_CloseKey(b, frame, &child_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;;
+
+}
+
+/*
+ * The function sets a share in the registry with the parameters
+ * held in the smbconf_service struct
+ */
+static NTSTATUS rpc_conf_set_share(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *parent_hnd,
+ struct smbconf_service *service,
+ WERROR *werr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR result = WERR_OK;
+ WERROR _werr;
+ enum winreg_CreateAction action;
+ uint32_t i, j;
+
+ const char **includes;
+
+ struct winreg_String wkey, wkeyclass;
+ struct policy_handle share_hnd;
+
+ ZERO_STRUCT(share_hnd);
+ ZERO_STRUCT(wkey);
+ ZERO_STRUCT(wkeyclass);
+
+ wkey.name = service->name;
+ wkeyclass.name = "";
+ action = REG_ACTION_NONE;
+
+ status = dcerpc_winreg_CreateKey(b,
+ frame,
+ parent_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ REG_KEY_ALL,
+ NULL,
+ &share_hnd,
+ &action,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("winreg_CreateKey: Could not create smbconf key\n");
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ d_printf("winreg_CreateKey: Could not create smbconf key\n");
+ goto error;
+ }
+
+ for (i = 0; i < service->num_params; i++) {
+ if (strequal(service->param_names[i], "include") == 0)
+ {
+
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ service->param_names[i],
+ service->param_values[i],
+ &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr,
+ "ERROR: Share: '%s'\n"
+ "Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ service->name,
+ service->param_names[i],
+ service->param_values[i],
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr,
+ "ERROR: Share: '%s'\n"
+ "Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ service->name,
+ service->param_names[i],
+ service->param_values[i],
+ win_errstr(result));
+ goto error;
+ }
+ } else {
+
+ includes = talloc_zero_array(frame,
+ const char *,
+ service->num_params + 1);
+ if (includes == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, "ERROR: out of memory\n");
+ goto error;
+ }
+
+ for (j = i; j < service->num_params; j++) {
+
+ includes[j - i] = talloc_strdup(
+ frame,
+ service->param_values[j]);
+
+ if (includes[j-i] == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, "ERROR: out of memory\n");
+ goto error;
+ }
+ }
+
+ status = dcerpc_winreg_set_multi_sz(frame, b, &share_hnd,
+ "includes",
+ includes,
+ &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Share: '%s'\n"
+ "Could not set includes\n %s\n",
+ service->name,
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, "ERROR: Share: '%s'\n"
+ "Could not set includes\n %s\n",
+ service->name,
+ win_errstr(result));
+ goto error;
+ }
+
+ i = service->num_params;
+ }
+ }
+
+error:
+ /* in case of error, should it delete the created key? */
+ if (!(W_ERROR_IS_OK(result))) {
+ status = werror_to_ntstatus(result);
+
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+/*
+ * The function opens the registry database and retrieves
+ * as a smbconf_service struct the share with the name
+ * 'share_name'
+ */
+static NTSTATUS rpc_conf_get_share(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *parent_hnd,
+ const char *share_name,
+ struct smbconf_service *share,
+ WERROR *werr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR result = WERR_OK;
+ WERROR _werr;
+ struct policy_handle child_hnd;
+ int32_t includes_cnt, includes_idx = -1;
+ uint32_t num_vals, num_subkeys, i, param_cnt = 0;
+ const char **val_names;
+ const char **subkeys = NULL;
+ enum winreg_Type *types;
+ DATA_BLOB *data;
+ struct winreg_String key = { 0, };
+ const char **multi_s = NULL;
+ const char *s = NULL;
+ struct smbconf_service tmp_share;
+
+ ZERO_STRUCT(tmp_share);
+
+ /*
+ * Determine correct upper/lowercase.
+ */
+ status = dcerpc_winreg_enum_keys(frame,
+ b,
+ parent_hnd,
+ &num_subkeys,
+ &subkeys,
+ &result);
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to enumerate shares: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, _("Failed to enumerate shares: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+
+ for (i = 0; i < num_subkeys; i++) {
+ if (!strequal(share_name, subkeys[i])) {
+ continue;
+ }
+
+ key.name = subkeys[i];
+ }
+
+ if (key.name == NULL) {
+ d_fprintf(stderr, _("Could not find share.\n"));
+ goto error;
+ }
+
+ status = dcerpc_winreg_OpenKey(b, frame, parent_hnd, key, 0,
+ REG_KEY_READ, &child_hnd, &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to open subkey: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, _("Failed to open subkey: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ /* get all the info from the share key */
+ status = dcerpc_winreg_enumvals(frame,
+ b,
+ &child_hnd,
+ &num_vals,
+ &val_names,
+ &types,
+ &data,
+ &result);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to enumerate values: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, _("Failed to enumerate values: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ /* check for includes */
+ for (i = 0; i < num_vals; i++) {
+ if (strcmp(val_names[i], "includes") == 0){
+ if (!pull_reg_multi_sz(frame,
+ &data[i],
+ &multi_s))
+ {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr,
+ _("Failed to enumerate values: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ includes_idx = i;
+ }
+ }
+ /* count the number of includes */
+ includes_cnt = 0;
+ if (includes_idx != -1) {
+ for (includes_cnt = 0;
+ multi_s[includes_cnt] != NULL;
+ includes_cnt ++);
+ }
+ /* place the name of the share in the smbconf_service struct */
+ tmp_share.name = talloc_strdup(frame, key.name);
+ if (tmp_share.name == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ /* place the number of parameters in the smbconf_service struct */
+ tmp_share.num_params = num_vals;
+ if (includes_idx != -1) {
+ tmp_share.num_params = num_vals + includes_cnt - 1;
+ }
+ /* allocate memory for the param_names and param_values lists */
+ tmp_share.param_names = talloc_zero_array(frame, char *, tmp_share.num_params);
+ if (tmp_share.param_names == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ tmp_share.param_values = talloc_zero_array(frame, char *, tmp_share.num_params);
+ if (tmp_share.param_values == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ /* place all params except includes */
+ for (i = 0; i < num_vals; i++) {
+ if (strcmp(val_names[i], "includes") != 0) {
+ if (!pull_reg_sz(frame, &data[i], &s)) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr,
+ _("Failed to enumerate values: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ /* place param_names */
+ tmp_share.param_names[param_cnt] = talloc_strdup(frame, val_names[i]);
+ if (tmp_share.param_names[param_cnt] == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+
+ /* place param_values */
+ tmp_share.param_values[param_cnt++] = talloc_strdup(frame, s);
+ if (tmp_share.param_values[param_cnt - 1] == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ }
+ }
+ /* place the includes last */
+ for (i = 0; i < includes_cnt; i++) {
+ tmp_share.param_names[param_cnt] = talloc_strdup(frame, "include");
+ if (tmp_share.param_names[param_cnt] == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+
+ tmp_share.param_values[param_cnt++] = talloc_strdup(frame, multi_s[i]);
+ if (tmp_share.param_values[param_cnt - 1] == NULL) {
+ result = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+ }
+
+ /* move everything to the main memory ctx */
+ for (i = 0; i < param_cnt; i++) {
+ tmp_share.param_names[i] = talloc_move(mem_ctx, &tmp_share.param_names[i]);
+ tmp_share.param_values[i] = talloc_move(mem_ctx, &tmp_share.param_values[i]);
+ }
+
+ tmp_share.name = talloc_move(mem_ctx, &tmp_share.name);
+ tmp_share.param_names = talloc_move(mem_ctx, &tmp_share.param_names);
+ tmp_share.param_values = talloc_move(mem_ctx, &tmp_share.param_values);
+ /* out parameter */
+ *share = tmp_share;
+error:
+ /* close child */
+ dcerpc_winreg_CloseKey(b, frame, &child_hnd, &_werr);
+ *werr = result;
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/*
+ * The function prints the shares held as smbconf_service structs
+ * in a smbconf file format.
+ */
+static int rpc_conf_print_shares(uint32_t num_shares,
+ struct smbconf_service *shares)
+{
+
+ uint32_t share_count, param_count;
+ const char *indent = "\t";
+
+ if (num_shares == 0) {
+ return 0;
+ }
+
+ for (share_count = 0; share_count < num_shares; share_count++) {
+ d_printf("\n");
+ if (shares[share_count].name != NULL) {
+ d_printf("[%s]\n", shares[share_count].name);
+ }
+
+ for (param_count = 0;
+ param_count < shares[share_count].num_params;
+ param_count++)
+ {
+ d_printf("%s%s = %s\n",
+ indent,
+ shares[share_count].param_names[param_count],
+ shares[share_count].param_values[param_count]);
+ }
+ }
+ d_printf("\n");
+
+ return 0;
+
+}
+
+/*
+ * The function opens the registry key
+ * HKLM/Software/Samba/smbconf with the give access_mask
+ */
+static NTSTATUS rpc_conf_open_conf(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ uint32_t access_mask,
+ struct policy_handle *hive_hnd,
+ struct policy_handle *key_hnd,
+ WERROR *werr)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR result = WERR_OK;
+ WERROR _werr;
+ struct policy_handle tmp_hive_hnd, tmp_key_hnd;
+ struct winreg_String key;
+
+ ZERO_STRUCT(key);
+
+ status = dcerpc_winreg_OpenHKLM(b, frame, NULL,
+ access_mask, &tmp_hive_hnd, &result);
+
+ /*
+ * print no error messages if it is a read only open
+ * and key does not exist
+ * error still gets returned
+ */
+
+ if (access_mask == REG_KEY_READ &&
+ W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND))
+ {
+ goto error;
+ }
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to open hive: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, _("Failed to open hive: %s\n"),
+ win_errstr(result));
+ goto error;
+ }
+
+ key.name = confpath;
+ status = dcerpc_winreg_OpenKey(b, frame, &tmp_hive_hnd, key, 0,
+ access_mask, &tmp_key_hnd, &result);
+
+ /*
+ * print no error messages if it is a read only open
+ * and key does not exist
+ * error still gets returned
+ */
+
+ if (access_mask == REG_KEY_READ &&
+ W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND))
+ {
+ goto error;
+ }
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to open smbconf key: %s\n"),
+ nt_errstr(status));
+ dcerpc_winreg_CloseKey(b, frame, &tmp_hive_hnd, &_werr);
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(result))) {
+ d_fprintf(stderr, _("Failed to open smbconf key: %s\n"),
+ win_errstr(result));
+ dcerpc_winreg_CloseKey(b, frame, &tmp_hive_hnd, &_werr);
+ goto error;
+ }
+
+ *hive_hnd = tmp_hive_hnd;
+ *key_hnd = tmp_key_hnd;
+
+error:
+ TALLOC_FREE(frame);
+ *werr = result;
+
+ return status;
+}
+
+/**********************************************************
+ *
+ * internal functions that provide the functionality
+ * net rpc conf
+ *
+ **********************************************************/
+
+static NTSTATUS rpc_conf_listshares_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ uint32_t num_subkeys;
+ uint32_t i;
+ const char **subkeys = NULL;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 0 || c->display_usage) {
+ rpc_conf_listshares_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = dcerpc_winreg_enum_keys(frame,
+ b,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to enumerate keys: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, _("Failed to enumerate keys: %s\n"),
+ win_errstr(werr));
+ goto error;
+ }
+
+ for (i = 0; i < num_subkeys; i++) {
+ d_printf("%s\n", subkeys[i]);
+ }
+
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;;
+}
+
+static NTSTATUS rpc_conf_delshare_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 1 || c->display_usage) {
+ rpc_conf_delshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_ALL,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = dcerpc_winreg_delete_subkeys_recursive(frame,
+ b,
+ &key_hnd,
+ REG_KEY_ALL,
+ argv[0],
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "winreg_delete_subkeys: Could not delete key %s: %s\n",
+ argv[0], nt_errstr(status));
+ goto error;
+ }
+
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)){
+ d_fprintf(stderr, _("ERROR: Key does not exist\n"));
+ }
+
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr,
+ "winreg_delete_subkeys: Could not delete key %s: %s\n",
+ argv[0], win_errstr(werr));
+ goto error;
+ }
+
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+
+ return status;
+}
+
+static NTSTATUS rpc_conf_list_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ uint32_t num_subkeys;
+ uint32_t i;
+ struct smbconf_service *shares;
+ const char **subkeys = NULL;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 0 || c->display_usage) {
+ rpc_conf_list_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = dcerpc_winreg_enum_keys(frame,
+ b,
+ &key_hnd,
+ &num_subkeys,
+ &subkeys,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("Failed to enumerate keys: %s\n"),
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, _("Failed to enumerate keys: %s\n"),
+ win_errstr(werr));
+ goto error;
+ }
+
+ if (num_subkeys == 0) {
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+ TALLOC_FREE(frame);
+ return NT_STATUS_OK;
+ }
+
+ /* get info from each subkey */
+ shares = talloc_zero_array(frame, struct smbconf_service, num_subkeys);
+ if (shares == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create shares: %s\n"),
+ win_errstr(werr));
+ goto error;
+
+ }
+
+ for (i = 0; i < num_subkeys; i++) {
+ /* get each share and place it in the shares array */
+ status = rpc_conf_get_share(frame,
+ b,
+ &key_hnd,
+ subkeys[i],
+ &shares[i],
+ &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ }
+ /* print the shares array */
+ rpc_conf_print_shares(num_subkeys, shares);
+
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+static NTSTATUS rpc_conf_drop_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ const char *keyname = confpath;
+ struct winreg_String wkey, wkeyclass;
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 0 || c->display_usage) {
+ rpc_conf_drop_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_ALL,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = dcerpc_winreg_delete_subkeys_recursive(frame,
+ b,
+ &hive_hnd,
+ REG_KEY_ALL,
+ keyname,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("winreg_delete_subkeys: Could not delete key %s: %s\n",
+ keyname, nt_errstr(status));
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("winreg_delete_subkeys: Could not delete key %s: %s\n",
+ keyname, win_errstr(werr));
+ goto error;
+ }
+
+ ZERO_STRUCT(wkey);
+ wkey.name = keyname;
+ ZERO_STRUCT(wkeyclass);
+ wkeyclass.name = "";
+ action = REG_ACTION_NONE;
+
+ status = dcerpc_winreg_CreateKey(b,
+ frame,
+ &hive_hnd,
+ wkey,
+ wkeyclass,
+ 0,
+ REG_KEY_ALL,
+ NULL,
+ &key_hnd,
+ &action,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("winreg_CreateKey: Could not create smbconf key\n");
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("winreg_CreateKey: Could not create smbconf key\n");
+ goto error;
+ }
+
+
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_import_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ struct policy_handle hive_hnd, key_hnd;
+
+ const char *filename = NULL;
+ const char *servicename = NULL;
+ char *conf_source = NULL;
+ TALLOC_CTX *frame;
+ struct smbconf_ctx *txt_ctx;
+ struct smbconf_service *service = NULL;
+ struct smbconf_service **services = NULL;
+ uint32_t num_shares, i;
+ sbcErr err = SBC_ERR_UNKNOWN_FAILURE;
+
+ WERROR werr = WERR_OK;
+ NTSTATUS status = NT_STATUS_OK;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+ frame = talloc_stackframe();
+
+ if (c->display_usage) {
+ rpc_conf_import_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ switch (argc) {
+ case 0:
+ default:
+ rpc_conf_import_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ case 2:
+ servicename = talloc_strdup(frame, argv[1]);
+ if (servicename == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto error;
+ }
+
+ FALL_THROUGH;
+ case 1:
+ filename = argv[0];
+ break;
+ }
+
+ DEBUG(3,("rpc_conf_import: reading configuration from file %s.\n",
+ filename));
+
+ conf_source = talloc_asprintf(frame, "file:%s", filename);
+ if (conf_source == NULL) {
+ d_fprintf(stderr, _("error: out of memory!\n"));
+ goto error;
+ }
+
+ err = smbconf_init(frame, &txt_ctx, conf_source);
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, _("error loading file '%s': %s\n"), filename,
+ sbcErrorString(err));
+ goto error;
+ }
+
+ if (c->opt_testmode) {
+ d_printf(_("\nTEST MODE - "
+ "would import the following configuration:\n\n"));
+ }
+
+ if (servicename != NULL) {
+ err = smbconf_get_share(txt_ctx, frame,
+ servicename,
+ &service);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto error;
+ }
+
+ num_shares = 1;
+
+ } else {
+
+ err = smbconf_get_config(txt_ctx, frame,
+ &num_shares,
+ &services);
+ if (!SBC_ERROR_IS_OK(err)) {
+ goto error;
+ }
+ }
+
+ if (c->opt_testmode) {
+ if (servicename != NULL) {
+ rpc_conf_print_shares(1, service);
+ }
+ for (i = 0; i < num_shares; i++) {
+ rpc_conf_print_shares(1, services[i]);
+ }
+ goto error;
+ }
+
+ status = rpc_conf_drop_internal(c,
+ domain_sid,
+ domain_name,
+ cli,
+ pipe_hnd,
+ frame,
+ 0,
+ NULL );
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ if (servicename != NULL) {
+ status = rpc_conf_set_share(frame,
+ b,
+ &key_hnd,
+ service,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ } else {
+
+ for (i = 0; i < num_shares; i++) {
+ status = rpc_conf_set_share(frame,
+ b,
+ &key_hnd,
+ services[i],
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ }
+ }
+
+error:
+ if (!SBC_ERROR_IS_OK(err)) {
+ d_fprintf(stderr, "ERROR: %s\n", sbcErrorString(err));
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_showshare_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ struct smbconf_service *service = NULL;
+ const char *sharename = NULL;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 1 || c->display_usage) {
+ rpc_conf_showshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ sharename = talloc_strdup(frame, argv[0]);
+ if (sharename == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(werr));
+ goto error;
+ }
+
+ service = talloc(frame, struct smbconf_service);
+ if (service == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ d_fprintf(stderr, _("Failed to create share: %s\n"),
+ win_errstr(werr));
+ goto error;
+ }
+
+ status = rpc_conf_get_share(frame,
+ b,
+ &key_hnd,
+ sharename,
+ service,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ rpc_conf_print_shares(1, service);
+
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_addshare_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd, share_hnd;
+ char *sharename = NULL;
+ const char *path = NULL;
+ const char *comment = NULL;
+ const char *guest_ok = "no";
+ const char *read_only = "yes";
+ struct winreg_String key, keyclass;
+ enum winreg_CreateAction action = 0;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(share_hnd);
+
+ ZERO_STRUCT(key);
+ ZERO_STRUCT(keyclass);
+
+ if (c->display_usage) {
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ switch (argc) {
+ case 0:
+ case 1:
+ default:
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ case 5:
+ comment = argv[4];
+
+ FALL_THROUGH;
+ case 4:
+ if (!strnequal(argv[3], "guest_ok=", 9)) {
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+ switch (argv[3][9]) {
+ case 'y':
+ case 'Y':
+ guest_ok = "yes";
+ break;
+ case 'n':
+ case 'N':
+ guest_ok = "no";
+ break;
+ default:
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ FALL_THROUGH;
+ case 3:
+ if (!strnequal(argv[2], "writeable=", 10)) {
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+ switch (argv[2][10]) {
+ case 'y':
+ case 'Y':
+ read_only = "no";
+ break;
+ case 'n':
+ case 'N':
+ read_only = "yes";
+ break;
+ default:
+ rpc_conf_addshare_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ FALL_THROUGH;
+ case 2:
+ path = argv[1];
+ sharename = talloc_strdup(frame, argv[0]);
+ if (sharename == NULL) {
+ d_printf(_("error: out of memory!\n"));
+ goto error;
+ }
+
+ break;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ key.name = argv[0];
+ keyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass,
+ 0, REG_KEY_READ, NULL, &share_hnd,
+ &action, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], nt_errstr(status));
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], win_errstr(werr));
+ goto error;
+ }
+
+ switch (action) {
+ case REG_ACTION_NONE:
+ werr = WERR_CREATE_FAILED;
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], win_errstr(werr));
+ goto error;
+ case REG_CREATED_NEW_KEY:
+ DEBUG(5, ("net rpc conf setincludes:"
+ "createkey created %s\n", argv[0]));
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ d_fprintf(stderr, _("ERROR: Share '%s' already exists\n"), argv[0]);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ /* set the path parameter */
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ "path", path, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "path", path, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "path", path, win_errstr(werr));
+ goto error;
+ }
+
+ /* set the writeable parameter */
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ "read only", read_only, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "read only", read_only, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "read only", read_only, win_errstr(werr));
+ goto error;
+ }
+
+ /* set the guest ok parameter */
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ "guest ok", guest_ok, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "guest ok", guest_ok, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "guest ok", guest_ok, win_errstr(werr));
+ goto error;
+ }
+
+ if (argc == 5) {
+ /* set the comment parameter */
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ "comment", comment, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "comment", comment, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ "comment", comment, win_errstr(werr));
+ goto error;
+ }
+ }
+error:
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_getparm_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ struct smbconf_service *service = NULL;
+
+ bool param_is_set = false;
+ uint32_t param_count;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 2 || c->display_usage) {
+ rpc_conf_getparm_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+
+ service = talloc(frame, struct smbconf_service);
+
+ status = rpc_conf_get_share(frame,
+ b,
+ &key_hnd,
+ argv[0],
+ service,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) {
+ d_fprintf(stderr, _("ERROR: Share %s does not exist\n"),
+ argv[0]);
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ for (param_count = 0;
+ param_count < service->num_params;
+ param_count++)
+ {
+ /* should includes also be printed? */
+ if (strcmp(service->param_names[param_count], argv[1]) == 0) {
+ d_printf(_("%s\n"),
+ service->param_values[param_count]);
+ param_is_set = true;
+ }
+ }
+
+ if (!param_is_set) {
+ d_fprintf(stderr, _("ERROR: Given parameter '%s' has not been set\n"),
+ argv[1]);
+ werr = WERR_FILE_NOT_FOUND;
+ goto error;
+ }
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+static NTSTATUS rpc_conf_setparm_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd, share_hnd;
+
+ struct winreg_String key, keyclass;
+ enum winreg_CreateAction action = 0;
+
+ const char *service_name, *param_name, *valstr;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(share_hnd);
+
+ ZERO_STRUCT(key);
+ ZERO_STRUCT(keyclass);
+
+ if (argc != 3 || c->display_usage) {
+ rpc_conf_setparm_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ service_name = argv[0];
+ param_name = argv[1];
+ valstr = argv[2];
+
+ key.name = service_name;
+ keyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass,
+ 0, REG_KEY_READ, NULL, &share_hnd,
+ &action, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ service_name, nt_errstr(status));
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ service_name, win_errstr(werr));
+ goto error;
+ }
+
+ switch (action) {
+ case REG_ACTION_NONE:
+ werr = WERR_CREATE_FAILED;
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ service_name, win_errstr(werr));
+ goto error;
+ case REG_CREATED_NEW_KEY:
+ DEBUG(5, ("net rpc conf setparm:"
+ "createkey created %s\n", service_name));
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ DEBUG(5, ("net rpc conf setparm:"
+ "createkey opened existing %s\n",
+ service_name));
+
+ /* delete possibly existing value */
+ status = rpc_conf_del_value(frame,
+ b,
+ &key_hnd,
+ service_name,
+ param_name,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ break;
+ }
+
+ /*
+ * check if parameter is valid for writing
+ */
+
+ if (!net_conf_param_valid(service_name, param_name, valstr)) {
+ werr = WERR_INVALID_PARAMETER;
+ goto error;
+ }
+
+ /* set the parameter */
+ status = dcerpc_winreg_set_sz(frame, b, &share_hnd,
+ param_name, valstr, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ param_name, valstr, nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set parameter '%s'"
+ " with value %s\n %s\n",
+ param_name, valstr, win_errstr(werr));
+ goto error;
+ }
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_delparm_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 2 || c->display_usage) {
+ rpc_conf_delparm_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = rpc_conf_del_value(frame,
+ b,
+ &key_hnd,
+ argv[0],
+ argv[1],
+ &werr);
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+static NTSTATUS rpc_conf_getincludes_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+ struct smbconf_service *service = NULL;
+
+ uint32_t param_count;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 1 || c->display_usage) {
+ rpc_conf_getincludes_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ service = talloc(frame, struct smbconf_service);
+
+ status = rpc_conf_get_share(frame,
+ b,
+ &key_hnd,
+ argv[0],
+ service,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ for (param_count = 0;
+ param_count < service->num_params;
+ param_count++)
+ {
+ if (strcmp(service->param_names[param_count], "include") == 0) {
+ d_printf(_("%s = %s\n"),
+ service->param_names[param_count],
+ service->param_values[param_count]);
+ }
+ }
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+static NTSTATUS rpc_conf_setincludes_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd, share_hnd;
+
+ struct winreg_String key, keyclass;
+ enum winreg_CreateAction action = 0;
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+ ZERO_STRUCT(share_hnd);
+
+ ZERO_STRUCT(key);
+ ZERO_STRUCT(keyclass);
+
+ if (argc < 1 || c->display_usage) {
+ rpc_conf_setincludes_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ key.name = argv[0];
+ keyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass,
+ 0, REG_KEY_READ, NULL, &share_hnd,
+ &action, &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], nt_errstr(status));
+ goto error;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], win_errstr(werr));
+ goto error;
+ }
+
+ switch (action) {
+ case REG_ACTION_NONE:
+ /* Is there any other way to treat this? */
+ werr = WERR_CREATE_FAILED;
+ d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"),
+ argv[0], win_errstr(werr));
+ goto error;
+ case REG_CREATED_NEW_KEY:
+ DEBUG(5, ("net rpc conf setincludes:"
+ "createkey created %s\n", argv[0]));
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ DEBUG(5, ("net rpc conf setincludes:"
+ "createkey opened existing %s\n", argv[0]));
+
+ /* delete possibly existing value */
+ status = rpc_conf_del_value(frame,
+ b,
+ &key_hnd,
+ argv[0],
+ "includes",
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+ break;
+ }
+
+ /* set the 'includes' values */
+ status = dcerpc_winreg_set_multi_sz(frame, b, &share_hnd,
+ "includes", argv + 1, &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr, "ERROR: Could not set includes\n %s\n",
+ nt_errstr(status));
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ d_fprintf(stderr, "ERROR: Could not set includes\n %s\n",
+ win_errstr(werr));
+ goto error;
+ }
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS rpc_conf_delincludes_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ NTSTATUS status = NT_STATUS_OK;
+ WERROR werr = WERR_OK;
+ WERROR _werr;
+
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* key info */
+ struct policy_handle hive_hnd, key_hnd;
+
+
+ ZERO_STRUCT(hive_hnd);
+ ZERO_STRUCT(key_hnd);
+
+
+ if (argc != 1 || c->display_usage) {
+ rpc_conf_delincludes_usage(c, argc, argv);
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ status = rpc_conf_open_conf(frame,
+ b,
+ REG_KEY_READ,
+ &hive_hnd,
+ &key_hnd,
+ &werr);
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ goto error;
+ }
+
+ status = rpc_conf_del_value(frame,
+ b,
+ &key_hnd,
+ argv[0],
+ "includes",
+ &werr);
+
+error:
+
+ if (!(W_ERROR_IS_OK(werr))) {
+ status = werror_to_ntstatus(werr);
+ }
+
+ dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr);
+ dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr);
+
+ TALLOC_FREE(frame);
+ return status;
+
+}
+
+/**********************************************************
+ *
+ * Functions that run the rpc commands for net rpc conf modules
+ *
+ **********************************************************/
+
+static int rpc_conf_drop(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_drop_internal, argc, argv );
+
+}
+
+static int rpc_conf_showshare(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_showshare_internal, argc, argv );
+}
+
+static int rpc_conf_addshare(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_addshare_internal, argc, argv );
+}
+
+static int rpc_conf_listshares(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_listshares_internal, argc, argv );
+}
+
+static int rpc_conf_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_list_internal, argc, argv );
+}
+
+static int rpc_conf_import(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_import_internal, argc, argv );
+}
+static int rpc_conf_delshare(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_delshare_internal, argc, argv );
+}
+
+static int rpc_conf_getparm(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_getparm_internal, argc, argv );
+}
+
+static int rpc_conf_setparm(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_setparm_internal, argc, argv );
+}
+static int rpc_conf_delparm(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_delparm_internal, argc, argv );
+}
+
+static int rpc_conf_getincludes(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_getincludes_internal, argc, argv );
+}
+
+static int rpc_conf_setincludes(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_setincludes_internal, argc, argv );
+}
+
+static int rpc_conf_delincludes(struct net_context *c, int argc,
+ const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_conf_delincludes_internal, argc, argv );
+}
+
+/* function calls */
+int net_rpc_conf(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct functable func_table[] = {
+ {
+ "list",
+ rpc_conf_list,
+ NET_TRANSPORT_RPC,
+ N_("Dump the complete remote configuration in smb.conf like "
+ "format."),
+ N_("net rpc conf list\n"
+ " Dump the complete remote configuration in smb.conf "
+ "like format.")
+
+ },
+ {
+ "import",
+ rpc_conf_import,
+ NET_TRANSPORT_RPC,
+ N_("Import configuration from file in smb.conf "
+ "format."),
+ N_("net rpc conf import\n"
+ " Import configuration from file in smb.conf "
+ "format.")
+ },
+ {
+ "listshares",
+ rpc_conf_listshares,
+ NET_TRANSPORT_RPC,
+ N_("List the remote share names."),
+ N_("net rpc conf list\n"
+ " List the remote share names.")
+
+ },
+ {
+ "drop",
+ rpc_conf_drop,
+ NET_TRANSPORT_RPC,
+ N_("Delete the complete remote configuration."),
+ N_("net rpc conf drop\n"
+ " Delete the complete remote configuration.")
+
+ },
+ {
+ "showshare",
+ rpc_conf_showshare,
+ NET_TRANSPORT_RPC,
+ N_("Show the definition of a remote share."),
+ N_("net rpc conf showshare\n"
+ " Show the definition of a remote share.")
+
+ },
+ {
+ "addshare",
+ rpc_conf_addshare,
+ NET_TRANSPORT_RPC,
+ N_("Create a new remote share."),
+ N_("net rpc conf addshare\n"
+ " Create a new remote share.")
+ },
+ {
+ "delshare",
+ rpc_conf_delshare,
+ NET_TRANSPORT_RPC,
+ N_("Delete a remote share."),
+ N_("net rpc conf delshare\n"
+ " Delete a remote share.")
+ },
+ {
+ "getparm",
+ rpc_conf_getparm,
+ NET_TRANSPORT_RPC,
+ N_("Retrieve the value of a parameter."),
+ N_("net rpc conf getparm\n"
+ " Retrieve the value of a parameter.")
+ },
+ {
+ "setparm",
+ rpc_conf_setparm,
+ NET_TRANSPORT_RPC,
+ N_("Store a parameter."),
+ N_("net rpc conf setparm\n"
+ " Store a parameter.")
+ },
+ {
+ "delparm",
+ rpc_conf_delparm,
+ NET_TRANSPORT_RPC,
+ N_("Delete a parameter."),
+ N_("net rpc conf delparm\n"
+ " Delete a parameter.")
+ },
+ {
+ "getincludes",
+ rpc_conf_getincludes,
+ NET_TRANSPORT_RPC,
+ N_("Show the includes of a share definition."),
+ N_("net rpc conf getincludes\n"
+ " Show the includes of a share definition.")
+ },
+ {
+ "setincludes",
+ rpc_conf_setincludes,
+ NET_TRANSPORT_RPC,
+ N_("Set includes for a share."),
+ N_("net rpc conf setincludes\n"
+ " Set includes for a share.")
+ },
+ {
+ "delincludes",
+ rpc_conf_delincludes,
+ NET_TRANSPORT_RPC,
+ N_("Delete includes from a share definition."),
+ N_("net rpc conf delincludes\n"
+ " Delete includes from a share definition.")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc conf", func_table);
+
+}
diff --git a/source3/utils/net_rpc_printer.c b/source3/utils/net_rpc_printer.c
new file mode 100644
index 0000000..f72203f
--- /dev/null
+++ b/source3/utils/net_rpc_printer.c
@@ -0,0 +1,2624 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2004,2009 Guenther Deschner (gd@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "system/filesys.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_spoolss_c.h"
+#include "rpc_client/cli_spoolss.h"
+#include "rpc_client/init_spoolss.h"
+#include "nt_printing.h"
+#include "registry.h"
+#include "../libcli/security/security.h"
+#include "../libcli/registry/util_reg.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "auth/gensec/gensec.h"
+#include "auth/credentials/credentials.h"
+#include "lib/util/string_wrappers.h"
+
+
+/**
+ * This display-printdriver-functions was borrowed from rpcclient/cmd_spoolss.c.
+ * It is here for debugging purpose and should be removed later on.
+ **/
+
+/****************************************************************************
+ Printer info level 3 display function.
+****************************************************************************/
+
+static void display_print_driver3(struct spoolss_DriverInfo3 *r)
+{
+ int i;
+
+ if (!r) {
+ return;
+ }
+
+ printf(_("Printer Driver Info 3:\n"));
+ printf(_("\tVersion: [%x]\n"), r->version);
+ printf(_("\tDriver Name: [%s]\n"), r->driver_name);
+ printf(_("\tArchitecture: [%s]\n"), r->architecture);
+ printf(_("\tDriver Path: [%s]\n"), r->driver_path);
+ printf(_("\tDatafile: [%s]\n"), r->data_file);
+ printf(_("\tConfigfile: [%s]\n\n"), r->config_file);
+ printf(_("\tHelpfile: [%s]\n\n"), r->help_file);
+
+ for (i=0; r->dependent_files && r->dependent_files[i] != NULL; i++) {
+ printf(_("\tDependentfiles: [%s]\n"), r->dependent_files[i]);
+ }
+
+ printf("\n");
+
+ printf(_("\tMonitorname: [%s]\n"), r->monitor_name);
+ printf(_("\tDefaultdatatype: [%s]\n\n"), r->default_datatype);
+}
+
+static void display_reg_value(const char *subkey, const char *name, struct registry_value *value)
+{
+ const char *text;
+
+ switch(value->type) {
+ case REG_DWORD:
+ if (value->data.length == sizeof(uint32_t)) {
+ d_printf(_("\t[%s:%s]: REG_DWORD: 0x%08x\n"), subkey,
+ name, IVAL(value->data.data,0));
+ } else {
+ d_printf(_("\t[%s:%s]: REG_DWORD: <invalid>\n"), subkey,
+ name);
+ }
+ break;
+
+ case REG_SZ:
+ pull_reg_sz(talloc_tos(), &value->data, &text);
+ if (!text) {
+ break;
+ }
+ d_printf(_("\t[%s:%s]: REG_SZ: %s\n"), subkey, name, text);
+ break;
+
+ case REG_BINARY:
+ d_printf(_("\t[%s:%s]: REG_BINARY: unknown length value not "
+ "displayed\n"),
+ subkey, name);
+ break;
+
+ case REG_MULTI_SZ: {
+ uint32_t i;
+ const char **values;
+
+ if (!pull_reg_multi_sz(NULL, &value->data, &values)) {
+ d_printf("pull_reg_multi_sz failed\n");
+ break;
+ }
+
+ printf("%s: REG_MULTI_SZ: \n", name);
+ for (i=0; values[i] != NULL; i++) {
+ d_printf("%s\n", values[i]);
+ }
+ TALLOC_FREE(values);
+ break;
+ }
+
+ default:
+ d_printf(_("\t%s: unknown type %d\n"), name, value->type);
+ }
+
+}
+
+/**
+ * Copies ACLs, DOS-attributes and timestamps from one
+ * file or directory from one connected share to another connected share
+ *
+ * @param c A net_context structure
+ * @param mem_ctx A talloc-context
+ * @param cli_share_src A connected cli_state
+ * @param cli_share_dst A connected cli_state
+ * @param src_file The source file-name
+ * @param dst_file The destination file-name
+ * @param copy_acls Whether to copy acls
+ * @param copy_attrs Whether to copy DOS attributes
+ * @param copy_timestamps Whether to preserve timestamps
+ * @param is_file Whether this file is a file or a dir
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS net_copy_fileattr(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *src_name, const char *dst_name,
+ bool copy_acls, bool copy_attrs,
+ bool copy_timestamps, bool is_file)
+{
+ NTSTATUS nt_status;
+ uint16_t fnum_src = 0;
+ uint16_t fnum_dst = 0;
+ struct security_descriptor *sd = NULL;
+ uint32_t attr = (uint32_t)-1;
+ struct timespec f_create_time = { .tv_nsec = SAMBA_UTIME_OMIT };
+ struct timespec f_access_time = { .tv_nsec = SAMBA_UTIME_OMIT };
+ struct timespec f_write_time = { .tv_nsec = SAMBA_UTIME_OMIT };
+ struct timespec f_change_time = { .tv_nsec = SAMBA_UTIME_OMIT };
+
+ if (!copy_timestamps && !copy_acls && !copy_attrs)
+ return NT_STATUS_OK;
+
+ /* open file/dir on the originating server */
+
+ DEBUGADD(3,("opening %s %s on originating server\n",
+ is_file?"file":"dir", src_name));
+
+ nt_status = cli_ntcreate(cli_share_src, src_name, 0,
+ READ_CONTROL_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
+ 0x0, 0x0, &fnum_src, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUGADD(0,("cannot open %s %s on originating server %s\n",
+ is_file?"file":"dir", src_name, nt_errstr(nt_status)));
+ goto out;
+ }
+
+ if (copy_acls) {
+ /* get the security descriptor */
+ nt_status = cli_query_secdesc(cli_share_src, fnum_src,
+ mem_ctx, &sd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("failed to get security descriptor: %s\n",
+ nt_errstr(nt_status)));
+ goto out;
+ }
+
+ if (c->opt_verbose && DEBUGLEVEL >= 3)
+ display_sec_desc(sd);
+ }
+
+ if (copy_attrs || copy_timestamps) {
+
+ /* get file attributes */
+ nt_status = cli_qfileinfo_basic(
+ cli_share_src,
+ fnum_src,
+ copy_attrs ? &attr : NULL,
+ NULL, /* size */
+ copy_timestamps ? &f_create_time : NULL,
+ copy_timestamps ? &f_access_time : NULL,
+ copy_timestamps ? &f_write_time : NULL,
+ copy_timestamps ? &f_change_time : NULL,
+ NULL); /* ino */
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("failed to get file-attrs: %s\n",
+ nt_errstr(nt_status)));
+ goto out;
+ }
+ }
+
+ /* open the file/dir on the destination server */
+ nt_status = cli_ntcreate(cli_share_dst, dst_name, 0,
+ WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN,
+ 0x0, 0x0, &fnum_dst, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("failed to open %s on the destination server: %s: %s\n",
+ is_file?"file":"dir", dst_name, nt_errstr(nt_status)));
+ goto out;
+ }
+
+ if (copy_acls) {
+ /* set acls */
+ nt_status = cli_set_secdesc(cli_share_dst, fnum_dst, sd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("could not set secdesc on %s: %s\n",
+ dst_name, nt_errstr(nt_status)));
+ goto out;
+ }
+ }
+
+ if (copy_timestamps || copy_attrs) {
+
+ nt_status = cli_setfileinfo_ext(
+ cli_share_dst,
+ fnum_dst,
+ f_create_time,
+ f_access_time,
+ f_write_time,
+ f_change_time,
+ attr);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DBG_ERR("failed to set file-attrs: %s\n",
+ nt_errstr(nt_status));
+ goto out;
+ }
+ }
+
+
+ /* closing files */
+ nt_status = cli_close(cli_share_src, fnum_src);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("could not close %s on originating server: %s\n"),
+ is_file?"file":"dir", nt_errstr(nt_status));
+ goto out;
+ }
+
+ nt_status = cli_close(cli_share_dst, fnum_dst);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("could not close %s on destination server: %s\n"),
+ is_file?"file":"dir", nt_errstr(nt_status));
+ goto out;
+ }
+
+
+ nt_status = NT_STATUS_OK;
+
+out:
+
+ /* cleaning up */
+ if (fnum_src)
+ cli_close(cli_share_src, fnum_src);
+
+ if (fnum_dst)
+ cli_close(cli_share_dst, fnum_dst);
+
+ return nt_status;
+}
+
+/**
+ * Copy a file or directory from a connected share to another connected share
+ *
+ * @param c A net_context structure
+ * @param mem_ctx A talloc-context
+ * @param cli_share_src A connected cli_state
+ * @param cli_share_dst A connected cli_state
+ * @param src_file The source file-name
+ * @param dst_file The destination file-name
+ * @param copy_acls Whether to copy acls
+ * @param copy_attrs Whether to copy DOS attributes
+ * @param copy_timestamps Whether to preserve timestamps
+ * @param is_file Whether this file is a file or a dir
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS net_copy_file(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *src_name, const char *dst_name,
+ bool copy_acls, bool copy_attrs,
+ bool copy_timestamps, bool is_file)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint16_t fnum_src = 0;
+ uint16_t fnum_dst = 0;
+ static int io_bufsize = 64512;
+ int read_size = io_bufsize;
+ char *data = NULL;
+ off_t nread = 0;
+
+
+ if (!src_name || !dst_name)
+ goto out;
+
+ if (cli_share_src == NULL || cli_share_dst == NULL)
+ goto out;
+
+ /* open on the originating server */
+ DEBUGADD(3,("opening %s %s on originating server\n",
+ is_file ? "file":"dir", src_name));
+ if (is_file)
+ nt_status = cli_open(cli_share_src, src_name, O_RDONLY, DENY_NONE, &fnum_src);
+ else
+ nt_status = cli_ntcreate(cli_share_src, src_name, 0, READ_CONTROL_ACCESS, 0,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x0, 0x0, &fnum_src, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUGADD(0,("cannot open %s %s on originating server %s\n",
+ is_file ? "file":"dir",
+ src_name, nt_errstr(nt_status)));
+ goto out;
+ }
+
+
+ if (is_file) {
+
+ /* open file on the destination server */
+ DEBUGADD(3,("opening file %s on destination server\n", dst_name));
+ nt_status = cli_open(cli_share_dst, dst_name,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE, &fnum_dst);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUGADD(1,("cannot create file %s on destination server: %s\n",
+ dst_name, nt_errstr(nt_status)));
+ goto out;
+ }
+
+ /* allocate memory */
+ if (!(data = (char *)SMB_MALLOC(read_size))) {
+ d_fprintf(stderr, _("malloc fail for size %d\n"),
+ read_size);
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ }
+
+
+ if (c->opt_verbose) {
+
+ d_printf(_("copying [\\\\%s\\%s%s] => [\\\\%s\\%s%s] "
+ "%s ACLs and %s DOS Attributes %s\n"),
+ smbXcli_conn_remote_name(cli_share_src->conn),
+ cli_share_src->share, src_name,
+ smbXcli_conn_remote_name(cli_share_dst->conn),
+ cli_share_dst->share, dst_name,
+ copy_acls ? _("with") : _("without"),
+ copy_attrs ? _("with") : _("without"),
+ copy_timestamps ? _("(preserving timestamps)") : "" );
+ }
+
+
+ while (is_file) {
+
+ /* copying file */
+ size_t n;
+
+ nt_status = cli_read(cli_share_src, fnum_src, data, nread,
+ read_size, &n);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("Error reading file [\\\\%s\\%s%s]: %s\n"),
+ smbXcli_conn_remote_name(cli_share_src->conn),
+ cli_share_src->share,
+ src_name, nt_errstr(nt_status));
+ goto out;
+ }
+
+ if (n == 0)
+ break;
+
+ nt_status = cli_writeall(cli_share_dst, fnum_dst, 0,
+ (uint8_t *)data, nread, n, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("Error writing file: [\\\\%s\\%s%s]: %s\n"),
+ smbXcli_conn_remote_name(cli_share_dst->conn),
+ cli_share_dst->share,
+ dst_name, nt_errstr(nt_status));
+ goto out;
+ }
+
+ nread += n;
+ }
+
+
+ if (!is_file && !NT_STATUS_IS_OK(cli_chkpath(cli_share_dst, dst_name))) {
+
+ /* creating dir */
+ DEBUGADD(3,("creating dir %s on the destination server\n",
+ dst_name));
+
+ nt_status = cli_mkdir(cli_share_dst, dst_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("cannot create directory %s: %s\n",
+ dst_name, nt_errstr(nt_status)));
+ nt_status = NT_STATUS_NO_SUCH_FILE;
+ }
+
+
+ nt_status = cli_chkpath(cli_share_dst, dst_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("cannot check for directory %s: %s\n"),
+ dst_name, nt_errstr(nt_status));
+ goto out;
+ }
+ }
+
+
+ /* closing files */
+ nt_status = cli_close(cli_share_src, fnum_src);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("could not close file on originating server: %s\n"),
+ nt_errstr(nt_status));
+ goto out;
+ }
+
+ if (is_file) {
+ nt_status = cli_close(cli_share_dst, fnum_dst);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr,
+ _("could not close file on destination server: %s\n"),
+ nt_errstr(nt_status));
+ goto out;
+ }
+ }
+
+ /* possibly we have to copy some file-attributes / acls / sd */
+ nt_status = net_copy_fileattr(c, mem_ctx, cli_share_src, cli_share_dst,
+ src_name, dst_name, copy_acls,
+ copy_attrs, copy_timestamps, is_file);
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto out;
+
+
+ nt_status = NT_STATUS_OK;
+
+out:
+
+ /* cleaning up */
+ if (fnum_src)
+ cli_close(cli_share_src, fnum_src);
+
+ if (fnum_dst)
+ cli_close(cli_share_dst, fnum_dst);
+
+ SAFE_FREE(data);
+
+ return nt_status;
+}
+
+/**
+ * Copy a driverfile from on connected share to another connected share
+ * This silently assumes that a driver-file is picked up from
+ *
+ * \\src_server\print$\{arch}\{version}\file
+ *
+ * and copied to
+ *
+ * \\dst_server\print$\{arch}\file
+ *
+ * to be added via setdriver-calls later.
+ * @param c A net_context structure
+ * @param mem_ctx A talloc-context
+ * @param cli_share_src A cli_state connected to source print$-share
+ * @param cli_share_dst A cli_state connected to destination print$-share
+ * @param file The file-name to be copied
+ * @param short_archi The name of the driver-architecture (short form)
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS net_copy_driverfile(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *file, const char *short_archi) {
+
+ const char *p;
+ char *src_name;
+ char *dst_name;
+ char *version = NULL;
+ char *filename = NULL;
+ char *tok;
+
+ if (!file) {
+ return NT_STATUS_OK;
+ }
+
+ /* scroll through the file until we have the part
+ beyond archi_table.short_archi */
+ p = file;
+ while (next_token_talloc(mem_ctx, &p, &tok, "\\")) {
+ if (strequal(tok, short_archi)) {
+ next_token_talloc(mem_ctx, &p, &version, "\\");
+ next_token_talloc(mem_ctx, &p, &filename, "\\");
+ }
+ }
+
+ if (version == NULL || filename == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* build source file name */
+ src_name = talloc_asprintf(mem_ctx, "\\%s\\%s\\%s",
+ short_archi, version, filename);
+ if (src_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* create destination file name */
+ dst_name = talloc_asprintf(mem_ctx, "\\%s\\%s", short_archi, filename);
+ if (dst_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ /* finally copy the file */
+ return net_copy_file(c, mem_ctx, cli_share_src, cli_share_dst,
+ src_name, dst_name, false, false, false, true);
+}
+
+/**
+ * Check for existing Architecture directory on a given server
+ *
+ * @param cli_share A cli_state connected to a print$-share
+ * @param short_archi The Architecture for the print-driver
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS check_arch_dir(struct cli_state *cli_share, const char *short_archi)
+{
+
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ char *dir;
+
+ if (asprintf(&dir, "\\%s", short_archi) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(10,("creating print-driver dir for architecture: %s\n",
+ short_archi));
+
+ nt_status = cli_mkdir(cli_share, dir);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("cannot create directory %s: %s\n",
+ dir, nt_errstr(nt_status)));
+ }
+
+ nt_status = cli_chkpath(cli_share, dir);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, _("cannot check %s: %s\n"),
+ dir, nt_errstr(nt_status));
+ goto out;
+ }
+
+ nt_status = NT_STATUS_OK;
+
+out:
+ SAFE_FREE(dir);
+ return nt_status;
+}
+
+/**
+ * Copy a print-driver (level 3) from one connected print$-share to another
+ * connected print$-share
+ *
+ * @param c A net_context structure
+ * @param mem_ctx A talloc-context
+ * @param cli_share_src A cli_state connected to a print$-share
+ * @param cli_share_dst A cli_state connected to a print$-share
+ * @param short_archi The Architecture for the print-driver
+ * @param i1 The DRIVER_INFO_3-struct
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS copy_print_driver_3(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct cli_state *cli_share_src,
+ struct cli_state *cli_share_dst,
+ const char *short_archi,
+ struct spoolss_DriverInfo3 *r)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ int i;
+
+ if (r == NULL) {
+ return nt_status;
+ }
+
+ if (c->opt_verbose)
+ d_printf(_("copying driver: [%s], for architecture: [%s], "
+ "version: [%d]\n"),
+ r->driver_name, short_archi, r->version);
+
+ nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst,
+ r->driver_path, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst,
+ r->data_file, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst,
+ r->config_file, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst,
+ r->help_file, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ for (i=0; r->dependent_files[i] != NULL; i++) {
+
+ nt_status = net_copy_driverfile(c, mem_ctx,
+ cli_share_src, cli_share_dst,
+ r->dependent_files[i], short_archi);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * net_spoolss-functions
+ * =====================
+ *
+ * the net_spoolss-functions aim to simplify spoolss-client-functions
+ * required during the migration-process wrt buffer-sizes, returned
+ * error-codes, etc.
+ *
+ * this greatly reduces the complexitiy of the migrate-functions.
+ *
+ **/
+
+static bool net_spoolss_enum_printers(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ char *name,
+ uint32_t flags,
+ uint32_t level,
+ uint32_t *num_printers,
+ union spoolss_PrinterInfo **info)
+{
+ WERROR result;
+
+ /* enum printers */
+
+ result = rpccli_spoolss_enumprinters(pipe_hnd, mem_ctx,
+ flags,
+ name,
+ level,
+ 0,
+ num_printers,
+ info);
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("cannot enum printers: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_open_printer_ex(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const char *printername,
+ uint32_t access_required,
+ struct policy_handle *hnd)
+{
+ struct cli_credentials *creds = gensec_get_credentials(pipe_hnd->auth->auth_ctx);
+ const char *username = cli_credentials_get_username(creds);
+ WERROR result;
+ fstring printername2;
+
+ fstrcpy(printername2, pipe_hnd->srv_name_slash);
+ fstrcat(printername2, "\\");
+ fstrcat(printername2, printername);
+
+ DEBUG(10,("connecting to: %s as %s for %s and access: %x\n",
+ pipe_hnd->srv_name_slash, username, printername2, access_required));
+
+ /* open printer */
+ result = rpccli_spoolss_openprinter_ex(pipe_hnd, mem_ctx,
+ printername2,
+ access_required,
+ hnd);
+
+ /* be more verbose */
+ if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) {
+ d_fprintf(stderr,
+ _("no access to printer [%s] on [%s] for user [%s] "
+ "granted\n"),
+ printername2, pipe_hnd->srv_name_slash, username);
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr,_("cannot open printer %s on server %s: %s\n"),
+ printername2, pipe_hnd->srv_name_slash, win_errstr(result));
+ return false;
+ }
+
+ DEBUG(2,("got printer handle for printer: %s, server: %s\n",
+ printername2, pipe_hnd->srv_name_slash));
+
+ return true;
+}
+
+static bool net_spoolss_getprinter(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ uint32_t level,
+ union spoolss_PrinterInfo *info)
+{
+ WERROR result;
+
+ /* getprinter call */
+ result = rpccli_spoolss_getprinter(pipe_hnd, mem_ctx,
+ hnd,
+ level,
+ 0, /* offered */
+ info);
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("cannot get printer-info: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_setprinter(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ uint32_t level,
+ union spoolss_PrinterInfo *info)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ WERROR result;
+ NTSTATUS status;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_SetPrinterInfo2 info2;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ /* setprinter call */
+
+ info_ctr.level = level;
+ switch (level) {
+ case 0:
+ info_ctr.info.info0 = (struct spoolss_SetPrinterInfo0 *)
+ (void *)&info->info0;
+ break;
+ case 1:
+ info_ctr.info.info1 = (struct spoolss_SetPrinterInfo1 *)
+ (void *)&info->info1;
+ break;
+ case 2:
+ spoolss_printerinfo2_to_setprinterinfo2(&info->info2, &info2);
+ info_ctr.info.info2 = &info2;
+ break;
+ case 3:
+ info_ctr.info.info3 = (struct spoolss_SetPrinterInfo3 *)
+ (void *)&info->info3;
+ break;
+ case 4:
+ info_ctr.info.info4 = (struct spoolss_SetPrinterInfo4 *)
+ (void *)&info->info4;
+ break;
+ case 5:
+ info_ctr.info.info5 = (struct spoolss_SetPrinterInfo5 *)
+ (void *)&info->info5;
+ break;
+ case 6:
+ info_ctr.info.info6 = (struct spoolss_SetPrinterInfo6 *)
+ (void *)&info->info6;
+ break;
+ case 7:
+ info_ctr.info.info7 = (struct spoolss_SetPrinterInfo7 *)
+ (void *)&info->info7;
+ break;
+#if 0 /* FIXME GD */
+ case 8:
+ info_ctr.info.info8 = (struct spoolss_SetPrinterInfo8 *)
+ (void *)&info->info8;
+ break;
+ case 9:
+ info_ctr.info.info9 = (struct spoolss_SetPrinterInfo9 *)
+ (void *)&info->info9;
+ break;
+#endif
+ default:
+ break; /* FIXME */
+ }
+
+ status = dcerpc_spoolss_SetPrinter(b, mem_ctx,
+ hnd,
+ &info_ctr,
+ &devmode_ctr,
+ &secdesc_ctr,
+ 0, /* command */
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(_("cannot set printer-info: %s\n"), nt_errstr(status));
+ return false;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("cannot set printer-info: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_setprinterdata(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ const char *value_name,
+ enum winreg_Type type,
+ uint8_t *data,
+ uint32_t offered)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ WERROR result;
+ NTSTATUS status;
+
+ /* setprinterdata call */
+ status = dcerpc_spoolss_SetPrinterData(b, mem_ctx,
+ hnd,
+ value_name,
+ type,
+ data,
+ offered,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf (_("unable to set printerdata: %s\n"),
+ nt_errstr(status));
+ return false;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ printf (_("unable to set printerdata: %s\n"),
+ win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_enumprinterkey(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ const char *keyname,
+ const char ***keylist)
+{
+ WERROR result;
+
+ /* enumprinterkey call */
+ result = rpccli_spoolss_enumprinterkey(pipe_hnd, mem_ctx, hnd, keyname, keylist, 0);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("enumprinterkey failed: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumprinterdataex(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ uint32_t offered,
+ struct policy_handle *hnd,
+ const char *keyname,
+ uint32_t *count,
+ struct spoolss_PrinterEnumValues **info)
+{
+ WERROR result;
+
+ /* enumprinterdataex call */
+ result = rpccli_spoolss_enumprinterdataex(pipe_hnd, mem_ctx,
+ hnd,
+ keyname,
+ 0, /* offered */
+ count,
+ info);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("enumprinterdataex failed: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_setprinterdataex(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ const char *keyname,
+ const char *name,
+ struct registry_value *value)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ WERROR result;
+ NTSTATUS status;
+
+ /* setprinterdataex call */
+ status = dcerpc_spoolss_SetPrinterDataEx(b, mem_ctx,
+ hnd,
+ keyname,
+ name,
+ value->type,
+ value->data.data,
+ value->data.length,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(_("could not set printerdataex: %s\n"),
+ nt_errstr(status));
+ return false;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("could not set printerdataex: %s\n"),
+ win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumforms(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd,
+ int level,
+ uint32_t *num_forms,
+ union spoolss_FormInfo **forms)
+{
+ WERROR result;
+
+ /* enumforms call */
+ result = rpccli_spoolss_enumforms(pipe_hnd, mem_ctx,
+ hnd,
+ level,
+ 0,
+ num_forms,
+ forms);
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("could not enum forms: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumprinterdrivers (struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ uint32_t level, const char *env,
+ uint32_t *count,
+ union spoolss_DriverInfo **info)
+{
+ WERROR result;
+
+ /* enumprinterdrivers call */
+ result = rpccli_spoolss_enumprinterdrivers(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ env,
+ level,
+ 0,
+ count,
+ info);
+ if (!W_ERROR_IS_OK(result)) {
+ if (W_ERROR_V(result) != W_ERROR_V(WERR_INVALID_ENVIRONMENT)) {
+ printf(_("cannot enum drivers for environment %s: %s\n"), env,
+ win_errstr(result));
+ return false;
+ } else {
+ printf(_("Server does not support environment [%s]\n"),
+ env);
+ }
+ }
+
+ return true;
+}
+
+static bool net_spoolss_getprinterdriver(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hnd, uint32_t level,
+ const char *env, int version,
+ union spoolss_DriverInfo *info)
+{
+ WERROR result;
+ uint32_t server_major_version;
+ uint32_t server_minor_version;
+
+ /* getprinterdriver call */
+ result = rpccli_spoolss_getprinterdriver2(pipe_hnd, mem_ctx,
+ hnd,
+ env,
+ level,
+ 0,
+ version,
+ 2,
+ info,
+ &server_major_version,
+ &server_minor_version);
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(1,("cannot get driver (for architecture: %s): %s\n",
+ env, win_errstr(result)));
+ if (W_ERROR_V(result) != W_ERROR_V(WERR_UNKNOWN_PRINTER_DRIVER) &&
+ W_ERROR_V(result) != W_ERROR_V(WERR_INVALID_ENVIRONMENT)) {
+ printf(_("cannot get driver: %s\n"),
+ win_errstr(result));
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_addprinterdriver(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx, uint32_t level,
+ union spoolss_DriverInfo *info)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ WERROR result;
+ NTSTATUS status;
+ struct spoolss_AddDriverInfoCtr info_ctr;
+
+ info_ctr.level = level;
+
+ switch (level) {
+ case 2:
+ info_ctr.info.info2 = (struct spoolss_AddDriverInfo2 *)
+ (void *)&info->info2;
+ break;
+ case 3:
+ info_ctr.info.info3 = (struct spoolss_AddDriverInfo3 *)
+ (void *)&info->info3;
+ break;
+ default:
+ printf(_("unsupported info level: %d\n"), level);
+ return false;
+ }
+
+ /* addprinterdriver call */
+ status = dcerpc_spoolss_AddPrinterDriver(b, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ &info_ctr,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf(_("cannot add driver: %s\n"), nt_errstr(status));
+ return false;
+ }
+ /* be more verbose */
+ if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) {
+ printf(_("You are not allowed to add drivers\n"));
+ return false;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ printf(_("cannot add driver: %s\n"), win_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * abstraction function to get uint32_t num_printers and PRINTER_INFO_CTR ctr
+ * for a single printer or for all printers depending on argc/argv
+ **/
+
+static bool get_printer_info(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int level,
+ int argc,
+ const char **argv,
+ uint32_t *num_printers,
+ union spoolss_PrinterInfo **info_p)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ struct policy_handle hnd;
+ WERROR werr;
+
+ /* no arguments given, enumerate all printers */
+ if (argc == 0) {
+
+ if (!net_spoolss_enum_printers(pipe_hnd, mem_ctx, NULL,
+ PRINTER_ENUM_LOCAL|PRINTER_ENUM_SHARED,
+ level, num_printers, info_p))
+ return false;
+
+ goto out;
+ }
+
+ /* argument given, get a single printer by name */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, argv[0],
+ MAXIMUM_ALLOWED_ACCESS,
+ &hnd))
+ return false;
+
+ *info_p = talloc_zero(mem_ctx, union spoolss_PrinterInfo);
+ if (*info_p == NULL) {
+ return false;
+ }
+
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, *info_p)) {
+ dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr);
+ return false;
+ }
+
+ dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr);
+
+ *num_printers = 1;
+
+out:
+ DEBUG(3,("got %d printers\n", *num_printers));
+
+ return true;
+
+}
+
+/**
+ * List print-queues (including local printers that are not shared)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i, num_printers;
+ uint32_t level = 2;
+ const char *printername, *sharename;
+ union spoolss_PrinterInfo *info;
+
+ printf("listing printers\n");
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+
+ /* do some initialization */
+ printername = info[i].info2.printername;
+ sharename = info[i].info2.sharename;
+
+ if (printername && sharename) {
+ d_printf(_("printer %d: %s, shared as: %s\n"),
+ i+1, printername, sharename);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * List printer-drivers from a server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_driver_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i;
+ uint32_t level = 3;
+ union spoolss_DriverInfo *info;
+
+ printf(_("listing printer-drivers\n"));
+
+ for (i=0; archi_table[i].long_archi!=NULL; i++) {
+
+ uint32_t d, num_drivers;
+
+ /* enum remote drivers */
+ if (!net_spoolss_enumprinterdrivers(pipe_hnd, mem_ctx, level,
+ archi_table[i].long_archi,
+ &num_drivers, &info)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (num_drivers == 0) {
+ d_printf(_("no drivers found on server for "
+ "architecture: [%s].\n"),
+ archi_table[i].long_archi);
+ continue;
+ }
+
+ d_printf(_("got %d printer-drivers for architecture: [%s]\n"),
+ num_drivers, archi_table[i].long_archi);
+
+
+ /* do something for all drivers for architecture */
+ for (d = 0; d < num_drivers; d++) {
+ display_print_driver3(&info[d].info3);
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ return nt_status;
+
+}
+
+/**
+ * Publish print-queues with args-wrapper
+ *
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ * @param action
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+static NTSTATUS rpc_printer_publish_internals_args(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ uint32_t action)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i, num_printers;
+ uint32_t level = 7;
+ const char *printername, *sharename;
+ union spoolss_PrinterInfo *info_enum;
+ union spoolss_PrinterInfo info;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+ struct spoolss_DevmodeContainer devmode_ctr;
+ struct sec_desc_buf secdesc_ctr;
+ struct policy_handle hnd = { 0, };
+ WERROR result;
+ const char *action_str;
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+ if (!printername || !sharename) {
+ goto done;
+ }
+
+ /* open printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &info))
+ goto done;
+
+ /* check action and set string */
+ switch (action) {
+ case DSPRINT_PUBLISH:
+ action_str = N_("published");
+ break;
+ case DSPRINT_UPDATE:
+ action_str = N_("updated");
+ break;
+ case DSPRINT_UNPUBLISH:
+ action_str = N_("unpublished");
+ break;
+ default:
+ action_str = N_("unknown action");
+ printf(_("unknown action: %d\n"), action);
+ break;
+ }
+
+ info.info7.action = action;
+ info_ctr.level = 7;
+ info_ctr.info.info7 = (struct spoolss_SetPrinterInfo7 *)
+ (void *)&info.info7;
+
+ ZERO_STRUCT(devmode_ctr);
+ ZERO_STRUCT(secdesc_ctr);
+
+ nt_status = dcerpc_spoolss_SetPrinter(b, mem_ctx,
+ &hnd,
+ &info_ctr,
+ &devmode_ctr,
+ &secdesc_ctr,
+ 0, /* command */
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf(_("cannot set printer-info: %s\n"),
+ nt_errstr(nt_status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result) && !W_ERROR_EQUAL(result, WERR_IO_PENDING)) {
+ if ((action == DSPRINT_UPDATE) && W_ERROR_EQUAL(result, W_ERROR(0x80070002))) {
+ printf(_("printer not published yet\n"));
+ } else {
+ printf(_("cannot set printer-info: %s\n"),
+ win_errstr(result));
+ }
+ nt_status = werror_to_ntstatus(result);
+ goto done;
+ }
+
+ printf(_("successfully %s printer %s in Active Directory\n"),
+ action_str, sharename);
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (is_valid_policy_hnd(&hnd)) {
+ dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &result);
+ }
+
+ return nt_status;
+}
+
+NTSTATUS rpc_printer_publish_publish_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_PUBLISH);
+}
+
+NTSTATUS rpc_printer_publish_unpublish_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_UNPUBLISH);
+}
+
+NTSTATUS rpc_printer_publish_update_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_UPDATE);
+}
+
+/**
+ * List print-queues w.r.t. their publishing state
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_publish_list_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i, num_printers;
+ uint32_t level = 7;
+ const char *printername, *sharename;
+ union spoolss_PrinterInfo *info_enum;
+ union spoolss_PrinterInfo info;
+ struct policy_handle hnd = { 0, };
+ int state;
+ WERROR werr;
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+
+ if (!printername || !sharename) {
+ goto done;
+ }
+
+ /* open printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &info))
+ goto done;
+
+ if (!info.info7.guid) {
+ goto done;
+ }
+ state = info.info7.action;
+ switch (state) {
+ case DSPRINT_PUBLISH:
+ printf(_("printer [%s] is published"),
+ sharename);
+ if (c->opt_verbose)
+ printf(_(", guid: %s"),info.info7.guid);
+ printf("\n");
+ break;
+ case DSPRINT_UNPUBLISH:
+ printf(_("printer [%s] is unpublished\n"),
+ sharename);
+ break;
+ case DSPRINT_UPDATE:
+ printf(_("printer [%s] is currently updating\n"),
+ sharename);
+ break;
+ default:
+ printf(_("unknown state: %d\n"), state);
+ break;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (is_valid_policy_hnd(&hnd)) {
+ dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr);
+ }
+
+ return nt_status;
+}
+
+/**
+ * Migrate Printer-ACLs from a source server to the destination server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_migrate_security_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle;
+ /* TODO: what now, info2 or info3 ?
+ convince jerry that we should add clientside setacls level 3 at least
+ */
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i = 0;
+ uint32_t num_printers;
+ uint32_t level = 2;
+ const char *printername, *sharename;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ struct dcerpc_binding_handle *b_dst = NULL;
+ struct policy_handle hnd_src = { 0, };
+ struct policy_handle hnd_dst = { 0, };
+ union spoolss_PrinterInfo *info_enum;
+ struct cli_state *cli_dst = NULL;
+ union spoolss_PrinterInfo info_src, info_dst;
+ WERROR werr;
+
+ DEBUG(3,("copying printer ACLs\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ b_dst = pipe_hnd_dst->binding_handle;
+
+ /* enum source printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!num_printers) {
+ printf (_("no printers found on server.\n"));
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* do something for all printers */
+ for (i = 0; i < num_printers; i++) {
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+
+ if (!printername || !sharename) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* we can reset NT_STATUS here because we do not
+ get any real NT_STATUS-codes anymore from now on */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ d_printf(_("migrating printer ACLs for: [%s] / [%s]\n"),
+ printername, sharename);
+
+ /* according to msdn you have specify these access-rights
+ to see the security descriptor
+ - READ_CONTROL (DACL)
+ - ACCESS_SYSTEM_SECURITY (SACL)
+ */
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS, &hnd_src))
+ goto done;
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd_dst))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst))
+ goto done;
+
+ /* check for existing src printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, 3, &info_src))
+ goto done;
+
+ /* Copy Security Descriptor */
+
+ /* copy secdesc (info level 2) */
+ info_dst.info2.devmode = NULL;
+ if (info_src.info3.secdesc == NULL) {
+ info_dst.info2.secdesc = NULL;
+ } else {
+ info_dst.info2.secdesc
+ = security_descriptor_copy(mem_ctx,
+ info_src.info3.secdesc);
+ if (info_dst.info2.secdesc == NULL) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+
+ if (c->opt_verbose)
+ display_sec_desc(info_dst.info2.secdesc);
+
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst))
+ goto done;
+
+ DEBUGADD(1,("\tSetPrinter of SECDESC succeeded\n"));
+
+
+ /* close printer handles here */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr);
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr);
+ }
+
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+ return nt_status;
+}
+
+/**
+ * Migrate printer-forms from a src server to the dst server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_migrate_forms_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ WERROR result;
+ uint32_t i, f;
+ uint32_t num_printers;
+ uint32_t level = 1;
+ const char *printername, *sharename;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ struct dcerpc_binding_handle *b_dst = NULL;
+ struct policy_handle hnd_src = { 0, };
+ struct policy_handle hnd_dst = { 0, };
+ union spoolss_PrinterInfo *info_enum;
+ union spoolss_PrinterInfo info_dst;
+ uint32_t num_forms;
+ union spoolss_FormInfo *forms;
+ struct cli_state *cli_dst = NULL;
+
+ DEBUG(3,("copying forms\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ b_dst = pipe_hnd_dst->binding_handle;
+
+ /* enum src printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!num_printers) {
+ printf (_("no printers found on server.\n"));
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* do something for all printers */
+ for (i = 0; i < num_printers; i++) {
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+
+ if (!printername || !sharename) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ /* we can reset NT_STATUS here because we do not
+ get any real NT_STATUS-codes anymore from now on */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ d_printf(_("migrating printer forms for: [%s] / [%s]\n"),
+ printername, sharename);
+
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS, &hnd_src))
+ goto done;
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd_dst))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst))
+ goto done;
+
+ /* finally migrate forms */
+ if (!net_spoolss_enumforms(pipe_hnd, mem_ctx, &hnd_src, level, &num_forms, &forms))
+ goto done;
+
+ DEBUG(1,("got %d forms for printer\n", num_forms));
+
+
+ for (f = 0; f < num_forms; f++) {
+
+ struct spoolss_AddFormInfoCtr info_ctr;
+ NTSTATUS status;
+
+ /* only migrate FORM_PRINTER types, according to jerry
+ FORM_BUILTIN-types are hard-coded in samba */
+ if (forms[f].info1.flags != SPOOLSS_FORM_PRINTER)
+ continue;
+
+ if (c->opt_verbose)
+ d_printf(_("\tmigrating form # %d [%s] of type "
+ "[%d]\n"),
+ f, forms[f].info1.form_name,
+ forms[f].info1.flags);
+ info_ctr.level = 1;
+ info_ctr.info.info1 = (struct spoolss_AddFormInfo1 *)
+ (void *)&forms[f].info1;
+
+ /* FIXME: there might be something wrong with samba's
+ builtin-forms */
+ status = dcerpc_spoolss_AddForm(b_dst, mem_ctx,
+ &hnd_dst,
+ &info_ctr,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(_("\tdcerpc_spoolss_AddForm form %d: [%s] - %s\n"),
+ f, forms[f].info1.form_name, nt_errstr(status));
+ continue;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ d_printf(_("\tAddForm form %d: [%s] refused.\n"),
+ f, forms[f].info1.form_name);
+ continue;
+ }
+
+ DEBUGADD(1,("\tAddForm of [%s] succeeded\n",
+ forms[f].info1.form_name));
+ }
+
+
+ /* close printer handles here */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+ return nt_status;
+}
+
+/**
+ * Migrate printer-drivers from a src server to the dst server
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_migrate_drivers_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i, p;
+ uint32_t num_printers;
+ uint32_t level = 3;
+ const char *printername, *sharename;
+ bool got_src_driver_share = false;
+ bool got_dst_driver_share = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ struct dcerpc_binding_handle *b_dst = NULL;
+ struct policy_handle hnd_src = { 0, };
+ struct policy_handle hnd_dst = { 0, };
+ union spoolss_DriverInfo drv_info_src;
+ union spoolss_PrinterInfo *info_enum;
+ union spoolss_PrinterInfo info_dst;
+ struct cli_state *cli_dst = NULL;
+ struct cli_state *cli_share_src = NULL;
+ struct cli_state *cli_share_dst = NULL;
+ const char *drivername = NULL;
+ WERROR werr;
+
+ DEBUG(3,("copying printer-drivers\n"));
+
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ b_dst = pipe_hnd_dst->binding_handle;
+
+ /* open print$-share on the src server */
+ nt_status = connect_to_service(c, &cli_share_src,
+ smbXcli_conn_remote_sockaddr(cli->conn),
+ smbXcli_conn_remote_name(cli->conn),
+ "print$", "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+ got_src_driver_share = true;
+
+
+ /* open print$-share on the dst server */
+ nt_status = connect_to_service(c, &cli_share_dst,
+ smbXcli_conn_remote_sockaddr(cli_dst->conn),
+ smbXcli_conn_remote_name(cli_dst->conn),
+ "print$", "A:");
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ got_dst_driver_share = true;
+
+
+ /* enum src printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (num_printers == 0) {
+ printf (_("no printers found on server.\n"));
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+
+ /* do something for all printers */
+ for (p = 0; p < num_printers; p++) {
+
+ /* do some initialization */
+ printername = info_enum[p].info2.printername;
+ sharename = info_enum[p].info2.sharename;
+
+ if (!printername || !sharename) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* we can reset NT_STATUS here because we do not
+ get any real NT_STATUS-codes anymore from now on */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ d_printf(_("migrating printer driver for: [%s] / [%s]\n"),
+ printername, sharename);
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd_dst))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst))
+ goto done;
+
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS,
+ &hnd_src))
+ goto done;
+
+ /* in a first step call getdriver for each shared printer (per arch)
+ to get a list of all files that have to be copied */
+
+ for (i=0; archi_table[i].long_archi!=NULL; i++) {
+
+ /* getdriver src */
+ if (!net_spoolss_getprinterdriver(pipe_hnd, mem_ctx, &hnd_src,
+ level, archi_table[i].long_archi,
+ archi_table[i].version, &drv_info_src))
+ continue;
+
+ drivername = drv_info_src.info3.driver_name;
+
+ if (c->opt_verbose)
+ display_print_driver3(&drv_info_src.info3);
+
+ /* check arch dir */
+ nt_status = check_arch_dir(cli_share_dst, archi_table[i].short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+
+ /* copy driver-files */
+ nt_status = copy_print_driver_3(c, mem_ctx, cli_share_src, cli_share_dst,
+ archi_table[i].short_archi,
+ &drv_info_src.info3);
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+
+ /* adddriver dst */
+ if (!net_spoolss_addprinterdriver(pipe_hnd_dst, mem_ctx, level, &drv_info_src)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUGADD(1,("Successfully added driver [%s] for printer [%s]\n",
+ drivername, printername));
+
+ }
+
+ if (!drivername || strlen(drivername) == 0) {
+ DEBUGADD(1,("Did not get driver for printer %s\n",
+ printername));
+ goto done;
+ }
+
+ /* setdriver dst */
+ info_dst.info2.drivername = drivername;
+
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUGADD(1,("Successfully set driver %s for printer %s\n",
+ drivername, printername));
+
+ /* close dst */
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr);
+ }
+
+ /* close src */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr);
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr);
+ }
+
+ /* close src */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr);
+ }
+
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+
+ if (got_src_driver_share)
+ cli_shutdown(cli_share_src);
+
+ if (got_dst_driver_share)
+ cli_shutdown(cli_share_dst);
+
+ return nt_status;
+
+}
+
+/**
+ * Migrate printer-queues from a src to the dst server
+ * (requires a working "addprinter command" to be installed for the local smbd)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_migrate_printers_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle;
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i = 0, num_printers;
+ uint32_t level = 2;
+ union spoolss_PrinterInfo info_dst, info_src;
+ union spoolss_PrinterInfo *info_enum;
+ struct cli_state *cli_dst = NULL;
+ struct policy_handle hnd_src = { 0, };
+ struct policy_handle hnd_dst = { 0, };
+ const char *printername, *sharename;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ struct dcerpc_binding_handle *b_dst = NULL;
+ struct spoolss_SetPrinterInfoCtr info_ctr;
+
+ DEBUG(3,("copying printers\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ b_dst = pipe_hnd_dst->binding_handle;
+
+ /* enum printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!num_printers) {
+ printf (_("no printers found on server.\n"));
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+ /* do something for all printers */
+ for (i = 0; i < num_printers; i++) {
+
+ struct spoolss_SetPrinterInfo2 info2;
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+
+ if (!printername || !sharename) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ /* we can reset NT_STATUS here because we do not
+ get any real NT_STATUS-codes anymore from now on */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ d_printf(_("migrating printer queue for: [%s] / [%s]\n"),
+ printername, sharename);
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd_dst)) {
+
+ DEBUG(1,("could not open printer: %s\n", sharename));
+ }
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst)) {
+ printf (_("could not get printer, creating printer.\n"));
+ } else {
+ DEBUG(1,("printer already exists: %s\n", sharename));
+ /* close printer handle here - dst only, not got src yet. */
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+ continue;
+ }
+
+ /* now get again src printer ctr via getprinter,
+ we first need a handle for that */
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS, &hnd_src))
+ goto done;
+
+ /* getprinter on the src server */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, level, &info_src))
+ goto done;
+
+ /* copy each src printer to a dst printer 1:1,
+ maybe some values have to be changed though */
+ d_printf(_("creating printer: %s\n"), printername);
+
+ info_ctr.level = level;
+ spoolss_printerinfo2_to_setprinterinfo2(&info_src.info2, &info2);
+ info_ctr.info.info2 = &info2;
+
+ result = rpccli_spoolss_addprinterex(pipe_hnd_dst,
+ mem_ctx,
+ &info_ctr);
+
+ if (W_ERROR_IS_OK(result))
+ d_printf (_("printer [%s] successfully added.\n"),
+ printername);
+ else if (W_ERROR_V(result) == W_ERROR_V(WERR_PRINTER_ALREADY_EXISTS))
+ d_fprintf (stderr, _("printer [%s] already exists.\n"),
+ printername);
+ else {
+ d_fprintf (stderr, _("could not create printer [%s]\n"),
+ printername);
+ goto done;
+ }
+
+ /* close printer handles here */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+ return nt_status;
+}
+
+/**
+ * Migrate Printer-Settings from a src server to the dst server
+ * (for this to work, printers and drivers already have to be migrated earlier)
+ *
+ * All parameters are provided by the run_rpc_command function, except for
+ * argc, argv which are passed through.
+ *
+ * @param c A net_context structure
+ * @param domain_sid The domain sid acquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destroyed on completion of the function.
+ * @param argc Standard main() style argc
+ * @param argv Standard main() style argv. Initial components are already
+ * stripped
+ *
+ * @return Normal NTSTATUS return.
+ **/
+
+NTSTATUS rpc_printer_migrate_settings_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle;
+
+ /* FIXME: Here the nightmare begins */
+
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32_t i = 0, j = 0;
+ uint32_t num_printers;
+ uint32_t level = 2;
+ const char *printername, *sharename;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ struct dcerpc_binding_handle *b_dst = NULL;
+ struct policy_handle hnd_src = { 0, };
+ struct policy_handle hnd_dst = { 0, };
+ union spoolss_PrinterInfo *info_enum;
+ union spoolss_PrinterInfo info_dst_publish;
+ union spoolss_PrinterInfo info_dst;
+ struct cli_state *cli_dst = NULL;
+ const char *longname;
+ const char **keylist = NULL;
+
+ /* FIXME GD */
+ ZERO_STRUCT(info_dst_publish);
+
+ DEBUG(3,("copying printer settings\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &ndr_table_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+ b_dst = pipe_hnd_dst->binding_handle;
+
+ /* enum src printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (!num_printers) {
+ printf (_("no printers found on server.\n"));
+ nt_status = NT_STATUS_OK;
+ goto done;
+ }
+
+
+ /* needed for dns-strings in regkeys */
+ longname = get_mydnsfullname();
+ if (!longname) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ /* do something for all printers */
+ for (i = 0; i < num_printers; i++) {
+
+ uint32_t value_needed;
+ uint32_t data_needed;
+ enum winreg_Type type;
+ struct spoolss_EnumPrinterData r;
+
+ /* do some initialization */
+ printername = info_enum[i].info2.printername;
+ sharename = info_enum[i].info2.sharename;
+
+ if (!printername || !sharename) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ /* we can reset NT_STATUS here because we do not
+ get any real NT_STATUS-codes anymore from now on */
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+
+ d_printf(_("migrating printer settings for: [%s] / [%s]\n"),
+ printername, sharename);
+
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS, &hnd_src))
+ goto done;
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, &hnd_dst))
+ goto done;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ level, &info_dst))
+ goto done;
+
+
+ /* STEP 1: COPY DEVICE-MODE and other
+ PRINTER_INFO_2-attributes
+ */
+
+ info_dst.info2 = info_enum[i].info2;
+
+ /* why is the port always disconnected when the printer
+ is correctly installed (incl. driver ???) */
+ info_dst.info2.portname = SAMBA_PRINTER_PORT_NAME;
+
+ /* check if printer is published */
+ if (info_enum[i].info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &info_dst_publish))
+ goto done;
+
+ info_dst_publish.info7.action = DSPRINT_PUBLISH;
+
+ /* ignore false from setprinter due to WERR_IO_PENDING */
+ net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &info_dst_publish);
+
+ DEBUG(3,("republished printer\n"));
+ }
+
+ if (info_enum[i].info2.devmode != NULL) {
+
+ /* copy devmode (info level 2) */
+ info_dst.info2.devmode = info_enum[i].info2.devmode;
+
+ /* do not copy security descriptor (we have another
+ * command for that) */
+ info_dst.info2.secdesc = NULL;
+
+#if 0
+ info_dst.info2.devmode.devicename =
+ talloc_asprintf(mem_ctx, "\\\\%s\\%s",
+ longname, printername);
+ if (!info_dst.info2.devmode.devicename) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+#endif
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ level, &info_dst))
+ goto done;
+
+ DEBUGADD(1,("\tSetPrinter of DEVICEMODE succeeded\n"));
+ }
+
+ /* STEP 2: COPY REGISTRY VALUES */
+
+ /* please keep in mind that samba parse_spools gives horribly
+ crippled results when used to rpccli_spoolss_enumprinterdataex
+ a win2k3-server. (Bugzilla #1851)
+ FIXME: IIRC I've seen it too on a win2k-server
+ */
+
+ r.in.handle = &hnd_src;
+ r.in.enum_index = 0;
+ r.in.value_offered = 0;
+ r.in.data_offered = 0;
+ r.out.value_name = NULL;
+ r.out.value_needed = &value_needed;
+ r.out.type = &type;
+ r.out.data = NULL;
+ r.out.data_needed = &data_needed;
+
+ /* enumerate data on src handle */
+ nt_status = dcerpc_spoolss_EnumPrinterData_r(b_src, mem_ctx, &r);
+
+ r.in.data_offered = *r.out.data_needed;
+ r.in.value_offered = *r.out.value_needed;
+ r.out.data = talloc_zero_array(mem_ctx, uint8_t, r.in.data_offered);
+ r.out.value_name = talloc_zero_array(mem_ctx, char, r.in.value_offered);
+
+ /* loop for all printerdata of "PrinterDriverData" */
+ while (NT_STATUS_IS_OK(nt_status) && W_ERROR_IS_OK(r.out.result)) {
+
+ r.in.enum_index++;
+
+ nt_status = dcerpc_spoolss_EnumPrinterData_r(b_src, mem_ctx, &r);
+
+ /* loop for all reg_keys */
+ if (NT_STATUS_IS_OK(nt_status) && W_ERROR_IS_OK(r.out.result)) {
+
+ /* display_value */
+ if (c->opt_verbose) {
+ struct registry_value v;
+ v.type = *r.out.type;
+ v.data = data_blob_const(
+ r.out.data, r.in.data_offered);
+
+ display_reg_value(SPOOL_PRINTERDATA_KEY,
+ r.out.value_name, &v);
+ }
+
+ /* set_value */
+ if (!net_spoolss_setprinterdata(pipe_hnd_dst, mem_ctx,
+ &hnd_dst, r.out.value_name,
+ *r.out.type, r.out.data, r.in.data_offered))
+ goto done;
+
+ DEBUGADD(1,("\tSetPrinterData of [%s] succeeded\n",
+ r.out.value_name));
+ }
+ }
+
+ /* STEP 3: COPY SUBKEY VALUES */
+
+ /* here we need to enum all printer_keys and then work
+ on the result with enum_printer_key_ex. nt4 does not
+ respond to enumprinterkey, win2k does, so continue
+ in case of an error */
+
+ if (!net_spoolss_enumprinterkey(pipe_hnd, mem_ctx, &hnd_src, "", &keylist)) {
+ printf(_("got no key-data\n"));
+ continue;
+ }
+
+
+ /* work on a list of printer keys
+ each key has to be enumerated to get all required
+ information. information is then set via setprinterdataex-calls */
+
+ if (keylist == NULL)
+ continue;
+
+ for (i=0; keylist && keylist[i] != NULL; i++) {
+
+ const char *subkey = keylist[i];
+ uint32_t count;
+ struct spoolss_PrinterEnumValues *info;
+
+ /* enumerate all src subkeys */
+ if (!net_spoolss_enumprinterdataex(pipe_hnd, mem_ctx, 0,
+ &hnd_src, subkey,
+ &count, &info)) {
+ goto done;
+ }
+
+ for (j=0; j < count; j++) {
+
+ struct registry_value value;
+ const char *value_name = info[j].value_name;
+ bool ok;
+
+ value.type = REG_SZ;
+
+ /* although samba replies with sane data in most cases we
+ should try to avoid writing wrong registry data */
+
+ if (strequal(value_name, SPOOL_REG_PORTNAME)) {
+ /* although windows uses a multi-sz, we use a sz */
+ ok = push_reg_sz(mem_ctx, &value.data, SAMBA_PRINTER_PORT_NAME);
+ if (!ok) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ else if (strequal(value_name, SPOOL_REG_UNCNAME)) {
+ char *unc_name;
+ if (asprintf(&unc_name, "\\\\%s\\%s", longname, sharename) < 0) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ ok = push_reg_sz(mem_ctx, &value.data, unc_name);
+ if (!ok) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ free(unc_name);
+ }
+ else if (strequal(value_name, SPOOL_REG_URL)) {
+ continue;
+#if 0
+ /* FIXME: should we really do that ??? */
+ if (asprintf(&url, "http://%s:631/printers/%s", longname, sharename) < 0) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ push_reg_sz(mem_ctx, NULL, &value.data, url);
+ free(url);
+#endif
+ }
+ else if (strequal(value_name, SPOOL_REG_SERVERNAME)) {
+ ok = push_reg_sz(mem_ctx, &value.data, longname);
+ if (!ok) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ else if (strequal(value_name, SPOOL_REG_SHORTSERVERNAME)) {
+ ok = push_reg_sz(mem_ctx, &value.data, lp_netbios_name());
+ if (!ok) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ }
+ else {
+ value.type = info[j].type;
+ value.data = *info[j].data;
+ }
+
+ if (c->opt_verbose) {
+ display_reg_value(subkey, value_name, &value);
+ }
+
+ /* here we have to set all subkeys on the dst server */
+ if (!net_spoolss_setprinterdataex(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ subkey, value_name, &value))
+ {
+ goto done;
+ }
+
+ DEBUGADD(1,("\tSetPrinterDataEx of key [%s\\%s] succeeded\n",
+ subkey, info[j].value_name));
+
+ }
+ }
+
+ TALLOC_FREE(keylist);
+
+ /* close printer handles here */
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (is_valid_policy_hnd(&hnd_src)) {
+ dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result);
+ }
+
+ if (is_valid_policy_hnd(&hnd_dst)) {
+ dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result);
+ }
+
+ if (cli_dst) {
+ cli_shutdown(cli_dst);
+ }
+ return nt_status;
+}
diff --git a/source3/utils/net_rpc_registry.c b/source3/utils/net_rpc_registry.c
new file mode 100644
index 0000000..cec5459
--- /dev/null
+++ b/source3/utils/net_rpc_registry.c
@@ -0,0 +1,2126 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) Gerald (Jerry) Carter 2005-2006
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "rpc_client/rpc_client.h"
+#include "registry.h"
+#include "utils/net.h"
+#include "utils/net_registry_util.h"
+#include "registry/regfio.h"
+#include "../librpc/gen_ndr/ndr_winreg_c.h"
+#include "../librpc/gen_ndr/ndr_security.h"
+#include "registry/reg_format.h"
+#include "registry/reg_import.h"
+#include <assert.h>
+#include "../libcli/security/display_sec.h"
+#include "../libcli/registry/util_reg.h"
+#include "client.h"
+#include "lib/util/smb_strtox.h"
+
+
+/*******************************************************************
+ connect to a registry hive root (open a registry policy)
+*******************************************************************/
+
+static NTSTATUS dcerpc_winreg_Connect(struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx,
+ uint32_t reg_type, uint32_t access_mask,
+ struct policy_handle *reg_hnd, WERROR *werr)
+{
+ ZERO_STRUCTP(reg_hnd);
+
+ switch (reg_type)
+ {
+ case HKEY_CLASSES_ROOT:
+ return dcerpc_winreg_OpenHKCR(b, mem_ctx, NULL,
+ access_mask, reg_hnd, werr);
+
+ case HKEY_LOCAL_MACHINE:
+ return dcerpc_winreg_OpenHKLM(b, mem_ctx, NULL,
+ access_mask, reg_hnd, werr);
+
+ case HKEY_USERS:
+ return dcerpc_winreg_OpenHKU(b, mem_ctx, NULL,
+ access_mask, reg_hnd, werr);
+
+ case HKEY_CURRENT_USER:
+ return dcerpc_winreg_OpenHKCU(b, mem_ctx, NULL,
+ access_mask, reg_hnd, werr);
+
+ case HKEY_PERFORMANCE_DATA:
+ return dcerpc_winreg_OpenHKPD(b, mem_ctx, NULL,
+ access_mask, reg_hnd, werr);
+
+ default:
+ /* fall through to end of function */
+ break;
+ }
+
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+static bool reg_hive_key(TALLOC_CTX *ctx, const char *fullname,
+ uint32_t *reg_type, const char **key_name)
+{
+ WERROR werr;
+ char *hivename = NULL;
+ char *tmp_keyname = NULL;
+ bool ret = false;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ werr = split_hive_key(tmp_ctx, fullname, &hivename, &tmp_keyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ *key_name = talloc_strdup(ctx, tmp_keyname);
+ if (*key_name == NULL) {
+ goto done;
+ }
+
+ if (strequal(hivename, "HKLM") ||
+ strequal(hivename, "HKEY_LOCAL_MACHINE"))
+ {
+ (*reg_type) = HKEY_LOCAL_MACHINE;
+ } else if (strequal(hivename, "HKCR") ||
+ strequal(hivename, "HKEY_CLASSES_ROOT"))
+ {
+ (*reg_type) = HKEY_CLASSES_ROOT;
+ } else if (strequal(hivename, "HKU") ||
+ strequal(hivename, "HKEY_USERS"))
+ {
+ (*reg_type) = HKEY_USERS;
+ } else if (strequal(hivename, "HKCU") ||
+ strequal(hivename, "HKEY_CURRENT_USER"))
+ {
+ (*reg_type) = HKEY_CURRENT_USER;
+ } else if (strequal(hivename, "HKPD") ||
+ strequal(hivename, "HKEY_PERFORMANCE_DATA"))
+ {
+ (*reg_type) = HKEY_PERFORMANCE_DATA;
+ } else {
+ DEBUG(10,("reg_hive_key: unrecognised hive key %s\n",
+ fullname));
+ goto done;
+ }
+
+ ret = true;
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return ret;
+}
+
+static NTSTATUS registry_openkey(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ const char *name, uint32_t access_mask,
+ struct policy_handle *hive_hnd,
+ struct policy_handle *key_hnd)
+{
+ uint32_t hive;
+ NTSTATUS status;
+ WERROR werr;
+ struct winreg_String key;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(key);
+
+ if (!reg_hive_key(mem_ctx, name, &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_winreg_Connect(b, mem_ctx, hive, access_mask,
+ hive_hnd, &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+
+ status = dcerpc_winreg_OpenKey(b, mem_ctx, hive_hnd, key, 0,
+ access_mask, key_hnd, &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ dcerpc_winreg_CloseKey(b, mem_ctx, hive_hnd, &werr);
+ return status;
+ }
+ if (!(W_ERROR_IS_OK(werr))) {
+ WERROR _werr;
+ dcerpc_winreg_CloseKey(b, mem_ctx, hive_hnd, &_werr);
+ return werror_to_ntstatus(werr);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS registry_enumkeys(TALLOC_CTX *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_keys, char ***pnames,
+ char ***pclasses, NTTIME ***pmodtimes)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t num_subkeys, max_subkeylen, max_classlen;
+ uint32_t num_values, max_valnamelen, max_valbufsize;
+ uint32_t i;
+ NTTIME last_changed_time;
+ uint32_t secdescsize;
+ struct winreg_String classname;
+ char **names, **classes;
+ NTTIME **modtimes;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (!(mem_ctx = talloc_new(ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+ status = dcerpc_winreg_QueryInfoKey(
+ b, mem_ctx, key_hnd, &classname, &num_subkeys,
+ &max_subkeylen, &max_classlen, &num_values, &max_valnamelen,
+ &max_valbufsize, &secdescsize, &last_changed_time, &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto error;
+ }
+
+ if (num_subkeys == 0) {
+ *pnum_keys = 0;
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ if ((!(names = talloc_zero_array(mem_ctx, char *, num_subkeys))) ||
+ (!(classes = talloc_zero_array(mem_ctx, char *, num_subkeys))) ||
+ (!(modtimes = talloc_zero_array(mem_ctx, NTTIME *,
+ num_subkeys)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ for (i=0; i<num_subkeys; i++) {
+ char c, n;
+ struct winreg_StringBuf class_buf;
+ struct winreg_StringBuf name_buf;
+ NTTIME modtime;
+
+ c = '\0';
+ class_buf.name = &c;
+ class_buf.size = max_classlen+2;
+
+ n = '\0';
+ name_buf.name = &n;
+ name_buf.size = max_subkeylen+2;
+
+ ZERO_STRUCT(modtime);
+
+ status = dcerpc_winreg_EnumKey(b, mem_ctx, key_hnd,
+ i, &name_buf, &class_buf,
+ &modtime, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (W_ERROR_EQUAL(werr,
+ WERR_NO_MORE_ITEMS) ) {
+ status = NT_STATUS_OK;
+ break;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto error;
+ }
+
+ classes[i] = NULL;
+
+ if (class_buf.name &&
+ (!(classes[i] = talloc_strdup(classes, class_buf.name)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ if (!(names[i] = talloc_strdup(names, name_buf.name))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ if ((!(modtimes[i] = (NTTIME *)talloc_memdup(
+ modtimes, &modtime, sizeof(modtime))))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+ }
+
+ *pnum_keys = num_subkeys;
+
+ if (pnames) {
+ *pnames = talloc_move(ctx, &names);
+ }
+ if (pclasses) {
+ *pclasses = talloc_move(ctx, &classes);
+ }
+ if (pmodtimes) {
+ *pmodtimes = talloc_move(ctx, &modtimes);
+ }
+
+ status = NT_STATUS_OK;
+
+ error:
+ TALLOC_FREE(mem_ctx);
+ return status;
+}
+
+static NTSTATUS registry_enumvalues(TALLOC_CTX *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_values, char ***pvalnames,
+ struct registry_value ***pvalues)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t num_subkeys, max_subkeylen, max_classlen;
+ uint32_t num_values, max_valnamelen, max_valbufsize;
+ uint32_t i;
+ NTTIME last_changed_time;
+ uint32_t secdescsize;
+ struct winreg_String classname;
+ struct registry_value **values;
+ char **names;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (!(mem_ctx = talloc_new(ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+ status = dcerpc_winreg_QueryInfoKey(
+ b, mem_ctx, key_hnd, &classname, &num_subkeys,
+ &max_subkeylen, &max_classlen, &num_values, &max_valnamelen,
+ &max_valbufsize, &secdescsize, &last_changed_time, &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto error;
+ }
+
+ if (num_values == 0) {
+ *pnum_values = 0;
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ if ((!(names = talloc_array(mem_ctx, char *, num_values))) ||
+ (!(values = talloc_array(mem_ctx, struct registry_value *,
+ num_values)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ for (i=0; i<num_values; i++) {
+ enum winreg_Type type = REG_NONE;
+ uint8_t *data = NULL;
+ uint32_t data_size;
+ uint32_t value_length;
+
+ char n;
+ struct winreg_ValNameBuf name_buf;
+ WERROR err;
+
+ n = '\0';
+ name_buf.name = &n;
+ name_buf.size = max_valnamelen + 2;
+
+ data_size = max_valbufsize;
+ data = (uint8_t *)TALLOC(mem_ctx, data_size);
+ value_length = 0;
+
+ status = dcerpc_winreg_EnumValue(b, mem_ctx, key_hnd,
+ i, &name_buf, &type,
+ data, &data_size,
+ &value_length, &err);
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if ( W_ERROR_EQUAL(err,
+ WERR_NO_MORE_ITEMS) ) {
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(err)) {
+ status = werror_to_ntstatus(err);
+ goto error;
+ }
+
+ if (name_buf.name == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!(names[i] = talloc_strdup(names, name_buf.name))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ values[i] = talloc_zero(values, struct registry_value);
+ if (values[i] == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ values[i]->type = type;
+ values[i]->data = data_blob_talloc(values[i], data, data_size);
+ }
+
+ *pnum_values = num_values;
+
+ if (pvalnames) {
+ *pvalnames = talloc_move(ctx, &names);
+ }
+ if (pvalues) {
+ *pvalues = talloc_move(ctx, &values);
+ }
+
+ status = NT_STATUS_OK;
+
+ error:
+ TALLOC_FREE(mem_ctx);
+ return status;
+}
+
+static NTSTATUS registry_enumvalues2(TALLOC_CTX *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ uint32_t *pnum_values, char ***pvalnames,
+ struct regval_blob ***pvalues)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t num_subkeys, max_subkeylen, max_classlen;
+ uint32_t num_values, max_valnamelen, max_valbufsize;
+ uint32_t i;
+ NTTIME last_changed_time;
+ uint32_t secdescsize;
+ struct winreg_String classname;
+ struct regval_blob **values;
+ char **names;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (!(mem_ctx = talloc_new(ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+ status = dcerpc_winreg_QueryInfoKey(
+ b, mem_ctx, key_hnd, &classname, &num_subkeys,
+ &max_subkeylen, &max_classlen, &num_values, &max_valnamelen,
+ &max_valbufsize, &secdescsize, &last_changed_time, &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto error;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto error;
+ }
+
+ if (num_values == 0) {
+ *pnum_values = 0;
+ TALLOC_FREE(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ if ((!(names = talloc_array(mem_ctx, char *, num_values))) ||
+ (!(values = talloc_array(mem_ctx, struct regval_blob *,
+ num_values)))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ for (i=0; i<num_values; i++) {
+ enum winreg_Type type = REG_NONE;
+ uint8_t *data = NULL;
+ uint32_t data_size;
+ uint32_t value_length;
+
+ char n;
+ struct winreg_ValNameBuf name_buf;
+ WERROR err;
+
+ n = '\0';
+ name_buf.name = &n;
+ name_buf.size = max_valnamelen + 2;
+
+ data_size = max_valbufsize;
+ data = (uint8_t *)TALLOC(mem_ctx, data_size);
+ value_length = 0;
+
+ status = dcerpc_winreg_EnumValue(b, mem_ctx, key_hnd,
+ i, &name_buf, &type,
+ data, &data_size,
+ &value_length, &err);
+ if (!(NT_STATUS_IS_OK(status))) {
+ goto error;
+ }
+
+ if ( W_ERROR_EQUAL(err, WERR_NO_MORE_ITEMS) ) {
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ if (!W_ERROR_IS_OK(err)) {
+ status = werror_to_ntstatus(err);
+ goto error;
+ }
+
+ if (name_buf.name == NULL) {
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto error;
+ }
+
+ if (!(names[i] = talloc_strdup(names, name_buf.name))) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+
+ assert(value_length<=data_size); /*??? */
+
+ values[i] = regval_compose(values,
+ name_buf.name,
+ type,
+ data, value_length);
+ if (!values[i]) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+ }
+
+ *pnum_values = num_values;
+
+ if (pvalnames) {
+ *pvalnames = talloc_move(ctx, &names);
+ }
+ if (pvalues) {
+ *pvalues = talloc_move(ctx, &values);
+ }
+
+ status = NT_STATUS_OK;
+
+ error:
+ TALLOC_FREE(mem_ctx);
+ return status;
+}
+
+static NTSTATUS registry_getsd(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *b,
+ struct policy_handle *key_hnd,
+ uint32_t sec_info,
+ struct KeySecurityData *sd,
+ WERROR *werr)
+{
+ return dcerpc_winreg_GetKeySecurity(b, mem_ctx, key_hnd,
+ sec_info, sd, werr);
+}
+
+
+static NTSTATUS registry_setvalue(TALLOC_CTX *mem_ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ const char *name,
+ const struct registry_value *value)
+{
+ struct winreg_String name_string;
+ NTSTATUS result;
+ WERROR werr;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(name_string);
+
+ name_string.name = name;
+ result = dcerpc_winreg_SetValue(b, mem_ctx, key_hnd,
+ name_string, value->type,
+ value->data.data, value->data.length, &werr);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ return werror_to_ntstatus(werr);
+}
+
+static NTSTATUS rpc_registry_setvalue_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hive_hnd, key_hnd;
+ NTSTATUS status;
+ WERROR werr;
+ struct registry_value value;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0],
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive_hnd, &key_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ if (!strequal(argv[2], "multi_sz") && (argc != 4)) {
+ d_fprintf(stderr, _("Too many args for type %s\n"), argv[2]);
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ if (strequal(argv[2], "dword")) {
+ int error = 0;
+ uint32_t v;
+
+ v = smb_strtoul(argv[3], NULL, 10, &error, SMB_STR_STANDARD);
+ if (error != 0) {
+ goto error;
+ }
+
+ value.type = REG_DWORD;
+ value.data = data_blob_talloc(mem_ctx, NULL, 4);
+ SIVAL(value.data.data, 0, v);
+ }
+ else if (strequal(argv[2], "sz")) {
+ value.type = REG_SZ;
+ if (!push_reg_sz(mem_ctx, &value.data, argv[3])) {
+ status = NT_STATUS_NO_MEMORY;
+ goto error;
+ }
+ }
+ else {
+ d_fprintf(stderr, _("type \"%s\" not implemented\n"), argv[2]);
+ status = NT_STATUS_NOT_IMPLEMENTED;
+ goto error;
+ }
+
+ status = registry_setvalue(mem_ctx, pipe_hnd, &key_hnd,
+ argv[1], &value);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_setvalue failed: %s\n"),
+ nt_errstr(status));
+ }
+
+ error:
+ dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr);
+
+ return NT_STATUS_OK;
+}
+
+static int rpc_registry_setvalue(struct net_context *c, int argc,
+ const char **argv )
+{
+ if (argc < 4 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry setvalue <key> <valuename> "
+ "<type> [<val>]+\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_setvalue_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_deletevalue_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hive_hnd, key_hnd;
+ NTSTATUS status;
+ WERROR werr;
+ struct winreg_String valuename;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(valuename);
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0],
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive_hnd, &key_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ valuename.name = argv[1];
+
+ status = dcerpc_winreg_DeleteValue(b, mem_ctx, &key_hnd,
+ valuename, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_deletevalue failed: %s\n"),
+ nt_errstr(status));
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ d_fprintf(stderr, _("registry_deletevalue failed: %s\n"),
+ win_errstr(werr));
+ }
+
+ dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr);
+
+ return status;
+}
+
+static int rpc_registry_deletevalue(struct net_context *c, int argc,
+ const char **argv )
+{
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry deletevalue <key> <valuename>\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_deletevalue_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_getvalue_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ bool raw,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle hive_hnd, key_hnd;
+ NTSTATUS status;
+ WERROR werr;
+ struct winreg_String valuename;
+ struct registry_value *value = NULL;
+ enum winreg_Type type = REG_NONE;
+ uint32_t data_size = 0;
+ uint32_t value_length = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(valuename);
+
+ status = registry_openkey(tmp_ctx, pipe_hnd, argv[0],
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive_hnd, &key_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ valuename.name = argv[1];
+
+ value = talloc_zero(tmp_ctx, struct registry_value);
+ if (value == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * call QueryValue once with data == NULL to get the
+ * needed memory size to be allocated, then allocate
+ * data buffer and call again.
+ */
+ status = dcerpc_winreg_QueryValue(b, tmp_ctx, &key_hnd,
+ &valuename,
+ &type,
+ NULL,
+ &data_size,
+ &value_length,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_queryvalue failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ d_fprintf(stderr, _("registry_queryvalue failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ value->data = data_blob_talloc(tmp_ctx, NULL, data_size);
+
+ status = dcerpc_winreg_QueryValue(b, tmp_ctx, &key_hnd,
+ &valuename,
+ &type,
+ value->data.data,
+ &data_size,
+ &value_length,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_queryvalue failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ d_fprintf(stderr, _("registry_queryvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+
+ value->type = type;
+
+ print_registry_value(value, raw);
+
+done:
+ dcerpc_winreg_CloseKey(b, tmp_ctx, &key_hnd, &werr);
+ dcerpc_winreg_CloseKey(b, tmp_ctx, &hive_hnd, &werr);
+
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+static NTSTATUS rpc_registry_getvalue_full(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_registry_getvalue_internal(c, domain_sid, domain_name,
+ cli, pipe_hnd, mem_ctx, false,
+ argc, argv);
+}
+
+static int rpc_registry_getvalue(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry getvalue <key> <valuename>\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_getvalue_full, argc, argv);
+}
+
+static NTSTATUS rpc_registry_getvalue_raw(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ return rpc_registry_getvalue_internal(c, domain_sid, domain_name,
+ cli, pipe_hnd, mem_ctx, true,
+ argc, argv);
+}
+
+static int rpc_registry_getvalueraw(struct net_context *c, int argc,
+ const char **argv)
+{
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry getvalue <key> <valuename>\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_getvalue_raw, argc, argv);
+}
+
+static NTSTATUS rpc_registry_createkey_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ uint32_t hive;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String key, keyclass;
+ enum winreg_CreateAction action;
+ NTSTATUS status;
+ WERROR werr;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(key);
+ ZERO_STRUCT(keyclass);
+
+ if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_winreg_Connect(b, mem_ctx, hive,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive_hnd, &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+
+ action = REG_ACTION_NONE;
+ keyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(b, mem_ctx, &hive_hnd, key,
+ keyclass, 0, REG_KEY_READ, NULL,
+ &key_hnd, &action, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("createkey returned %s\n"),
+ nt_errstr(status));
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr);
+ return status;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ WERROR _werr;
+ d_fprintf(stderr, _("createkey returned %s\n"),
+ win_errstr(werr));
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &_werr);
+ return werror_to_ntstatus(werr);
+ }
+
+ switch (action) {
+ case REG_ACTION_NONE:
+ d_printf(_("createkey did nothing -- huh?\n"));
+ break;
+ case REG_CREATED_NEW_KEY:
+ d_printf(_("createkey created %s\n"), argv[0]);
+ break;
+ case REG_OPENED_EXISTING_KEY:
+ d_printf(_("createkey opened existing %s\n"), argv[0]);
+ break;
+ }
+
+ dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr);
+
+ return status;
+}
+
+static int rpc_registry_createkey(struct net_context *c, int argc,
+ const char **argv )
+{
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry createkey <key>\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_createkey_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_deletekey_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ uint32_t hive;
+ struct policy_handle hive_hnd;
+ struct winreg_String key;
+ NTSTATUS status;
+ WERROR werr;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(key);
+
+ if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = dcerpc_winreg_Connect(b, mem_ctx, hive,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive_hnd, &werr);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ return werror_to_ntstatus(werr);
+ }
+
+ status = dcerpc_winreg_DeleteKey(b, mem_ctx, &hive_hnd, key, &werr);
+ if (is_valid_policy_hnd(&hive_hnd)) {
+ WERROR _werr;
+ dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &_werr);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("deletekey returned %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("deletekey returned %s\n"),
+ win_errstr(werr));
+ return werror_to_ntstatus(werr);
+ }
+
+ return status;
+}
+
+static int rpc_registry_deletekey(struct net_context *c, int argc, const char **argv )
+{
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net rpc registry deletekey <key>\n"));
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_deletekey_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_registry_enumerate_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle pol_hive, pol_key;
+ NTSTATUS status;
+ WERROR werr;
+ uint32_t num_subkeys = 0;
+ uint32_t num_values = 0;
+ char **names = NULL, **classes = NULL;
+ NTTIME **modtimes = NULL;
+ uint32_t i;
+ struct registry_value **values = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry enumerate <path>\n"));
+ d_printf("%s net rpc registry enumerate "
+ "'HKLM\\Software\\Samba'\n", _("Example:"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_READ,
+ &pol_hive, &pol_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ status = registry_enumkeys(mem_ctx, pipe_hnd, &pol_key, &num_subkeys,
+ &names, &classes, &modtimes);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("enumerating keys failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ for (i=0; i<num_subkeys; i++) {
+ print_registry_key(names[i], modtimes[i]);
+ }
+
+ status = registry_enumvalues(mem_ctx, pipe_hnd, &pol_key, &num_values,
+ &names, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("enumerating values failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ for (i=0; i<num_values; i++) {
+ print_registry_value_with_name(names[i], values[i]);
+ }
+
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_registry_enumerate(struct net_context *c, int argc,
+ const char **argv )
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_enumerate_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_registry_save_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ WERROR result = WERR_GEN_FAILURE;
+ struct policy_handle pol_hive, pol_key;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct winreg_String filename;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry backup <path> <file> \n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_ALL,
+ &pol_hive, &pol_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ filename.name = argv[1];
+ status = dcerpc_winreg_SaveKey(b, mem_ctx, &pol_key, &filename, NULL, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Unable to save [%s] to %s:%s\n"), argv[0],
+ pipe_hnd->desthost, argv[1]);
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ status = werror_to_ntstatus(result);
+ d_fprintf(stderr, _("Unable to save [%s] to %s:%s\n"), argv[0],
+ pipe_hnd->desthost, argv[1]);
+ }
+
+ /* cleanup */
+
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &result);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &result);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_registry_save(struct net_context *c, int argc, const char **argv )
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_save_internal, argc, argv );
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static void dump_values( REGF_NK_REC *nk )
+{
+ int i, j;
+ const char *data_str = NULL;
+ uint32_t data_size, data;
+ DATA_BLOB blob;
+
+ if ( !nk->values )
+ return;
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ d_printf( "\"%s\" = ", nk->values[i].valuename ? nk->values[i].valuename : "(default)" );
+ d_printf( "(%s) ", str_regtype( nk->values[i].type ) );
+
+ data_size = nk->values[i].data_size & ~VK_DATA_IN_OFFSET;
+ switch ( nk->values[i].type ) {
+ case REG_SZ:
+ blob = data_blob_const(nk->values[i].data, data_size);
+ if (!pull_reg_sz(talloc_tos(), &blob,
+ &data_str)) {
+ data_str = NULL;
+ }
+ if (!data_str) {
+ break;
+ }
+ d_printf( "%s", data_str );
+ break;
+ case REG_MULTI_SZ:
+ case REG_EXPAND_SZ:
+ for ( j=0; j<data_size; j++ ) {
+ d_printf( "%c", nk->values[i].data[j] );
+ }
+ break;
+ case REG_DWORD:
+ data = IVAL( nk->values[i].data, 0 );
+ d_printf("0x%x", data );
+ break;
+ case REG_BINARY:
+ for ( j=0; j<data_size; j++ ) {
+ d_printf( "%x", nk->values[i].data[j] );
+ }
+ break;
+ default:
+ d_printf(_("unknown"));
+ break;
+ }
+
+ d_printf( "\n" );
+ }
+
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool dump_registry_tree( REGF_FILE *file, REGF_NK_REC *nk, const char *parent )
+{
+ REGF_NK_REC *key;
+
+ /* depth first dump of the registry tree */
+
+ while ( (key = regfio_fetch_subkey( file, nk )) ) {
+ char *regpath;
+ if (asprintf(&regpath, "%s\\%s", parent, key->keyname) < 0) {
+ break;
+ }
+ d_printf("[%s]\n", regpath );
+ dump_values( key );
+ d_printf("\n");
+ dump_registry_tree( file, key, regpath );
+ SAFE_FREE(regpath);
+ }
+
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool write_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk,
+ REGF_NK_REC *parent, REGF_FILE *outfile,
+ const char *parentpath )
+{
+ REGF_NK_REC *key, *subkey;
+ struct regval_ctr *values = NULL;
+ struct regsubkey_ctr *subkeys = NULL;
+ int i;
+ char *path = NULL;
+ WERROR werr;
+
+ werr = regsubkey_ctr_init(infile->mem_ctx, &subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0, ("write_registry_tree: regsubkey_ctr_init failed: "
+ "%s\n", win_errstr(werr)));
+ return false;
+ }
+
+ werr = regval_ctr_init(subkeys, &values);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("write_registry_tree: talloc() failed!\n"));
+ TALLOC_FREE(subkeys);
+ return false;
+ }
+
+ /* copy values into the struct regval_ctr */
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type,
+ nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) );
+ }
+
+ /* copy subkeys into the struct regsubkey_ctr */
+
+ while ( (subkey = regfio_fetch_subkey( infile, nk )) ) {
+ regsubkey_ctr_addkey( subkeys, subkey->keyname );
+ }
+
+ key = regfio_write_key( outfile, nk->keyname, values, subkeys, nk->sec_desc->sec_desc, parent );
+
+ /* write each one of the subkeys out */
+
+ path = talloc_asprintf(subkeys,
+ "%s%s%s",
+ parentpath,
+ parent ? "\\" : "",
+ nk->keyname);
+ if (!path) {
+ TALLOC_FREE(subkeys);
+ return false;
+ }
+
+ nk->subkey_index = 0;
+ while ( (subkey = regfio_fetch_subkey( infile, nk )) ) {
+ write_registry_tree( infile, subkey, key, outfile, path );
+ }
+
+ d_printf("[%s]\n", path );
+ TALLOC_FREE(subkeys);
+
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_registry_dump(struct net_context *c, int argc, const char **argv)
+{
+ REGF_FILE *registry;
+ REGF_NK_REC *nk;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry dump <file> \n"));
+ return -1;
+ }
+
+ d_printf(_("Opening %s...."), argv[0]);
+ if ( !(registry = regfio_open( argv[0], O_RDONLY, 0)) ) {
+ d_fprintf(stderr, _("Failed to open %s for reading\n"),argv[0]);
+ return 1;
+ }
+ d_printf(_("ok\n"));
+
+ /* get the root of the registry file */
+
+ if ((nk = regfio_rootkey( registry )) == NULL) {
+ d_fprintf(stderr, _("Could not get rootkey\n"));
+ regfio_close( registry );
+ return 1;
+ }
+ d_printf("[%s]\n", nk->keyname);
+ dump_values( nk );
+ d_printf("\n");
+
+ dump_registry_tree( registry, nk, nk->keyname );
+
+#if 0
+ talloc_report_full( registry->mem_ctx, stderr );
+#endif
+ d_printf(_("Closing registry..."));
+ regfio_close( registry );
+ d_printf(_("ok\n"));
+
+ return 0;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_registry_copy(struct net_context *c, int argc, const char **argv )
+{
+ REGF_FILE *infile = NULL, *outfile = NULL;
+ REGF_NK_REC *nk;
+ int result = 1;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry copy <srcfile> <newfile>\n"));
+ return -1;
+ }
+
+ d_printf(_("Opening %s...."), argv[0]);
+ if ( !(infile = regfio_open( argv[0], O_RDONLY, 0 )) ) {
+ d_fprintf(stderr, _("Failed to open %s for reading\n"),argv[0]);
+ return 1;
+ }
+ d_printf(_("ok\n"));
+
+ d_printf(_("Opening %s...."), argv[1]);
+ if ( !(outfile = regfio_open( argv[1], (O_RDWR|O_CREAT|O_TRUNC),
+ (S_IRUSR|S_IWUSR) )) ) {
+ d_fprintf(stderr, _("Failed to open %s for writing\n"),argv[1]);
+ goto out;
+ }
+ d_printf(_("ok\n"));
+
+ /* get the root of the registry file */
+
+ if ((nk = regfio_rootkey( infile )) == NULL) {
+ d_fprintf(stderr, _("Could not get rootkey\n"));
+ goto out;
+ }
+ d_printf(_("RootKey: [%s]\n"), nk->keyname);
+
+ write_registry_tree( infile, nk, NULL, outfile, "" );
+
+ result = 0;
+
+out:
+
+ d_printf(_("Closing %s..."), argv[1]);
+ if (outfile) {
+ regfio_close( outfile );
+ }
+ d_printf(_("ok\n"));
+
+ d_printf(_("Closing %s..."), argv[0]);
+ if (infile) {
+ regfio_close( infile );
+ }
+ d_printf(_("ok\n"));
+
+ return( result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_registry_getsd_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle pol_hive, pol_key;
+ NTSTATUS status;
+ WERROR werr;
+ enum ndr_err_code ndr_err;
+ struct KeySecurityData *sd = NULL;
+ uint32_t sec_info;
+ DATA_BLOB blob;
+ struct security_descriptor sec_desc;
+ uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED |
+ SEC_FLAG_SYSTEM_SECURITY;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc <1 || argc > 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry getsd <path> <secinfo>\n"));
+ d_printf("%s net rpc registry getsd "
+ "'HKLM\\Software\\Samba'\n", _("Example:"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0],
+ access_mask,
+ &pol_hive, &pol_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ sd = talloc_zero(mem_ctx, struct KeySecurityData);
+ if (!sd) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ sd->size = 0x1000;
+
+ if (argc >= 2) {
+ sscanf(argv[1], "%x", &sec_info);
+ } else {
+ sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
+ }
+
+ status = registry_getsd(mem_ctx, b, &pol_key, sec_info, sd, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("getting sd failed: %s\n"),
+ nt_errstr(status));
+ goto out;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ d_fprintf(stderr, _("getting sd failed: %s\n"),
+ win_errstr(werr));
+ goto out;
+ }
+
+ blob.data = sd->data;
+ blob.length = sd->size;
+
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &sec_desc,
+ (ndr_pull_flags_fn_t)ndr_pull_security_descriptor);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ goto out;
+ }
+ status = NT_STATUS_OK;
+
+ display_sec_desc(&sec_desc);
+
+ out:
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr);
+
+ return status;
+}
+
+
+static int rpc_registry_getsd(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_getsd_internal, argc, argv);
+}
+
+/********************************************************************
+ ********************************************************************/
+/**
+ * @defgroup net_rpc_registry net rpc registry
+ */
+
+/**
+ * @defgroup net_rpc_registry_export Export
+ * @ingroup net_rpc_registry
+ * @{
+ */
+
+static NTSTATUS registry_export(struct rpc_pipe_client* pipe_hnd,
+ TALLOC_CTX* ctx,
+ struct policy_handle* key_hnd,
+ struct reg_format* f,
+ const char* parentfullname,
+ const char* name)
+{
+ NTSTATUS status;
+ uint32_t num_subkeys = 0;
+ uint32_t num_values = 0;
+ char **names = NULL, **classes = NULL;
+ NTTIME **modtimes = NULL;
+ struct regval_blob **values = NULL;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ TALLOC_CTX* mem_ctx = talloc_new(ctx);
+
+
+ const char* fullname = name
+ ? talloc_asprintf(mem_ctx, "%s\\%s", parentfullname, name)
+ : parentfullname;
+ reg_format_key(f, &fullname, 1, false);
+
+ status = registry_enumvalues2(mem_ctx, pipe_hnd, key_hnd, &num_values,
+ &names, &values);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("enumerating values failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ for (i=0; i<num_values; i++) {
+ reg_format_regval_blob(f, names[i], values[i]);
+ }
+
+
+ status = registry_enumkeys(mem_ctx, pipe_hnd, key_hnd, &num_subkeys,
+ &names, &classes, &modtimes);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("enumerating keys failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ for (i=0; i<num_subkeys; i++) {
+ struct policy_handle subkey_hnd;
+ struct winreg_String key;
+ WERROR werr;
+ ZERO_STRUCT(key);
+ /* key.name = talloc_strdup(mem_ctx, names[i]); ??? */
+ key.name = names[i];
+
+ status = dcerpc_winreg_OpenKey(b, mem_ctx, key_hnd, key,
+ 0, REG_KEY_READ,
+ &subkey_hnd, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("dcerpc_winreg_OpenKey failed: %s %s\n"),
+ names[i], nt_errstr(status));
+ continue;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ d_fprintf(stderr,
+ _("dcerpc_winreg_OpenKey failed: %s %s\n"),
+ names[i], win_errstr(werr));
+ continue;
+ }
+
+ status = registry_export(pipe_hnd, mem_ctx, &subkey_hnd,
+ f, fullname, names[i]);
+ if (!(NT_STATUS_IS_OK(status))) {
+ d_fprintf(stderr,
+ _("export key failed: %s %s\n"),
+ names[i], nt_errstr(status));
+ }
+ dcerpc_winreg_CloseKey(b, mem_ctx,
+ &subkey_hnd, &werr);
+ }
+done:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+static NTSTATUS rpc_registry_export_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle pol_hive, pol_key;
+ NTSTATUS status;
+ WERROR werr;
+ struct reg_format* f;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc < 2 || argc > 3 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry export <path> <file> [opt]\n"));
+ d_printf("%s net rpc registry export "
+ "'HKLM\\Software\\Samba' samba.reg\n", _("Example:"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_READ,
+ &pol_hive, &pol_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_openkey failed: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ f = reg_format_file(mem_ctx, argv[1], (argc > 2) ? argv[2] : NULL);
+ if (f == NULL) {
+ d_fprintf(stderr, _("open file failed: %s\n"), strerror(errno));
+ return map_nt_error_from_unix(errno);
+ }
+
+ status = registry_export(pipe_hnd, mem_ctx, &pol_key,
+ f, argv[0], NULL );
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr);
+ dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr);
+
+ return status;
+}
+/********************************************************************
+ ********************************************************************/
+
+static int rpc_registry_export(struct net_context *c, int argc,
+ const char **argv )
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_export_internal, argc, argv );
+}
+
+/**@}*/
+
+/********************************************************************
+ ********************************************************************/
+
+/**
+ * @defgroup net_rpc_registry_import Import
+ * @ingroup net_rpc_registry
+ * @{
+ */
+
+struct import_ctx {
+ struct rpc_pipe_client *pipe_hnd;
+ TALLOC_CTX *mem_ctx;
+};
+
+static WERROR import_create_key(struct import_ctx* ctx,
+ struct policy_handle* parent, const char* name,
+ void** pkey, bool* existing)
+{
+ WERROR werr;
+ NTSTATUS status;
+ void* mem_ctx = talloc_new(ctx->mem_ctx);
+
+ struct policy_handle* key = NULL;
+ struct policy_handle hive;
+ struct winreg_String keyclass, keyname;
+ enum winreg_CreateAction action = REG_ACTION_NONE;
+ struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(keyname);
+ keyname.name = name;
+
+ if (parent == NULL) {
+ uint32_t hive_idx = 0;
+ if (!reg_hive_key(mem_ctx, name, &hive_idx, &keyname.name)) {
+ werr = WERR_FOOBAR;
+ goto done;
+ }
+
+ status = dcerpc_winreg_Connect(b, mem_ctx,
+ hive_idx, SEC_FLAG_MAXIMUM_ALLOWED,
+ &hive, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ parent = &hive;
+ }
+
+ key = talloc_zero(mem_ctx, struct policy_handle);
+ if (key == NULL) {
+ werr = WERR_NOT_ENOUGH_MEMORY;
+ goto done;
+ }
+
+ ZERO_STRUCT(keyclass);
+ keyclass.name = "";
+
+ status = dcerpc_winreg_CreateKey(b, mem_ctx,
+ parent, keyname,
+ keyclass, 0, REG_KEY_READ, NULL,
+ key, &action, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("dcerpc_winreg_CreateKey returned %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("dcerpc_winreg_CreateKey returned %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ switch (action) {
+ case REG_CREATED_NEW_KEY:
+ d_printf(_("createkey created %s\n"), name);
+ if (existing != NULL)
+ *existing = false;
+ break;
+
+ case REG_OPENED_EXISTING_KEY:
+ d_printf(_("createkey opened existing %s\n"), name);
+ if (existing != NULL)
+ *existing = true;
+ break;
+
+ case REG_ACTION_NONE:
+ d_printf(_("createkey did nothing -- huh?\n"));
+ werr = WERR_CREATE_FAILED;
+ break;
+ default:
+ assert(false);
+ }
+
+done:
+ if ( parent == &hive ) {
+ WERROR _result;
+ dcerpc_winreg_CloseKey(b, mem_ctx,
+ parent, &_result);
+ }
+
+ if (pkey!=NULL) {
+ *pkey = talloc_steal(ctx->mem_ctx, key);
+ }
+
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_delete_key(struct import_ctx* ctx,
+ struct policy_handle* parent, const char* name)
+{
+ WERROR werr;
+ NTSTATUS status;
+ void* mem_ctx = talloc_new(ctx->mem_ctx);
+ struct winreg_String keyname = { 0, };
+ struct policy_handle hive;
+ struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle;
+
+ keyname.name = name;
+
+ if (parent == NULL) {
+ uint32_t hive_idx;
+ if (!reg_hive_key(mem_ctx, name, &hive_idx, &keyname.name)) {
+ werr = WERR_FOOBAR;
+ goto done;
+ }
+
+ status = dcerpc_winreg_Connect(b, mem_ctx, hive_idx,
+ SEC_FLAG_MAXIMUM_ALLOWED, &hive,
+ &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ parent = &hive;
+ }
+
+ status = dcerpc_winreg_DeleteKey(b, mem_ctx, parent,
+ keyname, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("dcerpc_winreg_DeleteKey returned %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("dcerpc_winreg_DeleteKey returned %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+done:
+ if ( parent == &hive ) {
+ WERROR _result;
+ dcerpc_winreg_CloseKey(b, mem_ctx, parent, &_result);
+ }
+
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_close_key(struct import_ctx* ctx,
+ struct policy_handle* key)
+{
+ WERROR werr;
+ NTSTATUS status;
+ void* mem_ctx = talloc_new(ctx->mem_ctx);
+ struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle;
+
+ status = dcerpc_winreg_CloseKey(b, mem_ctx, key, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("dcerpc_winreg_CloseKey returned %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("dcerpc_winreg_CloseKey returned %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+ werr = (talloc_free(key) == 0) ? WERR_OK : WERR_GEN_FAILURE;
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_create_val(struct import_ctx* ctx,
+ struct policy_handle* parent, const char* name,
+ uint32_t type, const uint8_t* val, uint32_t len)
+{
+ WERROR werr;
+ NTSTATUS status;
+ void* mem_ctx = talloc_new(ctx->mem_ctx);
+ struct winreg_String valuename;
+ struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle;
+
+ if (parent == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(valuename);
+ valuename.name = name;
+
+ status = dcerpc_winreg_SetValue(b, mem_ctx, parent,
+ valuename, type,
+ (uint8_t *)discard_const(val), len, &werr);
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("registry_setvalue failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, _("registry_setvalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+static WERROR import_delete_val(struct import_ctx* ctx,
+ struct policy_handle* parent, const char* name)
+{
+ WERROR werr;
+ NTSTATUS status;
+ void* mem_ctx = talloc_new(ctx->mem_ctx);
+ struct winreg_String valuename;
+ struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle;
+
+ if (parent == NULL) {
+ return WERR_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(valuename);
+ valuename.name = name;
+
+ status = dcerpc_winreg_DeleteValue(b, mem_ctx,
+ parent, valuename, &werr);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ werr = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("registry_deletevalue failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("registry_deletevalue failed: %s\n"),
+ win_errstr(werr));
+ goto done;
+ }
+
+done:
+ talloc_free(mem_ctx);
+ return werr;
+}
+
+
+
+static NTSTATUS rpc_registry_import_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct import_ctx import_ctx;
+
+ struct reg_import_callback import_callback = {
+ .openkey = NULL,
+ .closekey = (reg_import_callback_closekey_t)&import_close_key,
+ .createkey = (reg_import_callback_createkey_t)&import_create_key,
+ .deletekey = (reg_import_callback_deletekey_t)&import_delete_key,
+ .deleteval = (reg_import_callback_deleteval_t)&import_delete_val,
+ .setval = {
+ .blob = (reg_import_callback_setval_blob_t)&import_create_val,
+ },
+ .setval_type = BLOB,
+ .data = &import_ctx
+ };
+
+ int ret;
+ if (argc < 1 || argc > 2 || c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc registry import <file> [options]\n"));
+ d_printf("%s net rpc registry export "
+ "samba.reg enc=CP1252,flags=0\n", _("Example:"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ZERO_STRUCT(import_ctx);
+ import_ctx.pipe_hnd = pipe_hnd;
+ import_ctx.mem_ctx = mem_ctx;
+ ret = reg_parse_file(argv[0],
+ reg_import_adapter(import_ctx.mem_ctx,
+ import_callback
+ ),
+ (argc > 1) ? argv[1] : NULL
+ );
+
+ return ret==0 ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL;
+}
+
+/********************************************************************
+ ********************************************************************/
+
+static int rpc_registry_import(struct net_context *c, int argc,
+ const char **argv )
+{
+ return run_rpc_command(c, NULL, &ndr_table_winreg, 0,
+ rpc_registry_import_internal, argc, argv );
+}
+
+/**@}*/
+/********************************************************************
+ ********************************************************************/
+
+int net_rpc_registry(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "enumerate",
+ rpc_registry_enumerate,
+ NET_TRANSPORT_RPC,
+ N_("Enumerate registry keys and values"),
+ N_("net rpc registry enumerate\n"
+ " Enumerate registry keys and values")
+ },
+ {
+ "createkey",
+ rpc_registry_createkey,
+ NET_TRANSPORT_RPC,
+ N_("Create a new registry key"),
+ N_("net rpc registry createkey\n"
+ " Create a new registry key")
+ },
+ {
+ "deletekey",
+ rpc_registry_deletekey,
+ NET_TRANSPORT_RPC,
+ N_("Delete a registry key"),
+ N_("net rpc registry deletekey\n"
+ " Delete a registry key")
+ },
+ {
+ "getvalue",
+ rpc_registry_getvalue,
+ NET_TRANSPORT_RPC,
+ N_("Print a registry value"),
+ N_("net rpc registry getvalue\n"
+ " Print a registry value")
+ },
+ {
+ "getvalueraw",
+ rpc_registry_getvalueraw,
+ NET_TRANSPORT_RPC,
+ N_("Print a registry value"),
+ N_("net rpc registry getvalueraw\n"
+ " Print a registry value (raw version)")
+ },
+ {
+ "setvalue",
+ rpc_registry_setvalue,
+ NET_TRANSPORT_RPC,
+ N_("Set a new registry value"),
+ N_("net rpc registry setvalue\n"
+ " Set a new registry value")
+ },
+ {
+ "deletevalue",
+ rpc_registry_deletevalue,
+ NET_TRANSPORT_RPC,
+ N_("Delete a registry value"),
+ N_("net rpc registry deletevalue\n"
+ " Delete a registry value")
+ },
+ {
+ "save",
+ rpc_registry_save,
+ NET_TRANSPORT_RPC,
+ N_("Save a registry file"),
+ N_("net rpc registry save\n"
+ " Save a registry file")
+ },
+ {
+ "dump",
+ rpc_registry_dump,
+ NET_TRANSPORT_RPC,
+ N_("Dump a registry file"),
+ N_("net rpc registry dump\n"
+ " Dump a registry file")
+ },
+ {
+ "copy",
+ rpc_registry_copy,
+ NET_TRANSPORT_RPC,
+ N_("Copy a registry file"),
+ N_("net rpc registry copy\n"
+ " Copy a registry file")
+ },
+ {
+ "getsd",
+ rpc_registry_getsd,
+ NET_TRANSPORT_RPC,
+ N_("Get security descriptor"),
+ N_("net rpc registry getsd\n"
+ " Get security descriptor")
+ },
+ {
+ "import",
+ rpc_registry_import,
+ NET_TRANSPORT_RPC,
+ N_("Import .reg file"),
+ N_("net rpc registry import\n"
+ " Import .reg file")
+ },
+ {
+ "export",
+ rpc_registry_export,
+ NET_TRANSPORT_RPC,
+ N_("Export .reg file"),
+ N_("net rpc registry export\n"
+ " Export .reg file")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ return net_run_function(c, argc, argv, "net rpc registry", func);
+}
diff --git a/source3/utils/net_rpc_rights.c b/source3/utils/net_rpc_rights.c
new file mode 100644
index 0000000..267ce65
--- /dev/null
+++ b/source3/utils/net_rpc_rights.c
@@ -0,0 +1,798 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) Gerald (Jerry) Carter 2004
+ Copyright (C) Guenther Deschner 2008
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "rpc_client/init_lsa.h"
+#include "../libcli/security/security.h"
+#include "lib/util/string_wrappers.h"
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS sid_to_name(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct dom_sid *sid,
+ fstring name)
+{
+ struct policy_handle pol;
+ enum lsa_SidType *sid_types = NULL;
+ NTSTATUS status, result;
+ char **domains = NULL, **names = NULL;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ status = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &pol, 1, sid, &domains, &names, &sid_types);
+
+ if ( NT_STATUS_IS_OK(status) ) {
+ if ( *domains[0] )
+ fstr_sprintf( name, "%s\\%s", domains[0], names[0] );
+ else
+ fstrcpy( name, names[0] );
+ }
+
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS name_to_sid(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct dom_sid *sid, const char *name)
+{
+ struct policy_handle pol;
+ enum lsa_SidType *sid_types;
+ NTSTATUS status, result;
+ struct dom_sid *sids;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* maybe its a raw SID */
+ if (dom_sid_parse(name, sid)) {
+ return NT_STATUS_OK;
+ }
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ status = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &pol, 1, &name,
+ NULL, 1, &sids, &sid_types);
+
+ if ( NT_STATUS_IS_OK(status) )
+ sid_copy( sid, &sids[0] );
+
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS enum_privileges(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ struct policy_handle *pol )
+{
+ NTSTATUS status, result;
+ uint32_t enum_context = 0;
+ uint32_t pref_max_length=0x1000;
+ uint32_t i;
+ uint16_t lang_id=0;
+ uint16_t lang_id_sys=0;
+ uint16_t lang_id_desc;
+ struct lsa_StringLarge *description = NULL;
+ struct lsa_PrivArray priv_array;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_EnumPrivs(b, ctx,
+ pol,
+ &enum_context,
+ &priv_array,
+ pref_max_length,
+ &result);
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* Print results */
+
+ for (i = 0; i < priv_array.count; i++) {
+
+ struct lsa_String lsa_name;
+
+ d_printf("%30s ",
+ priv_array.privs[i].name.string ? priv_array.privs[i].name.string : "*unknown*" );
+
+ /* try to get the description */
+
+ init_lsa_String(&lsa_name, priv_array.privs[i].name.string);
+
+ status = dcerpc_lsa_LookupPrivDisplayName(b, ctx,
+ pol,
+ &lsa_name,
+ lang_id,
+ lang_id_sys,
+ &description,
+ &lang_id_desc,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("??????\n");
+ continue;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("??????\n");
+ continue;
+ }
+
+ d_printf("%s\n", description ? description->string : "??????");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS check_privilege_for_user(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ struct policy_handle *pol,
+ struct dom_sid *sid,
+ const char *right)
+{
+ NTSTATUS status, result;
+ struct lsa_RightSet rights;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_EnumAccountRights(b, ctx,
+ pol,
+ sid,
+ &rights,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ if (rights.count == 0) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ for (i = 0; i < rights.count; i++) {
+ if (strcasecmp_m(rights.names[i].string, right) == 0) {
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS enum_privileges_for_user(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ struct policy_handle *pol,
+ struct dom_sid *sid )
+{
+ NTSTATUS status, result;
+ struct lsa_RightSet rights;
+ uint32_t i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_EnumAccountRights(b, ctx,
+ pol,
+ sid,
+ &rights,
+ &result);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ if (rights.count == 0) {
+ d_printf(_("No privileges assigned\n"));
+ }
+
+ for (i = 0; i < rights.count; i++) {
+ printf("%s\n", rights.names[i].string);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS enum_accounts_for_privilege(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ struct policy_handle *pol,
+ const char *privilege)
+{
+ NTSTATUS status, result;
+ uint32_t enum_context=0;
+ uint32_t pref_max_length=0x1000;
+ struct lsa_SidArray sid_array;
+ uint32_t i;
+ fstring name;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_EnumAccounts(b, ctx,
+ pol,
+ &enum_context,
+ &sid_array,
+ pref_max_length,
+ &result);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ d_printf("%s:\n", privilege);
+
+ for ( i=0; i<sid_array.num_sids; i++ ) {
+
+ status = check_privilege_for_user(pipe_hnd, ctx, pol,
+ sid_array.sids[i].sid,
+ privilege);
+
+ if ( ! NT_STATUS_IS_OK(status)) {
+ if ( ! NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return status;
+ }
+ continue;
+ }
+
+ /* try to convert the SID to a name. Fall back to
+ printing the raw SID if necessary */
+ status = sid_to_name( pipe_hnd, ctx, sid_array.sids[i].sid, name );
+ if ( !NT_STATUS_IS_OK (status) )
+ sid_to_fstring(name, sid_array.sids[i].sid);
+
+ d_printf(" %s\n", name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS enum_privileges_for_accounts(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ struct policy_handle *pol)
+{
+ NTSTATUS status, result;
+ uint32_t enum_context=0;
+ uint32_t pref_max_length=0x1000;
+ struct lsa_SidArray sid_array;
+ uint32_t i;
+ fstring name;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_EnumAccounts(b, ctx,
+ pol,
+ &enum_context,
+ &sid_array,
+ pref_max_length,
+ &result);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for ( i=0; i<sid_array.num_sids; i++ ) {
+
+ /* try to convert the SID to a name. Fall back to
+ printing the raw SID if necessary */
+
+ status = sid_to_name(pipe_hnd, ctx, sid_array.sids[i].sid, name);
+ if ( !NT_STATUS_IS_OK (status) )
+ sid_to_fstring(name, sid_array.sids[i].sid);
+
+ d_printf("%s\n", name);
+
+ status = enum_privileges_for_user(pipe_hnd, ctx, pol,
+ sid_array.sids[i].sid);
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ d_printf("\n");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_list_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ struct dom_sid sid;
+ fstring privname;
+ struct lsa_String lsa_name;
+ struct lsa_StringLarge *description = NULL;
+ uint16_t lang_id = 0;
+ uint16_t lang_id_sys = 0;
+ uint16_t lang_id_desc;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(status) )
+ return status;
+
+ /* backwards compatibility; just list available privileges if no argument */
+
+ if (argc == 0) {
+ status = enum_privileges(pipe_hnd, mem_ctx, &pol );
+ goto done;
+ }
+
+ if (strequal(argv[0], "privileges")) {
+ int i = 1;
+
+ if (argv[1] == NULL) {
+ status = enum_privileges(pipe_hnd, mem_ctx, &pol );
+ goto done;
+ }
+
+ while ( argv[i] != NULL ) {
+ fstrcpy(privname, argv[i]);
+ init_lsa_String(&lsa_name, argv[i]);
+ i++;
+
+ /* verify that this is a valid privilege for error reporting */
+ status = dcerpc_lsa_LookupPrivDisplayName(b, mem_ctx,
+ &pol,
+ &lsa_name,
+ lang_id,
+ lang_id_sys,
+ &description,
+ &lang_id_desc,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ continue;
+ }
+ status = result;
+ if ( !NT_STATUS_IS_OK(result) ) {
+ if ( NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_PRIVILEGE))
+ d_fprintf(stderr, _("No such privilege "
+ "exists: %s.\n"), privname);
+ else
+ d_fprintf(stderr, _("Error resolving "
+ "privilege display name "
+ "[%s].\n"),
+ nt_errstr(result));
+ continue;
+ }
+
+ status = enum_accounts_for_privilege(pipe_hnd, mem_ctx, &pol, privname);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Error enumerating "
+ "accounts for privilege %s [%s].\n"),
+ privname, nt_errstr(status));
+ continue;
+ }
+ }
+ goto done;
+ }
+
+ /* special case to enumerate all privileged SIDs with associated rights */
+
+ if (strequal( argv[0], "accounts")) {
+ int i = 1;
+
+ if (argv[1] == NULL) {
+ status = enum_privileges_for_accounts(pipe_hnd, mem_ctx, &pol);
+ goto done;
+ }
+
+ while (argv[i] != NULL) {
+ status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[i]);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ i++;
+ }
+ goto done;
+ }
+
+ /* backward compatibility: if no keyword provided, treat the key
+ as an account name */
+ if (argc > 1) {
+ d_printf("%s net rpc rights list [[accounts|privileges] "
+ "[name|SID]]\n", _("Usage:"));
+ status = NT_STATUS_OK;
+ goto done;
+ }
+
+ status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ status = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid );
+
+done:
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_grant_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle dom_pol = {
+ .handle_type = 0,
+ };
+ NTSTATUS status, result;
+ struct lsa_RightSet rights;
+ int i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ struct dom_sid sid;
+
+ if (argc < 2 ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc rights grant <name|SID> <rights...>\n"));
+ return NT_STATUS_OK;
+ }
+
+ status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED))
+ status = NT_STATUS_NO_SUCH_USER;
+
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+
+ status = dcerpc_lsa_open_policy_fallback(b,
+ mem_ctx,
+ pipe_hnd->srv_name_slash,
+ true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &out_version,
+ &out_revision_info,
+ &dom_pol,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ DBG_DEBUG("Couldn't open policy handle: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ rights.count = argc-1;
+ rights.names = talloc_array(mem_ctx, struct lsa_StringLarge,
+ rights.count);
+ if (rights.names == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ for (i=0; i<argc-1; i++) {
+ init_lsa_StringLarge(&rights.names[i], argv[i+1]);
+ }
+
+ status = dcerpc_lsa_AddAccountRights(b, mem_ctx,
+ &dom_pol,
+ &sid,
+ &rights,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto done;
+ }
+
+ d_printf(_("Successfully granted rights.\n"));
+
+ done:
+ if ( !NT_STATUS_IS_OK(status) ) {
+ d_fprintf(stderr, _("Failed to grant privileges for %s (%s)\n"),
+ argv[0], nt_errstr(status));
+ }
+
+ dcerpc_lsa_Close(b, mem_ctx, &dom_pol, &result);
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_revoke_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle dom_pol;
+ NTSTATUS status, result;
+ struct lsa_RightSet rights;
+ struct dom_sid sid;
+ int i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+ union lsa_revision_info out_revision_info = {
+ .info1 =
+ {
+ .revision = 0,
+ },
+ };
+ uint32_t out_version = 0;
+
+ if (argc < 2 ) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _(" net rpc rights revoke <name|SID> <rights...>\n"));
+ return NT_STATUS_OK;
+ }
+
+ status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (!NT_STATUS_IS_OK(status))
+ return status;
+
+ status = dcerpc_lsa_open_policy_fallback(b,
+ mem_ctx,
+ pipe_hnd->srv_name_slash,
+ true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &out_version,
+ &out_revision_info,
+ &dom_pol,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ DBG_DEBUG("Couldn't open policy handle: %s\n",
+ nt_errstr(status));
+ return status;
+ }
+
+ rights.count = argc-1;
+ rights.names = talloc_array(mem_ctx, struct lsa_StringLarge,
+ rights.count);
+ if (!rights.names) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0; i<argc-1; i++) {
+ init_lsa_StringLarge(&rights.names[i], argv[i+1]);
+ }
+
+ status = dcerpc_lsa_RemoveAccountRights(b, mem_ctx,
+ &dom_pol,
+ &sid,
+ false,
+ &rights,
+ &result);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ d_printf(_("Successfully revoked rights.\n"));
+
+done:
+ if ( !NT_STATUS_IS_OK(status) ) {
+ d_fprintf(stderr,_("Failed to revoke privileges for %s (%s)\n"),
+ argv[0], nt_errstr(status));
+ }
+
+ dcerpc_lsa_Close(b, mem_ctx, &dom_pol, &result);
+
+ return status;
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_rights_list(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc rights list [{accounts|privileges} "
+ "[name|SID]]\n"
+ " View available/assigned privileges\n"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_rights_list_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_rights_grant(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc rights grant <name|SID> <right>\n"
+ " Assign privilege[s]\n"));
+ d_printf(_("For example:\n"
+ " net rpc rights grant 'VALE\\biddle' "
+ "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n"
+ " would grant the printer admin and disk manager "
+ "rights to the user 'VALE\\biddle'\n"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_rights_grant_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_rights_revoke(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc rights revoke <name|SID> <right>\n"
+ " Revoke privilege[s]\n"));
+ d_printf(_("For example:\n"
+ " net rpc rights revoke 'VALE\\biddle' "
+ "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n"
+ " would revoke the printer admin and disk manager"
+ " rights from the user 'VALE\\biddle'\n"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0,
+ rpc_rights_revoke_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+int net_rpc_rights(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ rpc_rights_list,
+ NET_TRANSPORT_RPC,
+ N_("View available/assigned privileges"),
+ N_("net rpc rights list\n"
+ " View available/assigned privileges")
+ },
+ {
+ "grant",
+ rpc_rights_grant,
+ NET_TRANSPORT_RPC,
+ N_("Assign privilege[s]"),
+ N_("net rpc rights grant\n"
+ " Assign privilege[s]")
+ },
+ {
+ "revoke",
+ rpc_rights_revoke,
+ NET_TRANSPORT_RPC,
+ N_("Revoke privilege[s]"),
+ N_("net rpc rights revoke\n"
+ " Revoke privilege[s]")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc rights", func);
+}
+
+static NTSTATUS rpc_sh_rights_list(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_rights_list_internal(c, ctx->domain_sid, ctx->domain_name,
+ ctx->cli, pipe_hnd, mem_ctx,
+ argc, argv);
+}
+
+static NTSTATUS rpc_sh_rights_grant(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_rights_grant_internal(c, ctx->domain_sid, ctx->domain_name,
+ ctx->cli, pipe_hnd, mem_ctx,
+ argc, argv);
+}
+
+static NTSTATUS rpc_sh_rights_revoke(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_rights_revoke_internal(c, ctx->domain_sid, ctx->domain_name,
+ ctx->cli, pipe_hnd, mem_ctx,
+ argc, argv);
+}
+
+struct rpc_sh_cmd *net_rpc_rights_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx)
+{
+ static struct rpc_sh_cmd cmds[] = {
+
+ { "list", NULL, &ndr_table_lsarpc, rpc_sh_rights_list,
+ N_("View available or assigned privileges") },
+
+ { "grant", NULL, &ndr_table_lsarpc, rpc_sh_rights_grant,
+ N_("Assign privilege[s]") },
+
+ { "revoke", NULL, &ndr_table_lsarpc, rpc_sh_rights_revoke,
+ N_("Revoke privilege[s]") },
+
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ return cmds;
+}
+
diff --git a/source3/utils/net_rpc_samsync.c b/source3/utils/net_rpc_samsync.c
new file mode 100644
index 0000000..e295d6a
--- /dev/null
+++ b/source3/utils/net_rpc_samsync.c
@@ -0,0 +1,257 @@
+/*
+ Unix SMB/CIFS implementation.
+ dump the remote SAM using rpc samsync operations
+
+ Copyright (C) Andrew Tridgell 2002
+ Copyright (C) Tim Potter 2001,2002
+ Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005
+ Modified by Volker Lendecke 2002
+ Copyright (C) Jeremy Allison 2005.
+ Copyright (C) Guenther Deschner 2008.
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "../librpc/gen_ndr/ndr_netlogon.h"
+#include "../librpc/gen_ndr/ndr_drsuapi.h"
+#include "libnet/libnet_dssync.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+
+/**
+ * Basic usage function for 'net rpc vampire'
+ *
+ * @param c A net_context structure
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int rpc_vampire_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net rpc vampire ([ldif [<ldif-filename>] | [keytab] "
+ "[<keytab-filename]) [options]\n"
+ "\t to pull accounts from a remote PDC where we are a BDC\n"
+ "\t\t no args puts accounts in local passdb from smb.conf\n"
+ "\t\t ldif - put accounts in ldif format (file defaults to "
+ "/tmp/tmp.ldif)\n"
+ "\t\t keytab - put account passwords in krb5 keytab "
+ "(defaults to system keytab)\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+static NTSTATUS rpc_vampire_ds_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS status;
+ struct dssync_context *ctx = NULL;
+
+ if (!dom_sid_equal(domain_sid, get_global_sam_sid())) {
+ struct dom_sid_buf buf1, buf2;
+ d_printf(_("Cannot import users from %s at this time, "
+ "as the current domain:\n\t%s: %s\nconflicts "
+ "with the remote domain\n\t%s: %s\n"
+ "Perhaps you need to set: \n\n\tsecurity=user\n\t"
+ "workgroup=%s\n\n in your smb.conf?\n"),
+ domain_name,
+ get_global_sam_name(),
+ dom_sid_str_buf(get_global_sam_sid(), &buf1),
+ domain_name,
+ dom_sid_str_buf(domain_sid, &buf2),
+ domain_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = libnet_dssync_init_context(mem_ctx,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ctx->cli = pipe_hnd;
+ ctx->domain_name = domain_name;
+ ctx->ops = &libnet_dssync_passdb_ops;
+
+ status = libnet_dssync(mem_ctx, ctx);
+ if (!NT_STATUS_IS_OK(status) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto out;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ out:
+ TALLOC_FREE(ctx);
+
+ return status;
+}
+
+int rpc_vampire_passdb(struct net_context *c, int argc, const char **argv)
+{
+ int ret = 0;
+ NTSTATUS status;
+ struct cli_state *cli = NULL;
+ struct net_dc_info dc_info;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc vampire passdb\n"
+ " %s\n",
+ _("Usage:"),
+ _("Dump remote SAM database to passdb"));
+ return 0;
+ }
+
+ status = net_make_ipc_connection(c, 0, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ status = net_scan_dc(c, cli, &dc_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ if (!dc_info.is_ad) {
+ printf(_("DC is not running Active Directory, exiting\n"));
+ return -1;
+ }
+
+ if (!c->opt_force) {
+ d_printf( "%s\n"
+ "net rpc vampire passdb\n"
+ " %s\n",
+ _("Usage:"),
+ _("Should not be used against Active Directory, maybe use --force"));
+ return -1;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_drsuapi,
+ NET_FLAGS_SEAL | NET_FLAGS_TCP,
+ rpc_vampire_ds_internals, argc, argv);
+ return ret;
+}
+
+static NTSTATUS rpc_vampire_keytab_ds_internals(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ NTSTATUS status;
+ struct dssync_context *ctx = NULL;
+
+ status = libnet_dssync_init_context(mem_ctx,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ctx->force_full_replication = c->opt_force_full_repl ? true : false;
+ ctx->clean_old_entries = c->opt_clean_old_entries ? true : false;
+
+ if (argc < 1) {
+ /* the caller should ensure that a filename is provided */
+ return NT_STATUS_INVALID_PARAMETER;
+ } else {
+ ctx->output_filename = argv[0];
+ }
+
+ if (argc >= 2) {
+ ctx->object_dns = &argv[1];
+ ctx->object_count = argc - 1;
+ ctx->single_object_replication = c->opt_single_obj_repl ? true
+ : false;
+ }
+
+ ctx->cli = pipe_hnd;
+ ctx->domain_name = domain_name;
+ ctx->ops = &libnet_dssync_keytab_ops;
+
+ status = libnet_dssync(mem_ctx, ctx);
+ if (!NT_STATUS_IS_OK(status) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto out;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ out:
+ TALLOC_FREE(ctx);
+
+ return status;
+}
+
+/**
+ * Basic function for 'net rpc vampire keytab'
+ *
+ * @param c A net_context structure
+ * @param argc Standard main() style argc
+ * @param argc Standard main() style argv. Initial components are already
+ * stripped
+ **/
+
+int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv)
+{
+ int ret = 0;
+ NTSTATUS status;
+ struct cli_state *cli = NULL;
+ struct net_dc_info dc_info;
+
+ if (c->display_usage || (argc < 1)) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net rpc vampire keytab <keytabfile>\n"
+ " Dump remote SAM database to Kerberos keytab "
+ "file\n"));
+ return 0;
+ }
+
+ status = net_make_ipc_connection(c, 0, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ status = net_scan_dc(c, cli, &dc_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ if (!dc_info.is_ad) {
+ printf(_("DC is not running Active Directory, exiting\n"));
+ return -1;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_drsuapi,
+ NET_FLAGS_SEAL | NET_FLAGS_TCP,
+ rpc_vampire_keytab_ds_internals, argc, argv);
+ return ret;
+}
diff --git a/source3/utils/net_rpc_service.c b/source3/utils/net_rpc_service.c
new file mode 100644
index 0000000..a0fbc51
--- /dev/null
+++ b/source3/utils/net_rpc_service.c
@@ -0,0 +1,1138 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) Gerald (Jerry) Carter 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_svcctl.h"
+#include "../librpc/gen_ndr/ndr_svcctl_c.h"
+#include "lib/util/string_wrappers.h"
+
+struct svc_state_msg {
+ uint32_t flag;
+ const char *message;
+};
+
+static struct svc_state_msg state_msg_table[] = {
+ { SVCCTL_STOPPED, N_("stopped") },
+ { SVCCTL_START_PENDING, N_("start pending") },
+ { SVCCTL_STOP_PENDING, N_("stop pending") },
+ { SVCCTL_RUNNING, N_("running") },
+ { SVCCTL_CONTINUE_PENDING, N_("resume pending") },
+ { SVCCTL_PAUSE_PENDING, N_("pause pending") },
+ { SVCCTL_PAUSED, N_("paused") },
+ { 0, NULL }
+};
+
+
+/********************************************************************
+********************************************************************/
+const char *svc_status_string( uint32_t state )
+{
+ fstring msg;
+ int i;
+
+ fstr_sprintf( msg, _("Unknown State [%d]"), state );
+
+ for ( i=0; state_msg_table[i].message; i++ ) {
+ if ( state_msg_table[i].flag == state ) {
+ fstrcpy( msg, state_msg_table[i].message );
+ break;
+ }
+ }
+
+ return talloc_strdup(talloc_tos(), msg);
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR open_service(struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hSCM,
+ const char *service,
+ uint32_t access_mask,
+ struct policy_handle *hService)
+{
+ NTSTATUS status;
+ WERROR result;
+
+ status = dcerpc_svcctl_OpenServiceW(b, mem_ctx,
+ hSCM,
+ service,
+ access_mask,
+ hService,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Failed to open service. [%s]\n"),
+ nt_errstr(status));
+ return result;
+ }
+ if (!W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, _("Failed to open service. [%s]\n"),
+ win_errstr(result));
+ return result;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR open_scm(struct dcerpc_binding_handle *b,
+ TALLOC_CTX *mem_ctx,
+ const char *server_name,
+ uint32_t access_mask,
+ struct policy_handle *hSCM)
+{
+ NTSTATUS status;
+ WERROR result;
+
+ status = dcerpc_svcctl_OpenSCManagerW(b, mem_ctx,
+ server_name,
+ NULL,
+ access_mask,
+ hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr,
+ _("Failed to open Service Control Manager. [%s]\n"),
+ nt_errstr(status));
+ return result;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr,
+ _("Failed to open Service Control Manager. [%s]\n"),
+ win_errstr(result));
+ return result;
+ }
+
+ return WERR_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR query_service_state(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hSCM,
+ const char *service,
+ uint32_t *state )
+{
+ struct policy_handle hService;
+ struct SERVICE_STATUS service_status;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* now cycle until the status is actually 'watch_state' */
+
+ result = open_service(b, mem_ctx, hSCM, service,
+ SC_RIGHT_SVC_QUERY_STATUS,
+ &hService);
+ if (!W_ERROR_IS_OK(result) ) {
+ return result;
+ }
+
+ status = dcerpc_svcctl_QueryServiceStatus(b, mem_ctx,
+ &hService,
+ &service_status,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ goto done;
+ }
+
+ *state = service_status.state;
+
+ done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR watch_service_state(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hSCM,
+ const char *service,
+ uint32_t watch_state,
+ uint32_t *final_state )
+{
+ uint32_t i;
+ uint32_t state = 0;
+ WERROR result = WERR_GEN_FAILURE;
+
+
+ i = 0;
+ while ( (state != watch_state ) && i<30 ) {
+ /* get the status */
+
+ result = query_service_state(pipe_hnd, mem_ctx, hSCM, service, &state );
+ if ( !W_ERROR_IS_OK(result) ) {
+ break;
+ }
+
+ d_printf(".");
+ i++;
+ usleep( 100 );
+ }
+ d_printf("\n");
+
+ *final_state = state;
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR control_service(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ struct policy_handle *hSCM,
+ const char *service,
+ uint32_t control,
+ uint32_t watch_state )
+{
+ struct policy_handle hService;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ struct SERVICE_STATUS service_status;
+ uint32_t state = 0;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ /* Open the Service */
+
+ result = open_service(b, mem_ctx, hSCM, service,
+ (SC_RIGHT_SVC_STOP|SC_RIGHT_SVC_PAUSE_CONTINUE),
+ &hService);
+ if (!W_ERROR_IS_OK(result) ) {
+ return result;
+ }
+
+ /* get the status */
+
+ status = dcerpc_svcctl_ControlService(b, mem_ctx,
+ &hService,
+ control,
+ &service_status,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Control service request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, _("Control service request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ /* loop -- checking the state until we are where we want to be */
+
+ result = watch_service_state(pipe_hnd, mem_ctx, hSCM, service, watch_state, &state );
+
+ d_printf(_("%s service is %s.\n"), service, svc_status_string(state));
+
+done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_list_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM;
+ struct ENUM_SERVICE_STATUSW *services = NULL;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ int i;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ uint8_t *buffer;
+ uint32_t buf_size = 0;
+ uint32_t bytes_needed = 0;
+ uint32_t num_services = 0;
+ uint32_t resume_handle = 0;
+
+ if (argc != 0 ) {
+ d_printf("%s net rpc service list\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ buffer = talloc_array(mem_ctx, uint8_t, buf_size);
+ if (buffer == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ do {
+ status = dcerpc_svcctl_EnumServicesStatusW(b, mem_ctx,
+ &hSCM,
+ SERVICE_TYPE_WIN32,
+ SERVICE_STATE_ALL,
+ buffer,
+ buf_size,
+ &bytes_needed,
+ &num_services,
+ &resume_handle,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ _("Failed to enumerate services. [%s]\n"),
+ nt_errstr(status));
+ break;
+ }
+
+ if (W_ERROR_EQUAL(result, WERR_MORE_DATA) && bytes_needed > 0) {
+ buf_size = bytes_needed;
+ buffer = talloc_realloc(mem_ctx, buffer, uint8_t, bytes_needed);
+ if (buffer == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ }
+ continue;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ status = werror_to_ntstatus(result);
+ d_fprintf(stderr,
+ _("Failed to enumerate services. [%s]\n"),
+ win_errstr(result));
+ break;
+ }
+
+ if ( num_services == 0 ) {
+ d_printf(_("No services returned\n"));
+ break;
+ }
+
+ {
+ enum ndr_err_code ndr_err;
+ DATA_BLOB blob;
+ struct ndr_pull *ndr;
+
+ blob.length = buf_size;
+ blob.data = talloc_steal(mem_ctx, buffer);
+
+ services = talloc_array(mem_ctx, struct ENUM_SERVICE_STATUSW, num_services);
+ if (!services) {
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ }
+
+ ndr = ndr_pull_init_blob(&blob, mem_ctx);
+ if (ndr == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ break;
+ }
+
+ ndr_err = ndr_pull_ENUM_SERVICE_STATUSW_array(
+ ndr, num_services, services);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ break;
+ }
+
+ for ( i=0; i<num_services; i++ ) {
+ d_printf("%-20s \"%s\"\n",
+ services[i].service_name,
+ services[i].display_name);
+ }
+ }
+
+ } while (W_ERROR_EQUAL(result, WERR_MORE_DATA));
+
+done:
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return status;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_status_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM, hService;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ struct SERVICE_STATUS service_status;
+ struct QUERY_SERVICE_CONFIG config;
+ uint32_t buf_size = sizeof(config);
+ uint32_t ret_size = 0;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service status <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ /* Open the Service */
+
+ result = open_service(b, mem_ctx, &hSCM, argv[0],
+ (SC_RIGHT_SVC_QUERY_STATUS|SC_RIGHT_SVC_QUERY_CONFIG),
+ &hService);
+ if (!W_ERROR_IS_OK(result) ) {
+ goto done;
+ }
+
+ /* get the status */
+
+ status = dcerpc_svcctl_QueryServiceStatus(b, mem_ctx,
+ &hService,
+ &service_status,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Query status request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (!W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, _("Query status request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ d_printf(_("%s service is %s.\n"), argv[0],
+ svc_status_string(service_status.state));
+
+ /* get the config */
+
+ status = dcerpc_svcctl_QueryServiceConfigW(b, mem_ctx,
+ &hService,
+ &config,
+ buf_size,
+ &ret_size,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Query config request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ if (W_ERROR_EQUAL(result, WERR_INSUFFICIENT_BUFFER)) {
+ buf_size = ret_size;
+ status = dcerpc_svcctl_QueryServiceConfigW(b, mem_ctx,
+ &hService,
+ &config,
+ buf_size,
+ &ret_size,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Query config request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ }
+
+ if (!W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, _("Query config request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ /* print out the configuration information for the service */
+
+ d_printf(_("Configuration details:\n"));
+ d_printf(_("\tControls Accepted = 0x%x\n"),
+ service_status.controls_accepted);
+ d_printf(_("\tService Type = 0x%x\n"), config.service_type);
+ d_printf(_("\tStart Type = 0x%x\n"), config.start_type);
+ d_printf(_("\tError Control = 0x%x\n"), config.error_control);
+ d_printf(_("\tTag ID = 0x%x\n"), config.tag_id);
+
+ if (config.executablepath) {
+ d_printf(_("\tExecutable Path = %s\n"),
+ config.executablepath);
+ }
+
+ if (config.loadordergroup) {
+ d_printf(_("\tLoad Order Group = %s\n"),
+ config.loadordergroup);
+ }
+
+ if (config.dependencies) {
+ d_printf(_("\tDependencies = %s\n"),
+ config.dependencies);
+ }
+
+ if (config.startname) {
+ d_printf(_("\tStart Name = %s\n"), config.startname);
+ }
+
+ if (config.displayname) {
+ d_printf(_("\tDisplay Name = %s\n"),
+ config.displayname);
+ }
+
+done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_stop_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM;
+ WERROR result = WERR_GEN_FAILURE;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service status <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0],
+ SVCCTL_CONTROL_STOP, SVCCTL_STOPPED );
+
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_pause_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM;
+ WERROR result = WERR_GEN_FAILURE;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service status <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0],
+ SVCCTL_CONTROL_PAUSE, SVCCTL_PAUSED );
+
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_resume_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM;
+ WERROR result = WERR_GEN_FAILURE;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service status <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0],
+ SVCCTL_CONTROL_CONTINUE, SVCCTL_RUNNING );
+
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_start_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ struct policy_handle hSCM, hService;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ uint32_t state = 0;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service status <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+
+ /* Open the Service */
+
+ result = open_service(b, mem_ctx, &hSCM, argv[0],
+ SC_RIGHT_SVC_START,
+ &hService);
+ if (!W_ERROR_IS_OK(result) ) {
+ goto done;
+ }
+
+ /* get the status */
+
+ status = dcerpc_svcctl_StartServiceW(b, mem_ctx,
+ &hService,
+ 0,
+ NULL,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Query status request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, _("Query status request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ result = watch_service_state(pipe_hnd, mem_ctx, &hSCM, argv[0], SVCCTL_RUNNING, &state );
+
+ if ( W_ERROR_IS_OK(result) && (state == SVCCTL_RUNNING) )
+ d_printf(_("Successfully started service: %s\n"),
+ argv[0] );
+ else
+ d_fprintf(stderr,_("Failed to start service: %s [%s]\n"),
+ argv[0], win_errstr(result) );
+
+done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_delete_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle hSCM, hService;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 1 ) {
+ d_printf("%s net rpc service delete <service>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ ZERO_STRUCT(hSCM);
+ ZERO_STRUCT(hService);
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ /* Open the Service */
+
+ result = open_service(b, mem_ctx, &hSCM, argv[0],
+ SERVICE_ALL_ACCESS,
+ &hService);
+ if (!W_ERROR_IS_OK(result) ) {
+ goto done;
+ }
+
+ /* Delete the Service */
+
+ status = dcerpc_svcctl_DeleteService(b, mem_ctx,
+ &hService,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Delete service request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, _("Delete service request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ d_printf(_("Successfully deleted Service: %s\n"), argv[0]);
+
+ done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_create_internal(struct net_context *c,
+ const struct dom_sid *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ struct policy_handle hSCM, hService;
+ WERROR result = WERR_GEN_FAILURE;
+ NTSTATUS status;
+ const char *ServiceName;
+ const char *DisplayName;
+ const char *binary_path;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ if (argc != 3) {
+ d_printf("%s net rpc service create <service> "
+ "<displayname> <binarypath>\n", _("Usage:"));
+ return NT_STATUS_OK;
+ }
+
+ ZERO_STRUCT(hSCM);
+ ZERO_STRUCT(hService);
+
+ /* Open the Service Control Manager */
+ result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash,
+ SC_RIGHT_MGR_CREATE_SERVICE,
+ &hSCM);
+ if (!W_ERROR_IS_OK(result)) {
+ return werror_to_ntstatus(result);
+ }
+
+ /* Create the service */
+
+ ServiceName = argv[0];
+ DisplayName = argv[1];
+ binary_path = argv[2];
+
+ status = dcerpc_svcctl_CreateServiceW(b, mem_ctx,
+ &hSCM,
+ ServiceName,
+ DisplayName,
+ SERVICE_ALL_ACCESS,
+ SERVICE_TYPE_WIN32_OWN_PROCESS,
+ SVCCTL_DEMAND_START,
+ SVCCTL_SVC_ERROR_NORMAL,
+ binary_path,
+ NULL, /* LoadOrderGroupKey */
+ NULL, /* TagId */
+ NULL, /* dependencies */
+ 0, /* dependencies_size */
+ NULL, /* service_start_name */
+ NULL, /* password */
+ 0, /* password_size */
+ &hService,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ result = ntstatus_to_werror(status);
+ d_fprintf(stderr, _("Create service request failed. [%s]\n"),
+ nt_errstr(status));
+ goto done;
+ }
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, _("Create service request failed. [%s]\n"),
+ win_errstr(result));
+ goto done;
+ }
+
+ d_printf(_("Successfully created Service: %s\n"), argv[0]);
+
+ done:
+ if (is_valid_policy_hnd(&hService)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result);
+ }
+ if (is_valid_policy_hnd(&hSCM)) {
+ WERROR _result;
+ dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result);
+ }
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_list(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service list\n"
+ " %s\n",
+ _("Usage:"),
+ _("View configured Win32 services"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_list_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_start(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service start <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Start a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_start_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_stop(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service stop <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Stop a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_stop_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_resume(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service resume <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Resume a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_resume_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_pause(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service pause <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Pause a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_pause_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_status(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service status <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Show the current status of a service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_status_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_delete(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service delete <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Delete a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_delete_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_service_create(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net rpc service create <service>\n"
+ " %s\n",
+ _("Usage:"),
+ _("Create a Win32 service"));
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl, 0,
+ rpc_service_create_internal, argc, argv);
+}
+
+/********************************************************************
+********************************************************************/
+
+int net_rpc_service(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ rpc_service_list,
+ NET_TRANSPORT_RPC,
+ N_("View configured Win32 services"),
+ N_("net rpc service list\n"
+ " View configured Win32 services")
+ },
+ {
+ "start",
+ rpc_service_start,
+ NET_TRANSPORT_RPC,
+ N_("Start a service"),
+ N_("net rpc service start\n"
+ " Start a service")
+ },
+ {
+ "stop",
+ rpc_service_stop,
+ NET_TRANSPORT_RPC,
+ N_("Stop a service"),
+ N_("net rpc service stop\n"
+ " Stop a service")
+ },
+ {
+ "pause",
+ rpc_service_pause,
+ NET_TRANSPORT_RPC,
+ N_("Pause a service"),
+ N_("net rpc service pause\n"
+ " Pause a service")
+ },
+ {
+ "resume",
+ rpc_service_resume,
+ NET_TRANSPORT_RPC,
+ N_("Resume a paused service"),
+ N_("net rpc service resume\n"
+ " Resume a service")
+ },
+ {
+ "status",
+ rpc_service_status,
+ NET_TRANSPORT_RPC,
+ N_("View current status of a service"),
+ N_("net rpc service status\n"
+ " View current status of a service")
+ },
+ {
+ "delete",
+ rpc_service_delete,
+ NET_TRANSPORT_RPC,
+ N_("Delete a service"),
+ N_("net rpc service delete\n"
+ " Deletes a service")
+ },
+ {
+ "create",
+ rpc_service_create,
+ NET_TRANSPORT_RPC,
+ N_("Create a service"),
+ N_("net rpc service create\n"
+ " Creates a service")
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc service",func);
+}
diff --git a/source3/utils/net_rpc_sh_acct.c b/source3/utils/net_rpc_sh_acct.c
new file mode 100644
index 0000000..3fc6568
--- /dev/null
+++ b/source3/utils/net_rpc_sh_acct.c
@@ -0,0 +1,489 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2006 Volker Lendecke (vl@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/rpc_client.h"
+#include "../librpc/gen_ndr/ndr_samr_c.h"
+#include "../libcli/security/security.h"
+
+/*
+ * Do something with the account policies. Read them all, run a function on
+ * them and possibly write them back. "fn" has to return the container index
+ * it has modified, it can return 0 for no change.
+ */
+
+static NTSTATUS rpc_sh_acct_do(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv,
+ int (*fn)(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv))
+{
+ struct policy_handle connect_pol, domain_pol;
+ NTSTATUS status, result;
+ union samr_DomainInfo *info1 = NULL;
+ union samr_DomainInfo *info3 = NULL;
+ union samr_DomainInfo *info12 = NULL;
+ int store;
+ struct dcerpc_binding_handle *b = pipe_hnd->binding_handle;
+
+ ZERO_STRUCT(connect_pol);
+ ZERO_STRUCT(domain_pol);
+
+ /* Get sam policy handle */
+
+ status = dcerpc_samr_Connect2(b, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ status = dcerpc_samr_OpenDomain(b, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ ctx->domain_sid,
+ &domain_pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 1,
+ &info1,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("query_domain_info level 1 failed: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 3,
+ &info3,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("query_domain_info level 3 failed: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ status = dcerpc_samr_QueryDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 12,
+ &info12,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ d_fprintf(stderr, _("query_domain_info level 12 failed: %s\n"),
+ nt_errstr(result));
+ goto done;
+ }
+
+ store = fn(c, mem_ctx, ctx, &info1->info1, &info3->info3,
+ &info12->info12, argc, argv);
+
+ if (store <= 0) {
+ /* Don't save anything */
+ goto done;
+ }
+
+ switch (store) {
+ case 1:
+ status = dcerpc_samr_SetDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 1,
+ info1,
+ &result);
+ break;
+ case 3:
+ status = dcerpc_samr_SetDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 3,
+ info3,
+ &result);
+ break;
+ case 12:
+ status = dcerpc_samr_SetDomainInfo(b, mem_ctx,
+ &domain_pol,
+ 12,
+ info12,
+ &result);
+ break;
+ default:
+ d_fprintf(stderr, _("Got unexpected info level %d\n"), store);
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = result;
+
+ done:
+ if (is_valid_policy_hnd(&domain_pol)) {
+ dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result);
+ }
+ if (is_valid_policy_hnd(&connect_pol)) {
+ dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result);
+ }
+
+ return status;
+}
+
+static int account_show(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 0) {
+ d_fprintf(stderr, "%s %s\n", _("Usage:"), ctx->whoami);
+ return -1;
+ }
+
+ d_printf(_("Minimum password length: %d\n"), i1->min_password_length);
+ d_printf(_("Password history length: %d\n"),
+ i1->password_history_length);
+
+ d_printf(_("Minimum password age: "));
+ if (!nt_time_is_zero((NTTIME *)&i1->min_password_age)) {
+ time_t t = nt_time_to_unix_abs((NTTIME *)&i1->min_password_age);
+ d_printf(_("%d seconds\n"), (int)t);
+ } else {
+ d_printf(_("not set\n"));
+ }
+
+ d_printf(_("Maximum password age: "));
+ if (nt_time_is_set((NTTIME *)&i1->max_password_age)) {
+ time_t t = nt_time_to_unix_abs((NTTIME *)&i1->max_password_age);
+ d_printf(_("%d seconds\n"), (int)t);
+ } else {
+ d_printf(_("not set\n"));
+ }
+
+ d_printf(_("Bad logon attempts: %d\n"), i12->lockout_threshold);
+
+ if (i12->lockout_threshold != 0) {
+
+ d_printf(_("Account lockout duration: "));
+ if (nt_time_is_set(&i12->lockout_duration)) {
+ time_t t = nt_time_to_unix_abs(&i12->lockout_duration);
+ d_printf(_("%d seconds\n"), (int)t);
+ } else {
+ d_printf(_("not set\n"));
+ }
+
+ d_printf(_("Bad password count reset after: "));
+ if (nt_time_is_set(&i12->lockout_window)) {
+ time_t t = nt_time_to_unix_abs(&i12->lockout_window);
+ d_printf(_("%d seconds\n"), (int)t);
+ } else {
+ d_printf(_("not set\n"));
+ }
+ }
+
+ d_printf(_("Disconnect users when logon hours expire: %s\n"),
+ nt_time_is_zero(&i3->force_logoff_time) ? _("yes") : _("no"));
+
+ d_printf(_("User must logon to change password: %s\n"),
+ (i1->password_properties & 0x2) ? _("yes") : _("no"));
+
+ return 0; /* Don't save */
+}
+
+static NTSTATUS rpc_sh_acct_pol_show(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv) {
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_show);
+}
+
+static int account_set_badpw(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, "%s %s <count>\n", _("Usage:"), ctx->whoami);
+ return -1;
+ }
+
+ i12->lockout_threshold = atoi(argv[0]);
+ d_printf(_("Setting bad password count to %d\n"),
+ i12->lockout_threshold);
+
+ return 12;
+}
+
+static NTSTATUS rpc_sh_acct_set_badpw(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_badpw);
+}
+
+static int account_set_lockduration(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ unix_to_nt_time_abs(&i12->lockout_duration, atoi(argv[0]));
+ d_printf(_("Setting lockout duration to %d seconds\n"),
+ (int)nt_time_to_unix_abs(&i12->lockout_duration));
+
+ return 12;
+}
+
+static NTSTATUS rpc_sh_acct_set_lockduration(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_lockduration);
+}
+
+static int account_set_resetduration(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ unix_to_nt_time_abs(&i12->lockout_window, atoi(argv[0]));
+ d_printf(_("Setting bad password reset duration to %d seconds\n"),
+ (int)nt_time_to_unix_abs(&i12->lockout_window));
+
+ return 12;
+}
+
+static NTSTATUS rpc_sh_acct_set_resetduration(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_resetduration);
+}
+
+static int account_set_minpwage(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ unix_to_nt_time_abs((NTTIME *)&i1->min_password_age, atoi(argv[0]));
+ d_printf(_("Setting minimum password age to %d seconds\n"),
+ (int)nt_time_to_unix_abs((NTTIME *)&i1->min_password_age));
+
+ return 1;
+}
+
+static NTSTATUS rpc_sh_acct_set_minpwage(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_minpwage);
+}
+
+static int account_set_maxpwage(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ unix_to_nt_time_abs((NTTIME *)&i1->max_password_age, atoi(argv[0]));
+ d_printf(_("Setting maximum password age to %d seconds\n"),
+ (int)nt_time_to_unix_abs((NTTIME *)&i1->max_password_age));
+
+ return 1;
+}
+
+static NTSTATUS rpc_sh_acct_set_maxpwage(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_maxpwage);
+}
+
+static int account_set_minpwlen(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ i1->min_password_length = atoi(argv[0]);
+ d_printf(_("Setting minimum password length to %d\n"),
+ i1->min_password_length);
+
+ return 1;
+}
+
+static NTSTATUS rpc_sh_acct_set_minpwlen(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_minpwlen);
+}
+
+static int account_set_pwhistlen(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct samr_DomInfo1 *i1,
+ struct samr_DomInfo3 *i3,
+ struct samr_DomInfo12 *i12,
+ int argc, const char **argv)
+{
+ if (argc != 1) {
+ d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami);
+ return -1;
+ }
+
+ i1->password_history_length = atoi(argv[0]);
+ d_printf(_("Setting password history length to %d\n"),
+ i1->password_history_length);
+
+ return 1;
+}
+
+static NTSTATUS rpc_sh_acct_set_pwhistlen(struct net_context *c,
+ TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv,
+ account_set_pwhistlen);
+}
+
+struct rpc_sh_cmd *net_rpc_acct_cmds(struct net_context *c, TALLOC_CTX *mem_ctx,
+ struct rpc_sh_ctx *ctx)
+{
+ static struct rpc_sh_cmd cmds[9] = {
+ { "show", NULL, &ndr_table_samr, rpc_sh_acct_pol_show,
+ N_("Show current account policy settings") },
+ { "badpw", NULL, &ndr_table_samr, rpc_sh_acct_set_badpw,
+ N_("Set bad password count before lockout") },
+ { "lockduration", NULL, &ndr_table_samr, rpc_sh_acct_set_lockduration,
+ N_("Set account lockout duration") },
+ { "resetduration", NULL, &ndr_table_samr,
+ rpc_sh_acct_set_resetduration,
+ N_("Set bad password count reset duration") },
+ { "minpwage", NULL, &ndr_table_samr, rpc_sh_acct_set_minpwage,
+ N_("Set minimum password age") },
+ { "maxpwage", NULL, &ndr_table_samr, rpc_sh_acct_set_maxpwage,
+ N_("Set maximum password age") },
+ { "minpwlen", NULL, &ndr_table_samr, rpc_sh_acct_set_minpwlen,
+ N_("Set minimum password length") },
+ { "pwhistlen", NULL, &ndr_table_samr, rpc_sh_acct_set_pwhistlen,
+ N_("Set the password history length") },
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ return cmds;
+}
diff --git a/source3/utils/net_rpc_shell.c b/source3/utils/net_rpc_shell.c
new file mode 100644
index 0000000..1ea7080
--- /dev/null
+++ b/source3/utils/net_rpc_shell.c
@@ -0,0 +1,306 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Shell around net rpc subcommands
+ * Copyright (C) Volker Lendecke 2006
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_samr.h"
+#include "lib/netapi/netapi.h"
+#include "lib/netapi/netapi_net.h"
+#include "../libcli/smbreadline/smbreadline.h"
+#include "libsmb/libsmb.h"
+#include "libcli/security/dom_sid.h"
+
+#include <popt.h>
+
+static NTSTATUS rpc_sh_info(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ int argc, const char **argv)
+{
+ return rpc_info_internals(c, ctx->domain_sid, ctx->domain_name,
+ ctx->cli, pipe_hnd, mem_ctx,
+ argc, argv);
+}
+
+static struct rpc_sh_ctx *this_ctx;
+
+static char **completion_fn(const char *text, int start, int end)
+{
+ char **cmds = NULL;
+ int n_cmds = 0;
+ struct rpc_sh_cmd *c;
+
+ if (start != 0) {
+ return NULL;
+ }
+
+ ADD_TO_ARRAY(NULL, char *, SMB_STRDUP(text), &cmds, &n_cmds);
+
+ for (c = this_ctx->cmds; c->name != NULL; c++) {
+ bool match = (strncmp(text, c->name, strlen(text)) == 0);
+
+ if (match) {
+ ADD_TO_ARRAY(NULL, char *, SMB_STRDUP(c->name),
+ &cmds, &n_cmds);
+ }
+ }
+
+ if (n_cmds == 2) {
+ SAFE_FREE(cmds[0]);
+ cmds[0] = cmds[1];
+ n_cmds -= 1;
+ }
+
+ ADD_TO_ARRAY(NULL, char *, NULL, &cmds, &n_cmds);
+ return cmds;
+}
+
+static NTSTATUS net_sh_run(struct net_context *c,
+ struct rpc_sh_ctx *ctx, struct rpc_sh_cmd *cmd,
+ int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ NTSTATUS status;
+
+ mem_ctx = talloc_new(ctx);
+ if (mem_ctx == NULL) {
+ d_fprintf(stderr, _("talloc_new failed\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = cli_rpc_pipe_open_noauth(ctx->cli, cmd->table,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not open pipe: %s\n"),
+ nt_errstr(status));
+ return status;
+ }
+
+ status = cmd->fn(c, mem_ctx, ctx, pipe_hnd, argc, argv);
+
+ TALLOC_FREE(pipe_hnd);
+
+ talloc_destroy(mem_ctx);
+
+ return status;
+}
+
+static bool net_sh_process(struct net_context *c,
+ struct rpc_sh_ctx *ctx,
+ int argc, const char **argv)
+{
+ struct rpc_sh_cmd *cmd;
+ struct rpc_sh_ctx *new_ctx;
+ NTSTATUS status;
+
+ if (argc == 0) {
+ return true;
+ }
+
+ if (ctx == this_ctx) {
+
+ /* We've been called from the cmd line */
+ if (strequal(argv[0], "..") &&
+ (this_ctx->parent != NULL)) {
+ new_ctx = this_ctx->parent;
+ TALLOC_FREE(this_ctx);
+ this_ctx = new_ctx;
+ return true;
+ }
+ }
+
+ if (strequal(argv[0], "exit") ||
+ strequal(argv[0], "quit") ||
+ strequal(argv[0], "q")) {
+ return false;
+ }
+
+ if (strequal(argv[0], "help") || strequal(argv[0], "?")) {
+ for (cmd = ctx->cmds; cmd->name != NULL; cmd++) {
+ if (ctx != this_ctx) {
+ d_printf("%s ", ctx->whoami);
+ }
+ d_printf("%-15s %s\n", cmd->name, cmd->help);
+ }
+ return true;
+ }
+
+ for (cmd = ctx->cmds; cmd->name != NULL; cmd++) {
+ if (strequal(cmd->name, argv[0])) {
+ break;
+ }
+ }
+
+ if (cmd->name == NULL) {
+ /* None found */
+ d_fprintf(stderr,_( "%s: unknown cmd\n"), argv[0]);
+ return true;
+ }
+
+ new_ctx = talloc(ctx, struct rpc_sh_ctx);
+ if (new_ctx == NULL) {
+ d_fprintf(stderr, _("talloc failed\n"));
+ return false;
+ }
+ new_ctx->cli = ctx->cli;
+ new_ctx->whoami = talloc_asprintf(new_ctx, "%s %s",
+ ctx->whoami, cmd->name);
+ new_ctx->thiscmd = talloc_strdup(new_ctx, cmd->name);
+
+ if (cmd->sub != NULL) {
+ new_ctx->cmds = cmd->sub(c, new_ctx, ctx);
+ } else {
+ new_ctx->cmds = NULL;
+ }
+
+ new_ctx->parent = ctx;
+ new_ctx->domain_name = ctx->domain_name;
+ new_ctx->domain_sid = ctx->domain_sid;
+
+ argc -= 1;
+ argv += 1;
+
+ if (cmd->sub != NULL) {
+ if (argc == 0) {
+ this_ctx = new_ctx;
+ return true;
+ }
+ return net_sh_process(c, new_ctx, argc, argv);
+ }
+
+ status = net_sh_run(c, new_ctx, cmd, argc, argv);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("%s failed: %s\n"), new_ctx->whoami,
+ nt_errstr(status));
+ }
+
+ return true;
+}
+
+static struct rpc_sh_cmd sh_cmds[6] = {
+
+ { "info", NULL, &ndr_table_samr, rpc_sh_info,
+ N_("Print information about the domain connected to") },
+
+ { "rights", net_rpc_rights_cmds, 0, NULL,
+ N_("List/Grant/Revoke user rights") },
+
+ { "share", net_rpc_share_cmds, 0, NULL,
+ N_("List/Add/Remove etc shares") },
+
+ { "user", net_rpc_user_cmds, 0, NULL,
+ N_("List/Add/Remove user info") },
+
+ { "account", net_rpc_acct_cmds, 0, NULL,
+ N_("Show/Change account policy settings") },
+
+ { NULL, NULL, 0, NULL, NULL }
+};
+
+int net_rpc_shell(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct rpc_sh_ctx *ctx;
+ struct dom_sid_buf buf;
+ NET_API_STATUS net_api_status;
+
+ if (argc != 0 || c->display_usage) {
+ d_printf("%s\nnet rpc shell\n", _("Usage:"));
+ return -1;
+ }
+
+ net_api_status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds);
+ if (net_api_status != 0) {
+ return -1;
+ }
+
+ ctx = talloc(NULL, struct rpc_sh_ctx);
+ if (ctx == NULL) {
+ d_fprintf(stderr, _("talloc failed\n"));
+ return -1;
+ }
+
+ status = net_make_ipc_connection(c, 0, &(ctx->cli));
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not open connection: %s\n"),
+ nt_errstr(status));
+ return -1;
+ }
+
+ ctx->cmds = sh_cmds;
+ ctx->whoami = "net rpc";
+ ctx->parent = NULL;
+
+ status = net_get_remote_domain_sid(ctx->cli, ctx, &ctx->domain_sid,
+ &ctx->domain_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return -1;
+ }
+
+ d_printf(_("Talking to domain %s (%s)\n"), ctx->domain_name,
+ dom_sid_str_buf(ctx->domain_sid, &buf));
+
+ this_ctx = ctx;
+
+ while(1) {
+ char *prompt = NULL;
+ char *line = NULL;
+ int ret;
+
+ if (asprintf(&prompt, "%s> ", this_ctx->whoami) < 0) {
+ break;
+ }
+
+ line = smb_readline(prompt, NULL, completion_fn);
+ SAFE_FREE(prompt);
+
+ if (line == NULL) {
+ break;
+ }
+
+ ret = poptParseArgvString(line, &argc, &argv);
+ if (ret == POPT_ERROR_NOARG) {
+ SAFE_FREE(line);
+ continue;
+ }
+ if (ret != 0) {
+ d_fprintf(stderr, _("cmdline invalid: %s\n"),
+ poptStrerror(ret));
+ SAFE_FREE(line);
+ return false;
+ }
+
+ if ((line[0] != '\n') &&
+ (!net_sh_process(c, this_ctx, argc, argv))) {
+ SAFE_FREE(line);
+ break;
+ }
+ SAFE_FREE(line);
+ }
+
+ cli_shutdown(ctx->cli);
+
+ TALLOC_FREE(ctx);
+
+ return 0;
+}
diff --git a/source3/utils/net_rpc_trust.c b/source3/utils/net_rpc_trust.c
new file mode 100644
index 0000000..a3354ad
--- /dev/null
+++ b/source3/utils/net_rpc_trust.c
@@ -0,0 +1,735 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2011 Sumit Bose (sbose@redhat.com)
+
+ 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "librpc/gen_ndr/ndr_drsblobs.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "../libcli/security/dom_sid.h"
+#include "libsmb/libsmb.h"
+
+#include "lib/crypto/gnutls_helpers.h"
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#define ARG_OTHERSERVER "otherserver="
+#define ARG_OTHERUSER "otheruser="
+#define ARG_OTHERDOMAINSID "otherdomainsid="
+#define ARG_OTHERDOMAIN "otherdomain="
+#define ARG_OTHERNETBIOSDOMAIN "other_netbios_domain="
+#define ARG_TRUSTPW "trustpw="
+
+enum trust_op {
+ TRUST_CREATE,
+ TRUST_DELETE
+};
+
+struct other_dom_data {
+ char *host;
+ char *user_name;
+ char *domain_sid_str;
+ char *dns_domain_name;
+ char *domain_name;
+};
+
+struct dom_data {
+ struct dom_sid *domsid;
+ char *dns_domain_name;
+ char *domain_name;
+};
+
+static NTSTATUS close_handle(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bind_hnd,
+ struct policy_handle *pol_hnd)
+{
+ NTSTATUS status;
+ NTSTATUS result;
+
+ status = dcerpc_lsa_Close(bind_hnd, mem_ctx, pol_hnd, &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_lsa_Close failed with error [%s].\n",
+ nt_errstr(status)));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("lsa close failed with error [%s].\n",
+ nt_errstr(result)));
+ return result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS delete_trust(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bind_hnd,
+ struct policy_handle *pol_hnd,
+ struct dom_sid *domsid)
+{
+ NTSTATUS status;
+ struct lsa_DeleteTrustedDomain dr;
+
+ dr.in.handle = pol_hnd;
+ dr.in.dom_sid = domsid;
+
+ status = dcerpc_lsa_DeleteTrustedDomain_r(bind_hnd, mem_ctx, &dr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_lsa_DeleteTrustedDomain_r failed with [%s]\n",
+ nt_errstr(status)));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(dr.out.result)) {
+ DEBUG(0, ("DeleteTrustedDomain returned [%s]\n",
+ nt_errstr(dr.out.result)));
+ return dr.out.result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS create_trust(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bind_hnd,
+ struct policy_handle *pol_hnd,
+ const char *trust_name,
+ const char *trust_name_dns,
+ struct dom_sid *domsid,
+ struct lsa_TrustDomainInfoAuthInfoInternal *authinfo)
+{
+ NTSTATUS status;
+ struct lsa_CreateTrustedDomainEx2 r;
+ struct lsa_TrustDomainInfoInfoEx trustinfo;
+ struct policy_handle trustdom_handle;
+ bool is_nt4 = trust_name_dns == NULL;
+
+ if (!is_nt4) {
+ fprintf(stdout, "Creating AD trust\n");
+ trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL;
+ trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE;
+ } else {
+ fprintf(stdout, "Creating NT4 trust\n");
+ trustinfo.trust_type = LSA_TRUST_TYPE_DOWNLEVEL;
+ trustinfo.trust_attributes = 0;
+ trust_name_dns = trust_name;
+ }
+
+ trustinfo.sid = domsid;
+ trustinfo.netbios_name.string = trust_name;
+ trustinfo.domain_name.string = trust_name_dns;
+
+ trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND |
+ LSA_TRUST_DIRECTION_OUTBOUND;
+
+ r.in.policy_handle = pol_hnd;
+ r.in.info = &trustinfo;
+ r.in.auth_info_internal = authinfo;
+ r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH |
+ LSA_TRUSTED_QUERY_DOMAIN_NAME;
+ r.out.trustdom_handle = &trustdom_handle;
+
+ status = dcerpc_lsa_CreateTrustedDomainEx2_r(bind_hnd, mem_ctx, &r);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_lsa_CreateTrustedDomainEx2_r failed "
+ "with error [%s].\n", nt_errstr(status)));
+ return status;
+ }
+ if (!NT_STATUS_IS_OK(r.out.result)) {
+ DEBUG(0, ("CreateTrustedDomainEx2_r returned [%s].\n",
+ nt_errstr(r.out.result)));
+ return r.out.result;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS get_domain_info(TALLOC_CTX *mem_ctx,
+ struct dcerpc_binding_handle *bind_hdn,
+ struct policy_handle *pol_hnd,
+ struct dom_data *dom_data)
+{
+ NTSTATUS status;
+ struct lsa_QueryInfoPolicy2 qr;
+ struct dom_sid_buf buf;
+
+ qr.in.handle = pol_hnd;
+ qr.in.level = LSA_POLICY_INFO_DNS;
+
+ status = dcerpc_lsa_QueryInfoPolicy2_r(bind_hdn, mem_ctx, &qr);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("dcerpc_lsa_QueryInfoPolicy2_r failed "
+ "with error [%s].\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (!NT_STATUS_IS_OK(qr.out.result)) {
+ DEBUG(0, ("QueryInfoPolicy2 returned [%s].\n",
+ nt_errstr(qr.out.result)));
+ return qr.out.result;
+ }
+
+ dom_data->domain_name = talloc_strdup(mem_ctx,
+ (*qr.out.info)->dns.name.string);
+ dom_data->dns_domain_name = talloc_strdup(mem_ctx,
+ (*qr.out.info)->dns.dns_domain.string);
+ dom_data->domsid = dom_sid_dup(mem_ctx, (*qr.out.info)->dns.sid);
+ if (dom_data->domain_name == NULL ||
+ dom_data->dns_domain_name == NULL ||
+ dom_data->domsid == NULL) {
+ DEBUG(0, ("Copying domain data failed.\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ DEBUG(0, ("Got the following domain info [%s][%s][%s].\n",
+ dom_data->domain_name, dom_data->dns_domain_name,
+ dom_sid_str_buf(dom_data->domsid, &buf)));
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS connect_and_get_info(TALLOC_CTX *mem_ctx,
+ struct net_context *net_ctx,
+ struct cli_state **cli,
+ struct rpc_pipe_client **pipe_hnd,
+ struct policy_handle *pol_hnd,
+ struct dom_data *dom_data,
+ DATA_BLOB *session_key)
+{
+ NTSTATUS status;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32_t out_version = 0;
+ union lsa_revision_info out_revision_info = {
+ .info1 = {
+ .revision = 0,
+ },
+ };
+
+ status = net_make_ipc_connection_ex(net_ctx, NULL, NULL, NULL,
+ NET_FLAGS_PDC, cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to connect to [%s] with error [%s]\n",
+ net_ctx->opt_host, nt_errstr(status)));
+ return status;
+ }
+
+ status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc, pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to initialise lsa pipe with error [%s]\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = dcerpc_lsa_open_policy_fallback(
+ (*pipe_hnd)->binding_handle,
+ mem_ctx,
+ (*pipe_hnd)->srv_name_slash,
+ false,
+ LSA_POLICY_VIEW_LOCAL_INFORMATION |
+ LSA_POLICY_TRUST_ADMIN |
+ LSA_POLICY_CREATE_SECRET,
+ &out_version,
+ &out_revision_info,
+ pol_hnd,
+ &result);
+ if (any_nt_status_not_ok(status, result, &status)) {
+ DBG_ERR("Failed to open policy handle: %s\n",
+ nt_errstr(result));
+ return status;
+ }
+
+ status = get_domain_info(mem_ctx, (*pipe_hnd)->binding_handle,
+ pol_hnd, dom_data);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("get_domain_info failed with error [%s].\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ status = cli_get_session_key(mem_ctx, *pipe_hnd, session_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Error getting session_key of LSA pipe. Error was %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx,
+ const char *password,
+ DATA_BLOB *auth_blob)
+{
+ struct trustDomainPasswords auth_struct;
+ struct AuthenticationInformation *auth_info_array;
+ enum ndr_err_code ndr_err;
+ size_t converted_size;
+
+ generate_random_buffer(auth_struct.confounder,
+ sizeof(auth_struct.confounder));
+
+ auth_info_array = talloc_array(mem_ctx,
+ struct AuthenticationInformation, 1);
+ if (auth_info_array == NULL) {
+ return false;
+ }
+
+ auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR;
+ if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password,
+ strlen(password),
+ &auth_info_array[0].AuthInfo.clear.password,
+ &converted_size)) {
+ return false;
+ }
+
+ auth_info_array[0].AuthInfo.clear.size = converted_size;
+
+ auth_struct.outgoing.count = 1;
+ auth_struct.outgoing.current.count = 1;
+ auth_struct.outgoing.current.array = auth_info_array;
+ auth_struct.outgoing.previous.count = 0;
+ auth_struct.outgoing.previous.array = NULL;
+
+ auth_struct.incoming.count = 1;
+ auth_struct.incoming.current.count = 1;
+ auth_struct.incoming.current.array = auth_info_array;
+ auth_struct.incoming.previous.count = 0;
+ auth_struct.incoming.previous.array = NULL;
+
+ ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct,
+ (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return false;
+ }
+
+ return true;
+}
+
+static int parse_trust_args(TALLOC_CTX *mem_ctx, int argc, const char **argv, struct other_dom_data **_o, char **_trustpw)
+{
+ size_t c;
+ struct other_dom_data *o = NULL;
+ char *trustpw = NULL;
+ int ret = EFAULT;
+
+ if (argc == 0) {
+ return EINVAL;
+ }
+
+ o = talloc_zero(mem_ctx, struct other_dom_data);
+ if (o == NULL) {
+ DEBUG(0, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ for (c = 0; c < argc; c++) {
+ if (strnequal(argv[c], ARG_OTHERSERVER, sizeof(ARG_OTHERSERVER)-1)) {
+ o->host = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERSERVER)-1);
+ if (o->host == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else if (strnequal(argv[c], ARG_OTHERUSER, sizeof(ARG_OTHERUSER)-1)) {
+ o->user_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERUSER)-1);
+ if (o->user_name == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else if (strnequal(argv[c], ARG_OTHERDOMAINSID, sizeof(ARG_OTHERDOMAINSID)-1)) {
+ o->domain_sid_str = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAINSID)-1);
+ if (o->domain_sid_str == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else if (strnequal(argv[c], ARG_OTHERDOMAIN, sizeof(ARG_OTHERDOMAIN)-1)) {
+ o->dns_domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAIN)-1);
+ if (o->dns_domain_name == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else if (strnequal(argv[c], ARG_OTHERNETBIOSDOMAIN, sizeof(ARG_OTHERNETBIOSDOMAIN)-1)) {
+ o->domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERNETBIOSDOMAIN)-1);
+ if (o->domain_name == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else if (strnequal(argv[c], ARG_TRUSTPW, sizeof(ARG_TRUSTPW)-1)) {
+ trustpw = talloc_strdup(mem_ctx, argv[c] + sizeof(ARG_TRUSTPW)-1);
+ if (trustpw == NULL) {
+ ret = ENOMEM;
+ goto failed;
+ }
+ } else {
+ DEBUG(0, ("Unsupported option [%s].\n", argv[c]));
+ ret = EINVAL;
+ goto failed;
+ }
+ }
+
+ *_o = o;
+ *_trustpw = trustpw;
+
+ return 0;
+
+failed:
+ talloc_free(o);
+ talloc_free(trustpw);
+ return ret;
+}
+
+static void print_trust_delete_usage(void)
+{
+ d_printf( "%s\n"
+ "net rpc trust delete [options]\n"
+ "\nOptions:\n"
+ "\totherserver=DC in other domain\n"
+ "\totheruser=Admin user in other domain\n"
+ "\totherdomainsid=SID of other domain\n"
+ "\nExamples:\n"
+ "\tnet rpc trust delete otherserver=oname otheruser=ouser -S lname -U luser\n"
+ "\tnet rpc trust delete otherdomainsid=S-... -S lname -U luser\n"
+ " %s\n",
+ _("Usage:"),
+ _("Remove trust between two domains"));
+}
+
+static void print_trust_usage(void)
+{
+ d_printf( "%s\n"
+ "net rpc trust create [options]\n"
+ "\nOptions:\n"
+ "\totherserver=DC in other domain\n"
+ "\totheruser=Admin user in other domain\n"
+ "\totherdomainsid=SID of other domain\n"
+ "\tother_netbios_domain=NetBIOS/short name of other domain\n"
+ "\totherdomain=Full/DNS name of other domain (if not used, create an NT4 trust)\n"
+ "\ttrustpw=Trust password\n"
+ "\nExamples:\n"
+ "\tnet rpc trust create otherserver=oname otheruser=ouser -S lname -U luser\n"
+ "\tnet rpc trust create otherdomainsid=S-... other_netbios_domain=odom otherdomain=odom.org trustpw=secret -S lname -U luser\n"
+ " %s\n",
+ _("Usage:"),
+ _("Create trust between two domains"));
+}
+
+static int rpc_trust_common(struct net_context *net_ctx, int argc,
+ const char **argv, enum trust_op op)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ int ret;
+ int success = -1;
+ struct cli_state *cli[2] = {NULL, NULL};
+ struct rpc_pipe_client *pipe_hnd[2] = {NULL, NULL};
+ DATA_BLOB session_key[2];
+ struct policy_handle pol_hnd[2];
+ struct lsa_TrustDomainInfoAuthInfoInternal authinfo;
+ DATA_BLOB auth_blob;
+ char *trust_pw = NULL;
+ struct other_dom_data *other_dom_data;
+ struct net_context *other_net_ctx = NULL;
+ struct dom_data dom_data[2];
+ void (*usage)(void);
+
+ ZERO_STRUCT(session_key);
+
+ switch (op) {
+ case TRUST_CREATE:
+ usage = print_trust_usage;
+ break;
+ case TRUST_DELETE:
+ usage = print_trust_delete_usage;
+ break;
+ default:
+ DEBUG(0, ("Unsupported trust operation.\n"));
+ return -1;
+ }
+
+ if (net_ctx->display_usage) {
+ usage();
+ return 0;
+ }
+
+ mem_ctx = talloc_init("trust op");
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_init failed.\n"));
+ return -1;
+ }
+
+ ret = parse_trust_args(mem_ctx, argc, argv, &other_dom_data, &trust_pw);
+ if (ret != 0) {
+ if (ret == EINVAL) {
+ usage();
+ } else {
+ DEBUG(0, ("Failed to parse arguments.\n"));
+ }
+ goto done;
+ }
+
+ if (other_dom_data->host != 0) {
+ other_net_ctx = talloc_zero(other_dom_data, struct net_context);
+ if (other_net_ctx == NULL) {
+ DEBUG(0, ("talloc_zero failed.\n"));
+ goto done;
+ }
+
+ other_net_ctx->opt_host = other_dom_data->host;
+ other_net_ctx->creds = cli_credentials_init(other_net_ctx);
+ cli_credentials_parse_string(other_net_ctx->creds,
+ other_dom_data->user_name,
+ CRED_SPECIFIED);
+ } else {
+ dom_data[1].domsid = dom_sid_parse_talloc(mem_ctx,
+ other_dom_data->domain_sid_str);
+ dom_data[1].domain_name = other_dom_data->domain_name;
+ dom_data[1].dns_domain_name = other_dom_data->dns_domain_name;
+
+ if (dom_data[1].dns_domain_name == NULL) {
+ fprintf(stdout, "No DNS domain name passed, "
+ "assuming NT4 trust!\n");
+ }
+
+ if (dom_data[1].domsid == NULL ||
+ (op == TRUST_CREATE &&
+ (dom_data[1].domain_name == NULL))) {
+ DEBUG(0, ("Missing required argument.\n"));
+ usage();
+ goto done;
+ }
+ }
+
+ status = connect_and_get_info(mem_ctx, net_ctx, &cli[0], &pipe_hnd[0],
+ &pol_hnd[0], &dom_data[0], &session_key[0]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ if (other_net_ctx != NULL) {
+ status = connect_and_get_info(mem_ctx, other_net_ctx,
+ &cli[1], &pipe_hnd[1],
+ &pol_hnd[1], &dom_data[1],
+ &session_key[1]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("connect_and_get_info failed with error [%s]\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ }
+
+ if (op == TRUST_CREATE) {
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t enc_session_key = {
+ .data = session_key[0].data,
+ .size = session_key[0].length,
+ };
+ int rc;
+
+ if (trust_pw == NULL) {
+ if (other_net_ctx == NULL) {
+ DEBUG(0, ("Missing either trustpw or otherhost.\n"));
+ goto done;
+ }
+
+ DEBUG(0, ("Using random trust password.\n"));
+ trust_pw = trust_pw_new_value(mem_ctx,
+ SEC_CHAN_DOMAIN,
+ SEC_DOMAIN);
+ if (trust_pw == NULL) {
+ DEBUG(0, ("generate_random_password failed.\n"));
+ goto done;
+ }
+ } else {
+ DEBUG(0, ("Using user provided password.\n"));
+ }
+
+ if (!get_trust_domain_passwords_auth_blob(mem_ctx, trust_pw,
+ &auth_blob)) {
+ DEBUG(0, ("get_trust_domain_passwords_auth_blob failed\n"));
+ goto done;
+ }
+
+ authinfo.auth_blob.data = (uint8_t *)talloc_memdup(
+ mem_ctx,
+ auth_blob.data,
+ auth_blob.length);
+ if (authinfo.auth_blob.data == NULL) {
+ goto done;
+ }
+ authinfo.auth_blob.size = auth_blob.length;
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &enc_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ authinfo.auth_blob.data,
+ authinfo.auth_blob.size);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ status = create_trust(mem_ctx, pipe_hnd[0]->binding_handle,
+ &pol_hnd[0],
+ dom_data[1].domain_name,
+ dom_data[1].dns_domain_name,
+ dom_data[1].domsid,
+ &authinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("create_trust failed with error [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ if (other_net_ctx != NULL) {
+ talloc_free(authinfo.auth_blob.data);
+ authinfo.auth_blob.data = (uint8_t *)talloc_memdup(
+ mem_ctx,
+ auth_blob.data,
+ auth_blob.length);
+ if (authinfo.auth_blob.data == NULL) {
+ goto done;
+ }
+ authinfo.auth_blob.size = auth_blob.length;
+
+ enc_session_key = (gnutls_datum_t) {
+ .data = session_key[1].data,
+ .size = session_key[1].length,
+ };
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &enc_session_key,
+ NULL);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ authinfo.auth_blob.data,
+ authinfo.auth_blob.size);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID);
+ goto done;
+ }
+
+ status = create_trust(mem_ctx,
+ pipe_hnd[1]->binding_handle,
+ &pol_hnd[1],
+ dom_data[0].domain_name,
+ dom_data[0].dns_domain_name,
+ dom_data[0].domsid, &authinfo);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("create_trust failed with error [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ }
+ } else if (op == TRUST_DELETE) {
+ status = delete_trust(mem_ctx, pipe_hnd[0]->binding_handle,
+ &pol_hnd[0], dom_data[1].domsid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("delete_trust failed with [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ if (other_net_ctx != NULL) {
+ status = delete_trust(mem_ctx,
+ pipe_hnd[1]->binding_handle,
+ &pol_hnd[1], dom_data[0].domsid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("delete_trust failed with [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ }
+ }
+
+ status = close_handle(mem_ctx, pipe_hnd[0]->binding_handle,
+ &pol_hnd[0]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("close_handle failed with error [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+
+ if (other_net_ctx != NULL) {
+ status = close_handle(mem_ctx, pipe_hnd[1]->binding_handle,
+ &pol_hnd[1]);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("close_handle failed with error [%s].\n",
+ nt_errstr(status)));
+ goto done;
+ }
+ }
+
+ success = 0;
+
+done:
+ data_blob_clear_free(&session_key[0]);
+ data_blob_clear_free(&session_key[1]);
+ cli_shutdown(cli[0]);
+ cli_shutdown(cli[1]);
+ talloc_destroy(mem_ctx);
+ return success;
+}
+
+static int rpc_trust_create(struct net_context *net_ctx, int argc,
+ const char **argv)
+{
+ return rpc_trust_common(net_ctx, argc, argv, TRUST_CREATE);
+}
+
+static int rpc_trust_delete(struct net_context *net_ctx, int argc,
+ const char **argv)
+{
+ return rpc_trust_common(net_ctx, argc, argv, TRUST_DELETE);
+}
+
+int net_rpc_trust(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "create",
+ rpc_trust_create,
+ NET_TRANSPORT_RPC,
+ N_("Create trusts"),
+ N_("net rpc trust create\n"
+ " Create trusts")
+ },
+ {
+ "delete",
+ rpc_trust_delete,
+ NET_TRANSPORT_RPC,
+ N_("Remove trusts"),
+ N_("net rpc trust delete\n"
+ " Remove trusts")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net rpc trust", func);
+}
diff --git a/source3/utils/net_sam.c b/source3/utils/net_sam.c
new file mode 100644
index 0000000..5c1e007
--- /dev/null
+++ b/source3/utils/net_sam.c
@@ -0,0 +1,2308 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Local SAM access routines
+ * Copyright (C) Volker Lendecke 2006
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "utils/net.h"
+#include "../librpc/gen_ndr/samr.h"
+#include "smbldap.h"
+#include "../libcli/security/security.h"
+#include "lib/winbind_util.h"
+#include "passdb.h"
+#include "passdb/pdb_ldap_util.h"
+#include "passdb/pdb_ldap_schema.h"
+#include "lib/privileges.h"
+#include "secrets.h"
+#include "idmap.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/util/string_wrappers.h"
+#include "source3/lib/substitute.h"
+
+/*
+ * Set a user's data
+ */
+
+static int net_sam_userset(struct net_context *c, int argc, const char **argv,
+ const char *field,
+ bool (*fn)(struct samu *, const char *,
+ enum pdb_value_state))
+{
+ struct samu *sam_acct = NULL;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n", _("Usage:"));
+ d_fprintf(stderr, _("net sam set %s <user> <value>\n"),
+ field);
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (type != SID_NAME_USER) {
+ d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0],
+ sid_type_lookup(type));
+ return -1;
+ }
+
+ if ( !(sam_acct = samu_new( NULL )) ) {
+ d_fprintf(stderr, _("Internal error\n"));
+ return -1;
+ }
+
+ if (!pdb_getsampwsid(sam_acct, &sid)) {
+ d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]);
+ return -1;
+ }
+
+ if (!fn(sam_acct, argv[1], PDB_CHANGED)) {
+ d_fprintf(stderr, _("Internal error\n"));
+ return -1;
+ }
+
+ status = pdb_update_sam_account(sam_acct);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Updating sam account %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ TALLOC_FREE(sam_acct);
+
+ d_printf(_("Updated %s for %s\\%s to %s\n"), field, dom, name, argv[1]);
+ return 0;
+}
+
+static int net_sam_set_fullname(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "fullname",
+ pdb_set_fullname);
+}
+
+static int net_sam_set_logonscript(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "logonscript",
+ pdb_set_logon_script);
+}
+
+static int net_sam_set_profilepath(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "profilepath",
+ pdb_set_profile_path);
+}
+
+static int net_sam_set_homedrive(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "homedrive",
+ pdb_set_dir_drive);
+}
+
+static int net_sam_set_homedir(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "homedir",
+ pdb_set_homedir);
+}
+
+static int net_sam_set_workstations(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_userset(c, argc, argv, "workstations",
+ pdb_set_workstations);
+}
+
+/*
+ * Set account flags
+ */
+
+static int net_sam_set_userflag(struct net_context *c, int argc,
+ const char **argv, const char *field,
+ uint16_t flag)
+{
+ struct samu *sam_acct = NULL;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+ uint32_t acct_flags;
+
+ if ((argc != 2) || c->display_usage ||
+ (!strequal(argv[1], "yes") &&
+ !strequal(argv[1], "no"))) {
+ d_fprintf(stderr, "%s\n", _("Usage:"));
+ d_fprintf(stderr, _("net sam set %s <user> [yes|no]\n"),
+ field);
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (type != SID_NAME_USER) {
+ d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0],
+ sid_type_lookup(type));
+ return -1;
+ }
+
+ if ( !(sam_acct = samu_new( NULL )) ) {
+ d_fprintf(stderr, _("Internal error\n"));
+ return -1;
+ }
+
+ if (!pdb_getsampwsid(sam_acct, &sid)) {
+ d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]);
+ return -1;
+ }
+
+ acct_flags = pdb_get_acct_ctrl(sam_acct);
+
+ if (strequal(argv[1], "yes")) {
+ acct_flags |= flag;
+ } else {
+ acct_flags &= ~flag;
+ }
+
+ pdb_set_acct_ctrl(sam_acct, acct_flags, PDB_CHANGED);
+
+ status = pdb_update_sam_account(sam_acct);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Updating sam account %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ TALLOC_FREE(sam_acct);
+
+ d_fprintf(stderr, _("Updated flag %s for %s\\%s to %s\n"), field, dom,
+ name, argv[1]);
+ return 0;
+}
+
+static int net_sam_set_disabled(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_set_userflag(c, argc, argv, "disabled", ACB_DISABLED);
+}
+
+static int net_sam_set_pwnotreq(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_set_userflag(c, argc, argv, "pwnotreq", ACB_PWNOTREQ);
+}
+
+static int net_sam_set_autolock(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_set_userflag(c, argc, argv, "autolock", ACB_AUTOLOCK);
+}
+
+static int net_sam_set_pwnoexp(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_set_userflag(c, argc, argv, "pwnoexp", ACB_PWNOEXP);
+}
+
+/*
+ * Set pass last change time, based on force pass change now
+ */
+
+static int net_sam_set_pwdmustchangenow(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct samu *sam_acct = NULL;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if ((argc != 2) || c->display_usage ||
+ (!strequal(argv[1], "yes") &&
+ !strequal(argv[1], "no"))) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam set pwdmustchangenow <user> [yes|no]\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (type != SID_NAME_USER) {
+ d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0],
+ sid_type_lookup(type));
+ return -1;
+ }
+
+ if ( !(sam_acct = samu_new( NULL )) ) {
+ d_fprintf(stderr, _("Internal error\n"));
+ return -1;
+ }
+
+ if (!pdb_getsampwsid(sam_acct, &sid)) {
+ d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]);
+ return -1;
+ }
+
+ if (strequal(argv[1], "yes")) {
+ pdb_set_pass_last_set_time(sam_acct, 0, PDB_CHANGED);
+ } else {
+ pdb_set_pass_last_set_time(sam_acct, time(NULL), PDB_CHANGED);
+ }
+
+ status = pdb_update_sam_account(sam_acct);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Updating sam account %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ TALLOC_FREE(sam_acct);
+
+ d_fprintf(stderr, _("Updated 'user must change password at next logon' "
+ "for %s\\%s to %s\n"), dom,
+ name, argv[1]);
+ return 0;
+}
+
+
+/*
+ * Set a user's or a group's comment
+ */
+
+static int net_sam_set_comment(struct net_context *c, int argc,
+ const char **argv)
+{
+ GROUP_MAP *map;
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam set comment <name> <comment>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (type == SID_NAME_USER) {
+ return net_sam_userset(c, argc, argv, "comment",
+ pdb_set_acct_desc);
+ }
+
+ if ((type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) &&
+ (type != SID_NAME_WKN_GRP)) {
+ d_fprintf(stderr, _("%s is a %s, not a group\n"), argv[0],
+ sid_type_lookup(type));
+ return -1;
+ }
+
+ map = talloc_zero(talloc_tos(), GROUP_MAP);
+ if (!map) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ return -1;
+ }
+
+ if (!pdb_getgrsid(map, sid)) {
+ d_fprintf(stderr, _("Could not load group %s\n"), argv[0]);
+ return -1;
+ }
+
+ map->comment = talloc_strdup(map, argv[1]);
+ if (!map->comment) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ return -1;
+ }
+
+ status = pdb_update_group_mapping_entry(map);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Updating group mapping entry failed with "
+ "%s\n"), nt_errstr(status));
+ return -1;
+ }
+
+ d_printf("Updated comment of group %s\\%s to %s\n", dom, name,
+ argv[1]);
+
+ TALLOC_FREE(map);
+ return 0;
+}
+
+static int net_sam_set(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "homedir",
+ net_sam_set_homedir,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's home directory"),
+ N_("net sam set homedir\n"
+ " Change a user's home directory")
+ },
+ {
+ "profilepath",
+ net_sam_set_profilepath,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's profile path"),
+ N_("net sam set profilepath\n"
+ " Change a user's profile path")
+ },
+ {
+ "comment",
+ net_sam_set_comment,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a users or groups description"),
+ N_("net sam set comment\n"
+ " Change a users or groups description")
+ },
+ {
+ "fullname",
+ net_sam_set_fullname,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's full name"),
+ N_("net sam set fullname\n"
+ " Change a user's full name")
+ },
+ {
+ "logonscript",
+ net_sam_set_logonscript,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's logon script"),
+ N_("net sam set logonscript\n"
+ " Change a user's logon script")
+ },
+ {
+ "homedrive",
+ net_sam_set_homedrive,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's home drive"),
+ N_("net sam set homedrive\n"
+ " Change a user's home drive")
+ },
+ {
+ "workstations",
+ net_sam_set_workstations,
+ NET_TRANSPORT_LOCAL,
+ N_("Change a user's allowed workstations"),
+ N_("net sam set workstations\n"
+ " Change a user's allowed workstations")
+ },
+ {
+ "disabled",
+ net_sam_set_disabled,
+ NET_TRANSPORT_LOCAL,
+ N_("Disable/Enable a user"),
+ N_("net sam set disable\n"
+ " Disable/Enable a user")
+ },
+ {
+ "pwnotreq",
+ net_sam_set_pwnotreq,
+ NET_TRANSPORT_LOCAL,
+ N_("Disable/Enable the password not required flag"),
+ N_("net sam set pwnotreq\n"
+ " Disable/Enable the password not required flag")
+ },
+ {
+ "autolock",
+ net_sam_set_autolock,
+ NET_TRANSPORT_LOCAL,
+ N_("Disable/Enable a user's lockout flag"),
+ N_("net sam set autolock\n"
+ " Disable/Enable a user's lockout flag")
+ },
+ {
+ "pwnoexp",
+ net_sam_set_pwnoexp,
+ NET_TRANSPORT_LOCAL,
+ N_("Disable/Enable whether a user's pw does not "
+ "expire"),
+ N_("net sam set pwnoexp\n"
+ " Disable/Enable whether a user's pw does not "
+ "expire")
+ },
+ {
+ "pwdmustchangenow",
+ net_sam_set_pwdmustchangenow,
+ NET_TRANSPORT_LOCAL,
+ N_("Force users password must change at next logon"),
+ N_("net sam set pwdmustchangenow\n"
+ " Force users password must change at next logon")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net sam set", func);
+}
+
+/*
+ * Manage account policies
+ */
+
+static int net_sam_policy_set(struct net_context *c, int argc, const char **argv)
+{
+ const char *account_policy = NULL;
+ uint32_t value = 0;
+ uint32_t old_value = 0;
+ enum pdb_policy_type field;
+ int err = 0;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam policy set \"<account policy>\" <value>\n"));
+ return -1;
+ }
+
+ account_policy = argv[0];
+ field = account_policy_name_to_typenum(account_policy);
+
+ if (strequal(argv[1], "forever") || strequal(argv[1], "never")
+ || strequal(argv[1], "off")) {
+ value = -1;
+ }
+ else {
+ value = smb_strtoul(argv[1],
+ NULL,
+ 10,
+ &err,
+ SMB_STR_FULL_STR_CONV);
+
+ if (err != 0) {
+ d_printf(_("Unable to set policy \"%s\"! Invalid value "
+ "\"%s\".\n"),
+ account_policy, argv[1]);
+ return -1;
+ }
+ }
+
+ if (field == 0) {
+ const char **names;
+ int i, count;
+
+ account_policy_names_list(talloc_tos(), &names, &count);
+ d_fprintf(stderr, _("No account policy \"%s\"!\n\n"), argv[0]);
+ d_fprintf(stderr, _("Valid account policies are:\n"));
+
+ for (i=0; i<count; i++) {
+ d_fprintf(stderr, "%s\n", names[i]);
+ }
+
+ TALLOC_FREE(names);
+
+ return -1;
+ }
+
+ if (!pdb_get_account_policy(field, &old_value)) {
+ d_fprintf(stderr, _("Valid account policy, but unable to fetch "
+ "value!\n"));
+ } else {
+ d_printf(_("Account policy \"%s\" value was: %d\n"),
+ account_policy, old_value);
+ }
+
+ if (!pdb_set_account_policy(field, value)) {
+ d_fprintf(stderr, _("Valid account policy, but unable to "
+ "set value!\n"));
+ return -1;
+ } else {
+ d_printf(_("Account policy \"%s\" value is now: %d\n"),
+ account_policy, value);
+ }
+
+ return 0;
+}
+
+static int net_sam_policy_show(struct net_context *c, int argc, const char **argv)
+{
+ const char *account_policy = NULL;
+ uint32_t old_value;
+ enum pdb_policy_type field;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam policy show \"<account policy>\"\n"));
+ return -1;
+ }
+
+ account_policy = argv[0];
+ field = account_policy_name_to_typenum(account_policy);
+
+ if (field == 0) {
+ const char **names;
+ int count;
+ int i;
+ account_policy_names_list(talloc_tos(), &names, &count);
+ d_fprintf(stderr, _("No account policy by that name!\n"));
+ if (count != 0) {
+ d_fprintf(stderr, _("Valid account policies "
+ "are:\n"));
+ for (i=0; i<count; i++) {
+ d_fprintf(stderr, "%s\n", names[i]);
+ }
+ }
+ TALLOC_FREE(names);
+ return -1;
+ }
+
+ if (!pdb_get_account_policy(field, &old_value)) {
+ fprintf(stderr, _("Valid account policy, but unable to "
+ "fetch value!\n"));
+ return -1;
+ }
+
+ printf(_("Account policy \"%s\" description: %s\n"),
+ account_policy, account_policy_get_desc(field));
+ printf(_("Account policy \"%s\" value is: %d\n"), account_policy,
+ old_value);
+ return 0;
+}
+
+static int net_sam_policy_list(struct net_context *c, int argc, const char **argv)
+{
+ const char **names;
+ int count;
+ int i;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net sam policy list\n"
+ " %s\n",
+ _("Usage:"),
+ _("List account policies"));
+ return 0;
+ }
+
+ account_policy_names_list(talloc_tos(), &names, &count);
+ if (count != 0) {
+ d_fprintf(stderr, _("Valid account policies "
+ "are:\n"));
+ for (i = 0; i < count ; i++) {
+ d_fprintf(stderr, "%s\n", names[i]);
+ }
+ }
+ TALLOC_FREE(names);
+ return -1;
+}
+
+static int net_sam_policy(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_sam_policy_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List account policies"),
+ N_("net sam policy list\n"
+ " List account policies")
+ },
+ {
+ "show",
+ net_sam_policy_show,
+ NET_TRANSPORT_LOCAL,
+ N_("Show account policies"),
+ N_("net sam policy show\n"
+ " Show account policies")
+ },
+ {
+ "set",
+ net_sam_policy_set,
+ NET_TRANSPORT_LOCAL,
+ N_("Change account policies"),
+ N_("net sam policy set\n"
+ " Change account policies")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net sam policy", func);
+}
+
+static int net_sam_rights_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ enum sec_privilege privilege;
+
+ if (argc > 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam rights list [privilege name]\n"));
+ return -1;
+ }
+
+ if (argc == 0) {
+ int i;
+ int num = num_privileges_in_short_list();
+
+ for (i=0; i<num; i++) {
+ d_printf("%s\n", sec_privilege_name_from_index(i));
+ }
+ return 0;
+ }
+
+ privilege = sec_privilege_id(argv[0]);
+
+ if (privilege != SEC_PRIV_INVALID) {
+ struct dom_sid *sids;
+ int i, num_sids;
+ NTSTATUS status;
+
+ status = privilege_enum_sids(privilege, talloc_tos(),
+ &sids, &num_sids);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not list rights: %s\n"),
+ nt_errstr(status));
+ return -1;
+ }
+
+ for (i=0; i<num_sids; i++) {
+ const char *dom, *name;
+ enum lsa_SidType type;
+ struct dom_sid_buf buf;
+
+ if (lookup_sid(talloc_tos(), &sids[i], &dom, &name,
+ &type)) {
+ d_printf("%s\\%s\n", dom, name);
+ }
+ else {
+ d_printf("%s\n",
+ dom_sid_str_buf(&sids[i], &buf));
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+static int net_sam_rights_grant(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ int i;
+
+ if (argc < 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam rights grant <name> <rights> ...\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ for (i=1; i < argc; i++) {
+ enum sec_privilege privilege = sec_privilege_id(argv[i]);
+ if (privilege == SEC_PRIV_INVALID) {
+ d_fprintf(stderr, _("%s unknown\n"), argv[i]);
+ return -1;
+ }
+
+ if (!grant_privilege_by_name(&sid, argv[i])) {
+ d_fprintf(stderr, _("Could not grant privilege\n"));
+ return -1;
+ }
+
+ d_printf(_("Granted %s to %s\\%s\n"), argv[i], dom, name);
+ }
+
+ return 0;
+}
+
+static int net_sam_rights_revoke(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ int i;
+
+ if (argc < 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam rights revoke <name> <rights>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ for (i=1; i < argc; i++) {
+ enum sec_privilege privilege = sec_privilege_id(argv[i]);
+ if (privilege == SEC_PRIV_INVALID) {
+ d_fprintf(stderr, _("%s unknown\n"), argv[i]);
+ return -1;
+ }
+
+ if (!revoke_privilege_by_name(&sid, argv[i])) {
+ d_fprintf(stderr, _("Could not revoke privilege\n"));
+ return -1;
+ }
+
+ d_printf(_("Revoked %s from %s\\%s\n"), argv[i], dom, name);
+ }
+
+ return 0;
+}
+
+static int net_sam_rights(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_sam_rights_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List possible user rights"),
+ N_("net sam rights list\n"
+ " List possible user rights")
+ },
+ {
+ "grant",
+ net_sam_rights_grant,
+ NET_TRANSPORT_LOCAL,
+ N_("Grant right(s)"),
+ N_("net sam rights grant\n"
+ " Grant right(s)")
+ },
+ {
+ "revoke",
+ net_sam_rights_revoke,
+ NET_TRANSPORT_LOCAL,
+ N_("Revoke right(s)"),
+ N_("net sam rights revoke\n"
+ " Revoke right(s)")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ return net_run_function(c, argc, argv, "net sam rights", func);
+}
+
+/*
+ * Map a unix group to a domain group
+ */
+
+static NTSTATUS map_unix_group(const struct group *grp, GROUP_MAP *map)
+{
+ const char *dom, *name;
+ uint32_t rid;
+
+ if (pdb_getgrgid(map, grp->gr_gid)) {
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ map->gid = grp->gr_gid;
+
+ if (lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL,
+ &dom, &name, NULL, NULL)) {
+
+ map->nt_name = talloc_asprintf(map, "Unix Group %s",
+ grp->gr_name);
+
+ DEBUG(5, ("%s exists as %s\\%s, retrying as \"%s\"\n",
+ grp->gr_name, dom, name, map->nt_name));
+ }
+
+ if (lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL,
+ NULL, NULL, NULL, NULL)) {
+ DEBUG(3, ("\"%s\" exists, can't map it\n", grp->gr_name));
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ if (pdb_capabilities() & PDB_CAP_STORE_RIDS) {
+ if (!pdb_new_rid(&rid)) {
+ DEBUG(3, ("Could not get a new RID for %s\n",
+ grp->gr_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ } else {
+ rid = algorithmic_pdb_gid_to_group_rid( grp->gr_gid );
+ }
+
+ sid_compose(&map->sid, get_global_sam_sid(), rid);
+ map->sid_name_use = SID_NAME_DOM_GRP;
+ map->comment = talloc_asprintf(map, "Unix Group %s", grp->gr_name);
+
+ return pdb_add_group_mapping_entry(map);
+}
+
+static int net_sam_mapunixgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ GROUP_MAP *map;
+ struct group *grp;
+ struct dom_sid_buf buf;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam mapunixgroup <name>\n"));
+ return -1;
+ }
+
+ grp = getgrnam(argv[0]);
+ if (grp == NULL) {
+ d_fprintf(stderr, _("Could not find group %s\n"), argv[0]);
+ return -1;
+ }
+
+ map = talloc_zero(talloc_tos(), GROUP_MAP);
+ if (!map) {
+ d_fprintf(stderr, _("Out of memory!\n"));
+ return -1;
+ }
+
+ status = map_unix_group(grp, map);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Mapping group %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Mapped unix group %s to SID %s\n"), argv[0],
+ dom_sid_str_buf(&map->sid, &buf));
+
+ TALLOC_FREE(map);
+ return 0;
+}
+
+/*
+ * Remove a group mapping
+ */
+
+static NTSTATUS unmap_unix_group(const struct group *grp)
+{
+ struct dom_sid dom_sid;
+ struct unixid id;
+
+ if (!lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL,
+ NULL, NULL, NULL, NULL)) {
+ DEBUG(3, ("\"%s\" does not exist, can't unmap it\n", grp->gr_name));
+ return NT_STATUS_NO_SUCH_GROUP;
+ }
+
+ id.id = grp->gr_gid;
+ id.type = ID_TYPE_GID;
+ if (!pdb_id_to_sid(&id, &dom_sid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return pdb_delete_group_mapping_entry(dom_sid);
+}
+
+static int net_sam_unmapunixgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ struct group *grp;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam unmapunixgroup <name>\n"));
+ return -1;
+ }
+
+ grp = getgrnam(argv[0]);
+ if (grp == NULL) {
+ d_fprintf(stderr, _("Could not find mapping for group %s.\n"),
+ argv[0]);
+ return -1;
+ }
+
+ status = unmap_unix_group(grp);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Unmapping group %s failed with %s.\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Unmapped unix group %s.\n"), argv[0]);
+
+ return 0;
+}
+
+/*
+ * Create a domain group
+ */
+
+static int net_sam_createdomaingroup(struct net_context *c, int argc,
+ const char **argv)
+{
+ NTSTATUS status;
+ uint32_t rid;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam createdomaingroup <name>\n"));
+ return -1;
+ }
+
+ status = pdb_create_dom_group(talloc_tos(), argv[0], &rid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Creating %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Created domain group %s with RID %d\n"), argv[0], rid);
+
+ return 0;
+}
+
+/*
+ * Delete a domain group
+ */
+
+static int net_sam_deletedomaingroup(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct dom_sid sid;
+ uint32_t rid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam deletelocalgroup <name>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find %s.\n"), argv[0]);
+ return -1;
+ }
+
+ if (type != SID_NAME_DOM_GRP) {
+ d_fprintf(stderr, _("%s is a %s, not a domain group.\n"),
+ argv[0], sid_type_lookup(type));
+ return -1;
+ }
+
+ sid_peek_rid(&sid, &rid);
+
+ status = pdb_delete_dom_group(talloc_tos(), rid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,_("Deleting domain group %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Deleted domain group %s.\n"), argv[0]);
+
+ return 0;
+}
+
+/*
+ * Create a local group
+ */
+
+static int net_sam_createlocalgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ uint32_t rid;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam createlocalgroup <name>\n"));
+ return -1;
+ }
+
+ if (!winbind_ping()) {
+ d_fprintf(stderr, _("winbind seems not to run. "
+ "createlocalgroup only works when winbind runs.\n"));
+ return -1;
+ }
+
+ status = pdb_create_alias(argv[0], &rid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Creating %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Created local group %s with RID %d\n"), argv[0], rid);
+
+ return 0;
+}
+
+/*
+ * Delete a local group
+ */
+
+static int net_sam_deletelocalgroup(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam deletelocalgroup <name>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr,_("Could not find %s.\n"), argv[0]);
+ return -1;
+ }
+
+ if (type != SID_NAME_ALIAS) {
+ d_fprintf(stderr, _("%s is a %s, not a local group.\n"),argv[0],
+ sid_type_lookup(type));
+ return -1;
+ }
+
+ status = pdb_delete_alias(&sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Deleting local group %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Deleted local group %s.\n"), argv[0]);
+
+ return 0;
+}
+
+/*
+ * Create a builtin group
+ */
+
+static int net_sam_createbuiltingroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ uint32_t rid;
+ enum lsa_SidType type;
+ fstring groupname;
+ struct dom_sid sid;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam createbuiltingroup <name>\n"));
+ return -1;
+ }
+
+ if (!winbind_ping()) {
+ d_fprintf(stderr, _("winbind seems not to run. "
+ "createbuiltingroup only works when winbind "
+ "runs.\n"));
+ return -1;
+ }
+
+ /* validate the name and get the group */
+
+ fstrcpy( groupname, "BUILTIN\\" );
+ fstrcat( groupname, argv[0] );
+
+ if ( !lookup_name(talloc_tos(), groupname, LOOKUP_NAME_ALL, NULL,
+ NULL, &sid, &type)) {
+ d_fprintf(stderr, _("%s is not a BUILTIN group\n"), argv[0]);
+ return -1;
+ }
+
+ if ( !sid_peek_rid( &sid, &rid ) ) {
+ d_fprintf(stderr, _("Failed to get RID for %s\n"), argv[0]);
+ return -1;
+ }
+
+ status = pdb_create_builtin(rid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Creating %s failed with %s\n"),
+ argv[0], nt_errstr(status));
+ return -1;
+ }
+
+ d_printf(_("Created BUILTIN group %s with RID %d\n"), argv[0], rid);
+
+ return 0;
+}
+
+/*
+ * Add a group member
+ */
+
+static int net_sam_addmem(struct net_context *c, int argc, const char **argv)
+{
+ const char *groupdomain, *groupname, *memberdomain, *membername;
+ struct dom_sid group, member;
+ enum lsa_SidType grouptype, membertype;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam addmem <group> <member>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &groupdomain, &groupname, &group, &grouptype)) {
+ d_fprintf(stderr, _("Could not find group %s\n"), argv[0]);
+ return -1;
+ }
+
+ /* check to see if the member to be added is a name or a SID */
+
+ if (!lookup_name(talloc_tos(), argv[1], LOOKUP_NAME_LOCAL,
+ &memberdomain, &membername, &member, &membertype))
+ {
+ /* try it as a SID */
+
+ if ( !string_to_sid( &member, argv[1] ) ) {
+ d_fprintf(stderr, _("Could not find member %s\n"),
+ argv[1]);
+ return -1;
+ }
+
+ if ( !lookup_sid(talloc_tos(), &member, &memberdomain,
+ &membername, &membertype) )
+ {
+ d_fprintf(stderr, _("Could not resolve SID %s\n"),
+ argv[1]);
+ return -1;
+ }
+ }
+
+ if ((grouptype == SID_NAME_ALIAS) || (grouptype == SID_NAME_WKN_GRP)) {
+ if ((membertype != SID_NAME_USER) &&
+ (membertype != SID_NAME_ALIAS) &&
+ (membertype != SID_NAME_DOM_GRP)) {
+ d_fprintf(stderr, _("Can't add %s: only users, domain "
+ "groups and domain local groups "
+ "can be added. %s is a %s\n"),
+ argv[0], argv[1],
+ sid_type_lookup(membertype));
+ return -1;
+ }
+ status = pdb_add_aliasmem(&group, &member);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Adding local group member failed "
+ "with %s\n"), nt_errstr(status));
+ return -1;
+ }
+ } else if (grouptype == SID_NAME_DOM_GRP) {
+ uint32_t grouprid, memberrid;
+
+ sid_peek_rid(&group, &grouprid);
+ sid_peek_rid(&member, &memberrid);
+
+ status = pdb_add_groupmem(talloc_tos(), grouprid, memberrid);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Adding domain group member failed "
+ "with %s\n"), nt_errstr(status));
+ return -1;
+ }
+ } else {
+ d_fprintf(stderr, _("Can only add members to local groups so "
+ "far, %s is a %s\n"), argv[0],
+ sid_type_lookup(grouptype));
+ return -1;
+ }
+
+ d_printf(_("Added %s\\%s to %s\\%s\n"), memberdomain, membername,
+ groupdomain, groupname);
+
+ return 0;
+}
+
+/*
+ * Delete a group member
+ */
+
+static int net_sam_delmem(struct net_context *c, int argc, const char **argv)
+{
+ const char *groupdomain, *groupname;
+ const char *memberdomain = NULL;
+ const char *membername = NULL;
+ struct dom_sid group, member;
+ enum lsa_SidType grouptype;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr,"%s\n%s",
+ _("Usage:"),
+ _("net sam delmem <group> <member>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &groupdomain, &groupname, &group, &grouptype)) {
+ d_fprintf(stderr, _("Could not find group %s\n"), argv[0]);
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[1], LOOKUP_NAME_LOCAL,
+ &memberdomain, &membername, &member, NULL)) {
+ if (!string_to_sid(&member, argv[1])) {
+ d_fprintf(stderr, _("Could not find member %s\n"),
+ argv[1]);
+ return -1;
+ }
+ }
+
+ if ((grouptype == SID_NAME_ALIAS) ||
+ (grouptype == SID_NAME_WKN_GRP)) {
+ status = pdb_del_aliasmem(&group, &member);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,_("Deleting local group member failed "
+ "with %s\n"), nt_errstr(status));
+ return -1;
+ }
+ } else if (grouptype == SID_NAME_DOM_GRP) {
+ uint32_t grouprid, memberrid;
+
+ sid_peek_rid(&group, &grouprid);
+ sid_peek_rid(&member, &memberrid);
+
+ status = pdb_del_groupmem(talloc_tos(), grouprid, memberrid);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Deleting domain group member "
+ "failed with %s\n"), nt_errstr(status));
+ return -1;
+ }
+ } else {
+ d_fprintf(stderr, _("Can only delete members from local groups "
+ "so far, %s is a %s\n"), argv[0],
+ sid_type_lookup(grouptype));
+ return -1;
+ }
+
+ if (membername != NULL) {
+ d_printf(_("Deleted %s\\%s from %s\\%s\n"),
+ memberdomain, membername, groupdomain, groupname);
+ } else {
+ struct dom_sid_buf buf;
+ d_printf(_("Deleted %s from %s\\%s\n"),
+ dom_sid_str_buf(&member, &buf),
+ groupdomain,
+ groupname);
+ }
+
+ return 0;
+}
+
+/*
+ * List group members
+ */
+
+static int net_sam_listmem(struct net_context *c, int argc, const char **argv)
+{
+ const char *groupdomain, *groupname;
+ struct dom_sid group;
+ struct dom_sid *members = NULL;
+ size_t i, num_members = 0;
+ enum lsa_SidType grouptype;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam listmem <group>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &groupdomain, &groupname, &group, &grouptype)) {
+ d_fprintf(stderr, _("Could not find group %s\n"), argv[0]);
+ return -1;
+ }
+
+ if ((grouptype == SID_NAME_ALIAS) ||
+ (grouptype == SID_NAME_WKN_GRP)) {
+ status = pdb_enum_aliasmem(&group, talloc_tos(), &members,
+ &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Listing group members failed with "
+ "%s\n"), nt_errstr(status));
+ return -1;
+ }
+ } else if (grouptype == SID_NAME_DOM_GRP) {
+ uint32_t *rids;
+
+ status = pdb_enum_group_members(talloc_tos(), &group,
+ &rids, &num_members);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Listing group members failed with "
+ "%s\n"), nt_errstr(status));
+ return -1;
+ }
+
+ members = talloc_array(talloc_tos(), struct dom_sid,
+ num_members);
+ if (members == NULL) {
+ TALLOC_FREE(rids);
+ return -1;
+ }
+
+ for (i=0; i<num_members; i++) {
+ sid_compose(&members[i], get_global_sam_sid(),
+ rids[i]);
+ }
+ TALLOC_FREE(rids);
+ } else {
+ d_fprintf(stderr,_("Can only list local group members so far.\n"
+ "%s is a %s\n"), argv[0], sid_type_lookup(grouptype));
+ return -1;
+ }
+
+ d_printf(_("%s\\%s has %u members\n"), groupdomain, groupname,
+ (unsigned int)num_members);
+ for (i=0; i<num_members; i++) {
+ const char *dom, *name;
+ if (lookup_sid(talloc_tos(), &members[i], &dom, &name, NULL)) {
+ d_printf(" %s\\%s\n", dom, name);
+ } else {
+ struct dom_sid_buf buf;
+ d_printf(" %s\n",
+ dom_sid_str_buf(&members[i], &buf));
+ }
+ }
+
+ TALLOC_FREE(members);
+
+ return 0;
+}
+
+/*
+ * Do the listing
+ */
+static int net_sam_do_list(struct net_context *c, int argc, const char **argv,
+ struct pdb_search *search, const char *what)
+{
+ bool verbose = (argc == 1);
+
+ if ((argc > 1) || c->display_usage ||
+ ((argc == 1) && !strequal(argv[0], "verbose"))) {
+ d_fprintf(stderr, "%s\n", _("Usage:"));
+ d_fprintf(stderr, _("net sam list %s [verbose]\n"), what);
+ return -1;
+ }
+
+ if (search == NULL) {
+ d_fprintf(stderr, _("Could not start search\n"));
+ return -1;
+ }
+
+ while (true) {
+ struct samr_displayentry entry;
+ if (!search->next_entry(search, &entry)) {
+ break;
+ }
+ if (verbose) {
+ d_printf("%s:%d:%s\n",
+ entry.account_name,
+ entry.rid,
+ entry.description);
+ } else {
+ d_printf("%s\n", entry.account_name);
+ }
+ }
+
+ TALLOC_FREE(search);
+ return 0;
+}
+
+static int net_sam_list_users(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_do_list(c, argc, argv,
+ pdb_search_users(talloc_tos(), ACB_NORMAL),
+ "users");
+}
+
+static int net_sam_list_groups(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_do_list(c, argc, argv, pdb_search_groups(talloc_tos()),
+ "groups");
+}
+
+static int net_sam_list_localgroups(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_do_list(c, argc, argv,
+ pdb_search_aliases(talloc_tos(),
+ get_global_sam_sid()),
+ "localgroups");
+}
+
+static int net_sam_list_builtin(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_do_list(c, argc, argv,
+ pdb_search_aliases(talloc_tos(),
+ &global_sid_Builtin),
+ "builtin");
+}
+
+static int net_sam_list_workstations(struct net_context *c, int argc,
+ const char **argv)
+{
+ return net_sam_do_list(c, argc, argv,
+ pdb_search_users(talloc_tos(), ACB_WSTRUST),
+ "workstations");
+}
+
+/*
+ * List stuff
+ */
+
+static int net_sam_list(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "users",
+ net_sam_list_users,
+ NET_TRANSPORT_LOCAL,
+ N_("List SAM users"),
+ N_("net sam list users\n"
+ " List SAM users")
+ },
+ {
+ "groups",
+ net_sam_list_groups,
+ NET_TRANSPORT_LOCAL,
+ N_("List SAM groups"),
+ N_("net sam list groups\n"
+ " List SAM groups")
+ },
+ {
+ "localgroups",
+ net_sam_list_localgroups,
+ NET_TRANSPORT_LOCAL,
+ N_("List SAM local groups"),
+ N_("net sam list localgroups\n"
+ " List SAM local groups")
+ },
+ {
+ "builtin",
+ net_sam_list_builtin,
+ NET_TRANSPORT_LOCAL,
+ N_("List builtin groups"),
+ N_("net sam list builtin\n"
+ " List builtin groups")
+ },
+ {
+ "workstations",
+ net_sam_list_workstations,
+ NET_TRANSPORT_LOCAL,
+ N_("List domain member workstations"),
+ N_("net sam list workstations\n"
+ " List domain member workstations")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net sam list", func);
+}
+
+/*
+ * Show details of SAM entries
+ */
+
+static int net_sam_show(struct net_context *c, int argc, const char **argv)
+{
+ struct dom_sid sid;
+ struct dom_sid_buf buf;
+ enum lsa_SidType type;
+ const char *dom, *name;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "%s\n%s",
+ _("Usage:"),
+ _("net sam show <name>\n"));
+ return -1;
+ }
+
+ if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL,
+ &dom, &name, &sid, &type)) {
+ d_fprintf(stderr, _("Could not find name %s\n"), argv[0]);
+ return -1;
+ }
+
+ d_printf(_("%s\\%s is a %s with SID %s\n"), dom, name,
+ sid_type_lookup(type), dom_sid_str_buf(&sid, &buf));
+
+ return 0;
+}
+
+#ifdef HAVE_LDAP
+
+/*
+ * Init an LDAP tree with default users and Groups
+ * if ldapsam:editposix is enabled
+ */
+
+static int net_sam_provision(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *tc;
+ char *ldap_bk;
+ char *ldap_uri = NULL;
+ char *p;
+ struct smbldap_state *state = NULL;
+ GROUP_MAP *gmap = NULL;
+ struct dom_sid gsid;
+ gid_t domusers_gid = -1;
+ gid_t domadmins_gid = -1;
+ struct samu *samuser;
+ struct passwd *pwd;
+ bool is_ipa = false;
+ char *bind_dn = NULL;
+ char *bind_secret = NULL;
+ NTSTATUS status;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net sam provision\n"
+ " %s\n",
+ _("Usage:"),
+ _("Init an LDAP tree with default users/groups"));
+ return 0;
+ }
+
+ tc = talloc_new(NULL);
+ if (!tc) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ return -1;
+ }
+
+ if ((ldap_bk = talloc_strdup(tc, lp_passdb_backend())) == NULL) {
+ d_fprintf(stderr, _("talloc failed\n"));
+ talloc_free(tc);
+ return -1;
+ }
+ p = strchr(ldap_bk, ':');
+ if (p) {
+ *p = 0;
+ ldap_uri = talloc_strdup(tc, p+1);
+ trim_char(ldap_uri, ' ', ' ');
+ }
+
+ trim_char(ldap_bk, ' ', ' ');
+
+ if (strcmp(ldap_bk, "IPA_ldapsam") == 0 ) {
+ is_ipa = true;
+ }
+
+ if (strcmp(ldap_bk, "ldapsam") != 0 && !is_ipa ) {
+ d_fprintf(stderr,
+ _("Provisioning works only with ldapsam backend\n"));
+ goto failed;
+ }
+
+ if (!lp_parm_bool(-1, "ldapsam", "trusted", false) ||
+ !lp_parm_bool(-1, "ldapsam", "editposix", false)) {
+
+ d_fprintf(stderr, _("Provisioning works only if ldapsam:trusted"
+ " and ldapsam:editposix are enabled.\n"));
+ goto failed;
+ }
+
+ if (!is_ipa && !winbind_ping()) {
+ d_fprintf(stderr, _("winbind seems not to run. Provisioning "
+ "LDAP only works when winbind runs.\n"));
+ goto failed;
+ }
+
+ if (!fetch_ldap_pw(&bind_dn, &bind_secret)) {
+ d_fprintf(stderr, _("Failed to retrieve LDAP password from secrets.tdb\n"));
+ goto failed;
+ }
+
+ status = smbldap_init(tc, NULL, ldap_uri, false, bind_dn, bind_secret, &state);
+
+ BURN_FREE_STR(bind_secret);
+ SAFE_FREE(bind_dn);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Unable to connect to the LDAP server.\n"));
+ goto failed;
+ }
+
+ d_printf(_("Checking for Domain Users group.\n"));
+
+ sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_USERS);
+
+ gmap = talloc_zero(tc, GROUP_MAP);
+ if (!gmap) {
+ d_printf(_("Out of memory!\n"));
+ goto failed;
+ }
+
+ if (!pdb_getgrsid(gmap, gsid)) {
+ struct dom_sid_buf gsid_str;
+ LDAPMod **mods = NULL;
+ char *dn;
+ char *uname;
+ char *wname;
+ char *gidstr;
+ char *gtype;
+ int rc;
+
+ d_printf(_("Adding the Domain Users group.\n"));
+
+ /* lets allocate a new groupid for this group */
+ if (is_ipa) {
+ domusers_gid = 999;
+ } else {
+ if (!winbind_allocate_gid(&domusers_gid)) {
+ d_fprintf(stderr, _("Unable to allocate a new gid to "
+ "create Domain Users group!\n"));
+ goto domu_done;
+ }
+ }
+
+ uname = talloc_strdup(tc, "domusers");
+ wname = talloc_strdup(tc, "Domain Users");
+ dn = talloc_asprintf(tc, "cn=%s,%s", "domusers",
+ lp_ldap_group_suffix(talloc_tos()));
+ gidstr = talloc_asprintf(tc, "%u", (unsigned int)domusers_gid);
+ gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP);
+
+ if (!uname || !wname || !dn || !gidstr || !gtype) {
+ d_fprintf(stderr, "Out of Memory!\n");
+ goto failed;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP);
+ if (is_ipa) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup");
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid",
+ dom_sid_str_buf(&gsid, &gsid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ smbldap_talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, _("Failed to add Domain Users group "
+ "to ldap directory\n"));
+ }
+
+ if (is_ipa) {
+ if (!pdb_getgrsid(gmap, gsid)) {
+ d_fprintf(stderr, _("Failed to read just "
+ "created domain group.\n"));
+ goto failed;
+ } else {
+ domusers_gid = gmap->gid;
+ }
+ }
+ } else {
+ domusers_gid = gmap->gid;
+ d_printf(_("found!\n"));
+ }
+
+domu_done:
+
+ d_printf(_("Checking for Domain Admins group.\n"));
+
+ sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_ADMINS);
+
+ if (!pdb_getgrsid(gmap, gsid)) {
+ struct dom_sid_buf gsid_str;
+ LDAPMod **mods = NULL;
+ char *dn;
+ char *uname;
+ char *wname;
+ char *gidstr;
+ char *gtype;
+ int rc;
+
+ d_printf(_("Adding the Domain Admins group.\n"));
+
+ /* lets allocate a new groupid for this group */
+ if (is_ipa) {
+ domadmins_gid = 999;
+ } else {
+ if (!winbind_allocate_gid(&domadmins_gid)) {
+ d_fprintf(stderr, _("Unable to allocate a new gid to "
+ "create Domain Admins group!\n"));
+ goto doma_done;
+ }
+ }
+
+ uname = talloc_strdup(tc, "domadmins");
+ wname = talloc_strdup(tc, "Domain Admins");
+ dn = talloc_asprintf(tc, "cn=%s,%s", "domadmins",
+ lp_ldap_group_suffix(talloc_tos()));
+ gidstr = talloc_asprintf(tc, "%u", (unsigned int)domadmins_gid);
+ gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP);
+
+ if (!uname || !wname || !dn || !gidstr || !gtype) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP);
+ if (is_ipa) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup");
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid",
+ dom_sid_str_buf(&gsid, &gsid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ smbldap_talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, _("Failed to add Domain Admins group "
+ "to ldap directory\n"));
+ }
+
+ if (is_ipa) {
+ if (!pdb_getgrsid(gmap, gsid)) {
+ d_fprintf(stderr, _("Failed to read just "
+ "created domain group.\n"));
+ goto failed;
+ } else {
+ domadmins_gid = gmap->gid;
+ }
+ }
+ } else {
+ domadmins_gid = gmap->gid;
+ d_printf(_("found!\n"));
+ }
+
+doma_done:
+
+ d_printf(_("Check for Administrator account.\n"));
+
+ samuser = samu_new(tc);
+ if (!samuser) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ if (!pdb_getsampwnam(samuser, "Administrator")) {
+ LDAPMod **mods = NULL;
+ struct dom_sid sid;
+ struct dom_sid_buf sid_str;
+ char *dn;
+ char *name;
+ char *uidstr;
+ char *gidstr;
+ char *shell;
+ char *dir;
+ char *princ;
+ uid_t uid;
+ int rc;
+
+ d_printf(_("Adding the Administrator user.\n"));
+
+ if (domadmins_gid == -1) {
+ d_fprintf(stderr,
+ _("Can't create Administrator user, Domain "
+ "Admins group not available!\n"));
+ goto done;
+ }
+
+ if (is_ipa) {
+ uid = 999;
+ } else {
+ if (!winbind_allocate_uid(&uid)) {
+ d_fprintf(stderr,
+ _("Unable to allocate a new uid to create "
+ "the Administrator user!\n"));
+ goto done;
+ }
+ }
+
+ name = talloc_strdup(tc, "Administrator");
+ dn = talloc_asprintf(tc, "uid=Administrator,%s",
+ lp_ldap_user_suffix(talloc_tos()));
+ uidstr = talloc_asprintf(tc, "%u", (unsigned int)uid);
+ gidstr = talloc_asprintf(tc, "%u", (unsigned int)domadmins_gid);
+ dir = talloc_sub_specified(tc, lp_template_homedir(),
+ "Administrator",
+ NULL,
+ get_global_sam_name(),
+ uid, domadmins_gid);
+ shell = talloc_sub_specified(tc, lp_template_shell(),
+ "Administrator",
+ NULL,
+ get_global_sam_name(),
+ uid, domadmins_gid);
+
+ if (!name || !dn || !uidstr || !gidstr || !dir || !shell) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ sid_compose(&sid, get_global_sam_sid(), DOMAIN_RID_ADMINISTRATOR);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_ACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SAMBASAMACCOUNT);
+ if (is_ipa) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "person");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "organizationalperson");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetorgperson");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetuser");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbprincipalaux");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbticketpolicyaux");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sn", name);
+ princ = talloc_asprintf(tc, "%s@%s", name, lp_realm());
+ if (!princ) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "krbPrincipalName", princ);
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "uid", name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "uidNumber", uidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "homeDirectory", dir);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "loginShell", shell);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSID",
+ dom_sid_str_buf(&sid, &sid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags",
+ pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED,
+ NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ smbldap_talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, _("Failed to add Administrator user "
+ "to ldap directory\n"));
+ }
+
+ if (is_ipa) {
+ if (!pdb_getsampwnam(samuser, "Administrator")) {
+ d_fprintf(stderr, _("Failed to read just "
+ "created user.\n"));
+ goto failed;
+ }
+ }
+ } else {
+ d_printf(_("found!\n"));
+ }
+
+ d_printf(_("Checking for Guest user.\n"));
+
+ samuser = samu_new(tc);
+ if (!samuser) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ if (!pdb_getsampwnam(samuser, lp_guest_account())) {
+ LDAPMod **mods = NULL;
+ struct dom_sid sid;
+ struct dom_sid_buf sid_str;
+ char *dn;
+ char *uidstr;
+ char *gidstr;
+ int rc;
+
+ d_printf(_("Adding the Guest user.\n"));
+
+ sid_compose(&sid, get_global_sam_sid(), DOMAIN_RID_GUEST);
+
+ pwd = Get_Pwnam_alloc(tc, lp_guest_account());
+
+ if (!pwd) {
+ if (domusers_gid == -1) {
+ d_fprintf(stderr,
+ _("Can't create Guest user, Domain "
+ "Users group not available!\n"));
+ goto done;
+ }
+ if ((pwd = talloc(tc, struct passwd)) == NULL) {
+ d_fprintf(stderr, _("talloc failed\n"));
+ goto done;
+ }
+ pwd->pw_name = talloc_strdup(pwd, lp_guest_account());
+
+ if (is_ipa) {
+ pwd->pw_uid = 999;
+ } else {
+ if (!winbind_allocate_uid(&(pwd->pw_uid))) {
+ d_fprintf(stderr,
+ _("Unable to allocate a new uid to "
+ "create the Guest user!\n"));
+ goto done;
+ }
+ }
+ pwd->pw_gid = domusers_gid;
+ pwd->pw_dir = talloc_strdup(tc, "/");
+ pwd->pw_shell = talloc_strdup(tc, "/bin/false");
+ if (!pwd->pw_dir || !pwd->pw_shell) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+ }
+
+ dn = talloc_asprintf(tc, "uid=%s,%s", pwd->pw_name,
+ lp_ldap_user_suffix (talloc_tos()));
+ uidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_uid);
+ gidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_gid);
+ if (!dn || !uidstr || !gidstr) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_ACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXACCOUNT);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SAMBASAMACCOUNT);
+ if (is_ipa) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "person");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "organizationalperson");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetorgperson");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetuser");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbprincipalaux");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbticketpolicyaux");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sn", pwd->pw_name);
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "uid", pwd->pw_name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", pwd->pw_name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", pwd->pw_name);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "uidNumber", uidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ if ((pwd->pw_dir != NULL) && (pwd->pw_dir[0] != '\0')) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "homeDirectory", pwd->pw_dir);
+ }
+ if ((pwd->pw_shell != NULL) && (pwd->pw_shell[0] != '\0')) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "loginShell", pwd->pw_shell);
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSID",
+ dom_sid_str_buf(&sid, &sid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags",
+ pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED,
+ NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ smbldap_talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, _("Failed to add Guest user to "
+ "ldap directory\n"));
+ }
+
+ if (is_ipa) {
+ if (!pdb_getsampwnam(samuser, lp_guest_account())) {
+ d_fprintf(stderr, _("Failed to read just "
+ "created user.\n"));
+ goto failed;
+ }
+ }
+ } else {
+ d_printf(_("found!\n"));
+ }
+
+ d_printf(_("Checking Guest's group.\n"));
+
+ pwd = Get_Pwnam_alloc(tc, lp_guest_account());
+ if (!pwd) {
+ d_fprintf(stderr,
+ _("Failed to find just created Guest account!\n"
+ " Is nss properly configured?!\n"));
+ goto failed;
+ }
+
+ if (pwd->pw_gid == domusers_gid) {
+ d_printf(_("found!\n"));
+ goto done;
+ }
+
+ if (!pdb_getgrgid(gmap, pwd->pw_gid)) {
+ struct dom_sid_buf gsid_str;
+ LDAPMod **mods = NULL;
+ char *dn;
+ char *uname;
+ char *wname;
+ char *gidstr;
+ char *gtype;
+ int rc;
+
+ d_printf(_("Adding the Domain Guests group.\n"));
+
+ uname = talloc_strdup(tc, "domguests");
+ wname = talloc_strdup(tc, "Domain Guests");
+ dn = talloc_asprintf(tc, "cn=%s,%s", "domguests",
+ lp_ldap_group_suffix(talloc_tos()));
+ gidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_gid);
+ gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP);
+
+ if (!uname || !wname || !dn || !gidstr || !gtype) {
+ d_fprintf(stderr, _("Out of Memory!\n"));
+ goto failed;
+ }
+
+ sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_GUESTS);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP);
+ if (is_ipa) {
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup");
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup");
+ }
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid",
+ dom_sid_str_buf(&gsid, &gsid_str));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ smbldap_talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(state, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr,
+ _("Failed to add Domain Guests group to ldap "
+ "directory\n"));
+ }
+ } else {
+ d_printf(_("found!\n"));
+ }
+
+
+done:
+ talloc_free(state);
+ return 0;
+
+failed:
+ talloc_free(state);
+ return -1;
+}
+
+#endif
+
+/***********************************************************
+ migrated functionality from smbgroupedit
+ **********************************************************/
+int net_sam(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "createbuiltingroup",
+ net_sam_createbuiltingroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new BUILTIN group"),
+ N_("net sam createbuiltingroup\n"
+ " Create a new BUILTIN group")
+ },
+ {
+ "createlocalgroup",
+ net_sam_createlocalgroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new local group"),
+ N_("net sam createlocalgroup\n"
+ " Create a new local group")
+ },
+ {
+ "createdomaingroup",
+ net_sam_createdomaingroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Create a new group"),
+ N_("net sam createdomaingroup\n"
+ " Create a new group")
+ },
+ {
+ "deletelocalgroup",
+ net_sam_deletelocalgroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete an existing local group"),
+ N_("net sam deletelocalgroup\n"
+ " Delete an existing local group")
+ },
+ {
+ "deletedomaingroup",
+ net_sam_deletedomaingroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a domain group"),
+ N_("net sam deletedomaingroup\n"
+ " Delete a group")
+ },
+ {
+ "mapunixgroup",
+ net_sam_mapunixgroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Map a unix group to a domain group"),
+ N_("net sam mapunixgroup\n"
+ " Map a unix group to a domain group")
+ },
+ {
+ "unmapunixgroup",
+ net_sam_unmapunixgroup,
+ NET_TRANSPORT_LOCAL,
+ N_("Remove a group mapping of an unix group to a "
+ "domain group"),
+ N_("net sam unmapunixgroup\n"
+ " Remove a group mapping of an unix group to a "
+ "domain group")
+ },
+ {
+ "addmem",
+ net_sam_addmem,
+ NET_TRANSPORT_LOCAL,
+ N_("Add a member to a group"),
+ N_("net sam addmem\n"
+ " Add a member to a group")
+ },
+ {
+ "delmem",
+ net_sam_delmem,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete a member from a group"),
+ N_("net sam delmem\n"
+ " Delete a member from a group")
+ },
+ {
+ "listmem",
+ net_sam_listmem,
+ NET_TRANSPORT_LOCAL,
+ N_("List group members"),
+ N_("net sam listmem\n"
+ " List group members")
+ },
+ {
+ "list",
+ net_sam_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List users, groups and local groups"),
+ N_("net sam list\n"
+ " List users, groups and local groups")
+ },
+ {
+ "show",
+ net_sam_show,
+ NET_TRANSPORT_LOCAL,
+ N_("Show details of a SAM entry"),
+ N_("net sam show\n"
+ " Show details of a SAM entry")
+ },
+ {
+ "set",
+ net_sam_set,
+ NET_TRANSPORT_LOCAL,
+ N_("Set details of a SAM account"),
+ N_("net sam set\n"
+ " Set details of a SAM account")
+ },
+ {
+ "policy",
+ net_sam_policy,
+ NET_TRANSPORT_LOCAL,
+ N_("Set account policies"),
+ N_("net sam policy\n"
+ " Set account policies")
+ },
+ {
+ "rights",
+ net_sam_rights,
+ NET_TRANSPORT_LOCAL,
+ N_("Manipulate user privileges"),
+ N_("net sam rights\n"
+ " Manipulate user privileges")
+ },
+#ifdef HAVE_LDAP
+ {
+ "provision",
+ net_sam_provision,
+ NET_TRANSPORT_LOCAL,
+ N_("Provision a clean user database"),
+ N_("net sam privison\n"
+ " Provision a clear user database")
+ },
+#endif
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (getuid() != 0) {
+ d_fprintf(stderr, _("You are not root, most things won't "
+ "work\n"));
+ }
+
+ return net_run_function(c, argc, argv, "net sam", func);
+}
+
diff --git a/source3/utils/net_serverid.c b/source3/utils/net_serverid.c
new file mode 100644
index 0000000..435b63b
--- /dev/null
+++ b/source3/utils/net_serverid.c
@@ -0,0 +1,703 @@
+/*
+ Samba Unix/Linux SMB client library
+ net serverid commands
+ Copyright (C) Volker Lendecke 2010
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "lib/util/server_id.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_rbt.h"
+#include "serverid.h"
+#include "session.h"
+#include "smbd/globals.h"
+#include "smbd/smbXsrv_open.h"
+#include "util_tdb.h"
+#include "librpc/gen_ndr/ndr_open_files.h"
+
+struct wipedbs_record_marker {
+ struct wipedbs_record_marker *prev, *next;
+ TDB_DATA key, val;
+ const char *desc;
+};
+
+struct wipedbs_server_data {
+ struct server_id server_id;
+ const char *server_id_str;
+ bool exists;
+ struct wipedbs_record_marker *session_records;
+ struct wipedbs_record_marker *tcon_records;
+ struct wipedbs_record_marker *open_records;
+};
+
+struct wipedbs_state {
+ struct db_context *id2server_data;
+ struct {
+ struct {
+ int total;
+ int existing;
+ int disconnected;
+ } server;
+ struct {
+ int total;
+ int disconnected;
+ int todelete;
+ int failure;
+ } session, tcon, open;
+ int open_timed_out;
+ } stat;
+ struct server_id *server_ids;
+ bool *server_exists;
+ int idx;
+ struct db_context *session_db;
+ struct db_context *tcon_db;
+ struct db_context *open_db;
+ struct timeval now;
+ bool testmode;
+ bool verbose;
+};
+
+static struct wipedbs_server_data *get_server_data(struct wipedbs_state *state,
+ const struct server_id *id)
+{
+ struct wipedbs_server_data *ret = NULL;
+ TDB_DATA key, val = tdb_null;
+ NTSTATUS status;
+
+ key = make_tdb_data((const void*)&id->unique_id, sizeof(id->unique_id));
+ status = dbwrap_fetch(state->id2server_data, talloc_tos(), key, &val);
+ if (NT_STATUS_IS_OK(status)) {
+ ret = *(struct wipedbs_server_data**) val.dptr;
+ TALLOC_FREE(val.dptr);
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ struct server_id_buf idbuf;
+
+ server_id_str_buf(*id, &idbuf);
+
+ ret = talloc_zero(state->id2server_data,
+ struct wipedbs_server_data);
+ if (ret == NULL) {
+ DEBUG(0, ("Failed to allocate server entry for %s\n",
+ idbuf.buf));
+ goto done;
+ }
+ ret->server_id = *id;
+ ret->server_id_str = talloc_strdup(ret, idbuf.buf);
+ ret->exists = true;
+ val = make_tdb_data((const void*)&ret, sizeof(ret));
+ status = dbwrap_store(state->id2server_data,
+ key, val, TDB_INSERT);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to store server entry for %s: %s\n",
+ idbuf.buf, nt_errstr(status)));
+ }
+ goto done;
+ } else {
+ struct server_id_buf idbuf;
+ DEBUG(0, ("Failed to fetch server entry for %s: %s\n",
+ server_id_str_buf(*id, &idbuf), nt_errstr(status)));
+ goto done;
+ }
+ if (!server_id_equal(id, &ret->server_id)) {
+ struct server_id_buf idbuf1, idbuf2;
+ DEBUG(0, ("uniq id collision for %s and %s\n",
+ server_id_str_buf(*id, &idbuf1),
+ server_id_str_buf(ret->server_id, &idbuf2)));
+ smb_panic("server_id->unique_id not unique!");
+ }
+done:
+ return ret;
+}
+
+static int wipedbs_traverse_sessions(struct smbXsrv_session_global0 *session,
+ void *wipedbs_state)
+{
+ struct wipedbs_state *state =
+ talloc_get_type_abort(wipedbs_state,
+ struct wipedbs_state);
+ struct wipedbs_server_data *sd;
+ struct wipedbs_record_marker *rec;
+ TDB_DATA tmp;
+ int ret = -1;
+
+ assert(session->num_channels == 1);
+
+ state->stat.session.total++;
+
+ sd = get_server_data(state, &session->channels[0].server_id);
+ if (sd == NULL) {
+ goto done;
+ }
+
+ if (server_id_is_disconnected(&sd->server_id)) {
+ state->stat.session.disconnected++;
+ }
+
+ rec = talloc_zero(sd, struct wipedbs_record_marker);
+ if (rec == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ tmp = dbwrap_record_get_key(session->db_rec);
+ rec->key = tdb_data_talloc_copy(rec, tmp);
+ tmp = dbwrap_record_get_value(session->db_rec);
+ rec->val = tdb_data_talloc_copy(rec, tmp);
+
+ rec->desc = talloc_asprintf(
+ rec, "session[global: %u wire: %llu]",
+ session->session_global_id,
+ (long long unsigned)session->session_wire_id);
+
+ if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
+ (rec->desc == NULL))
+ {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ state->session_db = dbwrap_record_get_db(session->db_rec);
+
+ DLIST_ADD(sd->session_records, rec);
+ ret = 0;
+done:
+ return ret;
+}
+
+static int wipedbs_traverse_tcon(struct smbXsrv_tcon_global0 *tcon,
+ void *wipedbs_state)
+{
+ struct wipedbs_state *state =
+ talloc_get_type_abort(wipedbs_state,
+ struct wipedbs_state);
+ struct wipedbs_server_data *sd;
+ struct wipedbs_record_marker *rec;
+ TDB_DATA tmp;
+ int ret = -1;
+
+ state->stat.tcon.total++;
+
+ sd = get_server_data(state, &tcon->server_id);
+ if (sd == NULL) {
+ goto done;
+ }
+
+ if (server_id_is_disconnected(&sd->server_id)) {
+ state->stat.tcon.disconnected++;
+ }
+
+ rec = talloc_zero(sd, struct wipedbs_record_marker);
+ if (rec == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ tmp = dbwrap_record_get_key(tcon->db_rec);
+ rec->key = tdb_data_talloc_copy(rec, tmp);
+ tmp = dbwrap_record_get_value(tcon->db_rec);
+ rec->val = tdb_data_talloc_copy(rec, tmp);
+
+ rec->desc = talloc_asprintf(
+ rec, "tcon[global: %u wire: %u session: %u share: %s]",
+ tcon->tcon_global_id, tcon->tcon_wire_id,
+ tcon->session_global_id, tcon->share_name);
+
+ if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
+ (rec->desc == NULL))
+ {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ state->tcon_db = dbwrap_record_get_db(tcon->db_rec);
+
+ DLIST_ADD(sd->tcon_records, rec);
+ ret = 0;
+
+done:
+ return ret;
+}
+
+static int wipedbs_traverse_open(struct db_record *db_rec,
+ struct smbXsrv_open_global0 *open,
+ void *wipedbs_state)
+{
+ struct wipedbs_state *state =
+ talloc_get_type_abort(wipedbs_state,
+ struct wipedbs_state);
+ struct wipedbs_server_data *sd;
+ struct wipedbs_record_marker *rec;
+ TDB_DATA tmp;
+ int ret = -1;
+
+ state->stat.open.total++;
+
+ sd = get_server_data(state, &open->server_id);
+ if (sd == NULL) {
+ goto done;
+ }
+
+ if (server_id_is_disconnected(&sd->server_id)) {
+ struct timeval disconnect_time;
+ int64_t tdiff;
+ bool reached;
+
+ state->stat.open.disconnected++;
+
+ nttime_to_timeval(&disconnect_time, open->disconnect_time);
+ tdiff = usec_time_diff(&state->now, &disconnect_time);
+ reached = (tdiff >= INT64_C(1000)*open->durable_timeout_msec);
+
+ if (state->verbose) {
+ TALLOC_CTX *mem_ctx = talloc_new(talloc_tos());
+ enum ndr_err_code ndr_err;
+ struct vfs_default_durable_cookie cookie;
+
+ ndr_err = ndr_pull_struct_blob(
+ &open->backend_cookie, mem_ctx, &cookie,
+ (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ d_printf("ndr_pull_struct_blob failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ d_printf("open[%s/%s id: 0x%" PRIx32 "] disconnected at "
+ "[%s] %us ago with timeout of %us "
+ "-%s reached\n",
+ cookie.servicepath, cookie.base_name,
+ open->open_global_id,
+ nt_time_string(mem_ctx, open->disconnect_time),
+ (unsigned)(tdiff/1000000),
+ open->durable_timeout_msec / 1000,
+ reached ? "" : " not");
+ talloc_free(mem_ctx);
+ }
+
+ if (!reached) {
+ ret = 0;
+ goto done;
+ }
+ state->stat.open_timed_out++;
+ }
+
+ rec = talloc_zero(sd, struct wipedbs_record_marker);
+ if (rec == NULL) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ tmp = dbwrap_record_get_key(db_rec);
+ rec->key = tdb_data_talloc_copy(rec, tmp);
+ tmp = dbwrap_record_get_value(db_rec);
+ rec->val = tdb_data_talloc_copy(rec, tmp);
+
+ rec->desc = talloc_asprintf(
+ rec, "open[global: %u persistent: %llu volatile: %llu]",
+ open->open_global_id,
+ (long long unsigned)open->open_persistent_id,
+ (long long unsigned)open->open_volatile_id);
+
+ if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) ||
+ (rec->desc == NULL))
+ {
+ DEBUG(0, ("Out of memory!\n"));
+ goto done;
+ }
+
+ state->open_db = dbwrap_record_get_db(db_rec);
+
+ DLIST_ADD(sd->open_records, rec);
+ ret = 0;
+
+done:
+ return ret;
+}
+
+static int wipedbs_traverse_nop(struct db_record *rec, void *private_data)
+{
+ return 0;
+}
+
+static int wipedbs_traverse_fill_ids(struct db_record *rec, void *wipedbs_state)
+{
+ struct wipedbs_state *state = talloc_get_type_abort(
+ wipedbs_state, struct wipedbs_state);
+
+ TDB_DATA val = dbwrap_record_get_value(rec);
+
+ struct wipedbs_server_data *sd = talloc_get_type_abort(
+ *(void**)val.dptr, struct wipedbs_server_data);
+
+ state->server_ids[state->idx] = sd->server_id;
+ state->idx++;
+ return 0;
+}
+
+static int wipedbs_traverse_set_exists(struct db_record *rec,
+ void *wipedbs_state)
+{
+ struct wipedbs_state *state = talloc_get_type_abort(
+ wipedbs_state, struct wipedbs_state);
+
+ TDB_DATA val = dbwrap_record_get_value(rec);
+
+ struct wipedbs_server_data *sd = talloc_get_type_abort(
+ *(void**)val.dptr, struct wipedbs_server_data);
+
+ /* assume a stable traverse order for rbt */
+ SMB_ASSERT(server_id_equal(&state->server_ids[state->idx],
+ &sd->server_id));
+ sd->exists = state->server_exists[state->idx];
+
+ if (sd->exists) {
+ state->stat.server.existing++;
+ }
+ if (server_id_is_disconnected(&sd->server_id)) {
+ state->stat.server.disconnected++;
+ }
+
+ state->idx++;
+ return 0;
+}
+
+static bool serverids_exist(const struct server_id *ids, int num_ids,
+ bool *results)
+{
+ int i;
+
+ for (i=0; i<num_ids; i++) {
+ results[i] = serverid_exists(&ids[i]);
+ }
+
+ return true;
+}
+
+
+static NTSTATUS wipedbs_check_server_exists(struct wipedbs_state *state)
+{
+ NTSTATUS status;
+ bool ok;
+ int num_servers;
+
+ status = dbwrap_traverse_read(state->id2server_data,
+ wipedbs_traverse_nop, NULL, &num_servers);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse temporary database\n"));
+ goto done;
+ }
+ state->stat.server.total = num_servers;
+
+ state->server_ids = talloc_array(state, struct server_id, num_servers);
+ state->server_exists = talloc_array(state, bool, num_servers);
+ if (state->server_ids == NULL || state->server_exists == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ goto done;
+ }
+
+ state->idx = 0;
+ status = dbwrap_traverse_read(state->id2server_data,
+ wipedbs_traverse_fill_ids,
+ state, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse temporary database\n"));
+ goto done;
+ }
+
+ ok = serverids_exist(state->server_ids, num_servers, state->server_exists);
+ if (!ok) {
+ DEBUG(0, ("Calling serverids_exist failed\n"));
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ state->idx = 0;
+ status = dbwrap_traverse_read(state->id2server_data,
+ wipedbs_traverse_set_exists, state, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse temporary database\n"));
+ goto done;
+ }
+done:
+ TALLOC_FREE(state->server_ids);
+ TALLOC_FREE(state->server_exists);
+ return status;
+}
+
+struct wipedbs_delete_state {
+ struct wipedbs_record_marker *cur;
+ bool verbose;
+ bool dry_run;
+ size_t total;
+ size_t num;
+};
+
+static void wipedbs_delete_fn(
+ struct db_record *rec, TDB_DATA value, void *private_data)
+{
+ struct db_context *db = dbwrap_record_get_db(rec);
+ struct wipedbs_delete_state *state = private_data;
+ struct wipedbs_record_marker *cur = state->cur;
+ NTSTATUS status = NT_STATUS_OK;
+
+ state->total += 1;
+
+ if (!tdb_data_equal(value, cur->val)) {
+ DBG_ERR("Warning: record <%s> from %s changed,"
+ "skip record!\n",
+ cur->desc, dbwrap_name(db));
+ return;
+ }
+
+ if (state->verbose) {
+ d_printf("deleting %s\n", cur->desc);
+ }
+
+ if (!state->dry_run) {
+ status = dbwrap_record_delete(rec);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to delete record <%s> from %s: %s\n",
+ cur->desc,
+ dbwrap_name(db),
+ nt_errstr(status));
+ return;
+ }
+
+ state->num += 1;
+}
+
+static int wipedbs_delete_records(struct db_context *db,
+ struct wipedbs_record_marker *records,
+ bool dry_run, bool verbose, int *count)
+{
+ struct wipedbs_delete_state state = {
+ .verbose = verbose, .dry_run = dry_run,
+ };
+
+ if (db == NULL) {
+ return 0;
+ }
+
+ for (state.cur = records;
+ state.cur != NULL;
+ state.cur = state.cur->next) {
+
+ NTSTATUS status = dbwrap_do_locked(
+ db, state.cur->key, wipedbs_delete_fn, &state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("dbwrap_do_locked failed for record <%s> "
+ "from %s\n",
+ state.cur->desc,
+ dbwrap_name(db));
+ }
+ }
+
+ if (verbose) {
+ d_printf("Deleted %zu of %zu records from %s\n",
+ state.num,
+ state.total,
+ dbwrap_name(db));
+ }
+
+ if (count) {
+ *count += state.total;
+ }
+
+ return state.total - state.num;
+}
+
+static int wipedbs_traverse_server_data(struct db_record *rec,
+ void *wipedbs_state)
+{
+ struct wipedbs_state *state = talloc_get_type_abort(
+ wipedbs_state, struct wipedbs_state);
+ bool dry_run = state->testmode;
+ TDB_DATA val = dbwrap_record_get_value(rec);
+ int ret;
+ struct wipedbs_server_data *sd = talloc_get_type_abort(
+ *(void**)val.dptr, struct wipedbs_server_data);
+
+ if (state->verbose) {
+ d_printf("Server: '%s' %s\n", sd->server_id_str,
+ sd->exists ?
+ "exists" :
+ "does not exist, cleaning up...");
+ }
+
+ if (sd->exists) {
+ return 0;
+ }
+
+ ret = wipedbs_delete_records(state->session_db, sd->session_records,
+ dry_run, state->verbose,
+ &state->stat.session.todelete);
+ state->stat.session.failure += ret;
+
+ ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records,
+ dry_run, state->verbose,
+ &state->stat.tcon.todelete);
+ state->stat.tcon.failure += ret;
+
+ ret = wipedbs_delete_records(state->open_db, sd->open_records,
+ dry_run, state->verbose,
+ &state->stat.open.todelete);
+ state->stat.open.failure += ret;
+
+ return 0;
+}
+
+static int net_serverid_wipedbs(struct net_context *c, int argc,
+ const char **argv)
+{
+ int ret = -1;
+ NTSTATUS status;
+ struct wipedbs_state *state = talloc_zero(talloc_tos(),
+ struct wipedbs_state);
+
+ if (c->display_usage) {
+ d_printf("%s\n%s",
+ _("Usage:"),
+ _("net serverid wipedbs [--test] [--verbose]\n"));
+ d_printf("%s\n%s",
+ _("Example:"),
+ _("net serverid wipedbs -v\n"));
+ return -1;
+ }
+
+ state->now = timeval_current();
+ state->testmode = c->opt_testmode;
+ state->verbose = c->opt_verbose;
+
+ state->id2server_data = db_open_rbt(state);
+ if (state->id2server_data == NULL) {
+ DEBUG(0, ("Failed to open temporary database\n"));
+ goto done;
+ }
+
+ status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions,
+ state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = smbXsrv_tcon_global_traverse(wipedbs_traverse_tcon, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = smbXsrv_open_global_traverse(wipedbs_traverse_open, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = wipedbs_check_server_exists(state);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = dbwrap_traverse_read(state->id2server_data,
+ wipedbs_traverse_server_data,
+ state, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0, ("Failed to traverse db: %s\n", nt_errstr(status)));
+ goto done;
+ }
+
+ d_printf("Found %d serverids, %d alive and %d disconnected\n",
+ state->stat.server.total,
+ state->stat.server.existing,
+ state->stat.server.disconnected);
+ d_printf("Found %d sessions, %d alive and %d disconnected"
+ ", cleaned up %d of %d entries\n",
+ state->stat.session.total,
+ state->stat.session.total - state->stat.session.todelete,
+ state->stat.session.disconnected,
+ state->stat.session.todelete - state->stat.session.failure,
+ state->stat.session.todelete);
+ d_printf("Found %d tcons, %d alive and %d disconnected"
+ ", cleaned up %d of %d entries\n",
+ state->stat.tcon.total,
+ state->stat.tcon.total - state->stat.tcon.todelete,
+ state->stat.tcon.disconnected,
+ state->stat.tcon.todelete - state->stat.tcon.failure,
+ state->stat.tcon.todelete);
+ d_printf("Found %d opens, %d alive, %d disconnected and %d timed out"
+ ", cleaned up %d of %d entries\n",
+ state->stat.open.total,
+ state->stat.open.total - state->stat.open.todelete
+ - (state->stat.open.disconnected - state->stat.open_timed_out),
+ state->stat.open.disconnected,
+ state->stat.open_timed_out,
+ state->stat.open.todelete - state->stat.open.failure,
+ state->stat.open.todelete);
+
+ ret = 0;
+done:
+ talloc_free(state);
+ return ret;
+}
+
+static int net_serverid_exists(struct net_context *c, int argc,
+ const char **argv)
+{
+ struct server_id pid;
+ bool ok;
+
+ if ((argc != 1) || (c->display_usage)) {
+ d_printf("Usage:\n"
+ "net serverid exists <serverid>\n");
+ return -1;
+ }
+
+ pid = server_id_from_string(get_my_vnn(), argv[0]);
+ ok = serverid_exists(&pid);
+
+ if (ok) {
+ d_printf("%s exists\n", argv[0]);
+ } else {
+ d_printf("%s does not exist\n", argv[0]);
+ }
+
+ return 0;
+}
+
+int net_serverid(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "wipedbs",
+ net_serverid_wipedbs,
+ NET_TRANSPORT_LOCAL,
+ N_("Clean dead entries from temporary databases"),
+ N_("net serverid wipedbs\n"
+ " Clean dead entries from temporary databases")
+ },
+ {
+ "exists",
+ net_serverid_exists,
+ NET_TRANSPORT_LOCAL,
+ N_("Show existence of a serverid"),
+ N_("net serverid exists <id>")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net serverid", func);
+}
diff --git a/source3/utils/net_share.c b/source3/utils/net_share.c
new file mode 100644
index 0000000..56bb2c9
--- /dev/null
+++ b/source3/utils/net_share.c
@@ -0,0 +1,75 @@
+/*
+ Samba Unix/Linux SMB client library
+ net share commands
+ Copyright (C) 2002 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_share_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "\nnet [<method>] share [misc. options] [targets] \n"
+ "\tenumerates all exported resources (network shares) "
+ "on target server\n\n"
+ "net [<method>] share ADD <name=serverpath> [misc. options] [targets]"
+ "\n\tadds a share from a server (makes the export active)\n\n"
+ "net [<method>] share DELETE <sharename> [misc. options] [targets]"
+ "\n\tdeletes a share from a server (makes the export inactive)\n\n"
+ "net [<method>] share ALLOWEDUSERS [misc. options] [<filename>|- "
+ " [targets]]"
+ "\n\tshows a list of shares (either from [targets] or by default all"
+ "\n\tshares) together with all users allowed to access them. This"
+ "\n\tneeds the output of 'net usersidlist' on stdin or in <filename>."
+ "\n\n"
+ "net [<method>] share MIGRATE FILES <sharename> [misc. options] [targets]"
+ "\n\tMigrates files from remote to local server\n\n"
+ "net [<method>] share MIGRATE SHARES <sharename> [misc. options] [targets]"
+ "\n\tMigrates shares from remote to local server\n\n"
+ "net [<method>] share MIGRATE SECURITY <sharename> [misc. options] [targets]"
+ "\n\tMigrates share-ACLs from remote to local server\n\n"
+ "net [<method>] share MIGRATE ALL <sharename> [misc. options] [targets]"
+ "\n\tMigrates shares (including directories, files) from remote\n"
+ "\tto local server\n\n"
+ ));
+ net_common_methods_usage(c, argc, argv);
+ net_common_flags_usage(c, argc, argv);
+ d_printf(_(
+ "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"
+ "\t-M or --maxusers=<num>\t\tmax users allowed for share\n"
+ "\t --acls\t\t\tcopies ACLs as well\n"
+ "\t --attrs\t\t\tcopies DOS Attributes as well\n"
+ "\t --timestamps\t\tpreserve timestamps while copying files\n"
+ "\t --destination\t\tmigration target server (default: localhost)\n"
+ "\t-e or --exclude\t\t\tlist of shares to be excluded from mirroring\n"
+ "\t-v or --verbose\t\t\tgive verbose output\n"));
+ return -1;
+}
+
+int net_share(struct net_context *c, int argc, const char **argv)
+{
+ if (argc > 0 && strcasecmp_m(argv[0], "HELP") == 0) {
+ net_share_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (net_rpc_check(c, 0))
+ return net_rpc_share(c, argc, argv);
+ return net_rap_share(c, argc, argv);
+}
+
diff --git a/source3/utils/net_status.c b/source3/utils/net_status.c
new file mode 100644
index 0000000..a22b45c
--- /dev/null
+++ b/source3/utils/net_status.c
@@ -0,0 +1,246 @@
+/*
+ Samba Unix/Linux SMB client library
+ net status command -- possible replacement for smbstatus
+ Copyright (C) 2003 Volker Lendecke (vl@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "lib/util/server_id.h"
+#include "utils/net.h"
+#include "session.h"
+#include "messages.h"
+#include "conn_tdb.h"
+
+int net_status_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(" net status sessions [parseable] "
+ "Show list of open sessions\n"));
+ d_printf(_(" net status shares [parseable] "
+ "Show list of open shares\n"));
+ return -1;
+}
+
+static int show_session(const char *key, struct sessionid *session,
+ void *private_data)
+{
+ struct server_id_buf tmp;
+ bool *parseable = (bool *)private_data;
+
+ if (!process_exists(session->pid)) {
+ return 0;
+ }
+
+ if (*parseable) {
+ d_printf("%s\\%s\\%s\\%s\\%s\n",
+ server_id_str_buf(session->pid, &tmp),
+ uidtoname(session->uid),
+ gidtoname(session->gid),
+ session->remote_machine, session->hostname);
+ } else {
+ d_printf("%7s %-12s %-12s %-12s (%s)\n",
+ server_id_str_buf(session->pid, &tmp),
+ uidtoname(session->uid),
+ gidtoname(session->gid),
+ session->remote_machine, session->hostname);
+ }
+
+ return 0;
+}
+
+static int net_status_sessions(struct net_context *c, int argc, const char **argv)
+{
+ bool parseable;
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net status sessions [parseable]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display open user sessions.\n"
+ " If parseable is specified, output is machine-"
+ "readable."));
+ return 0;
+ }
+
+ if (argc == 0) {
+ parseable = false;
+ } else if ((argc == 1) && strequal(argv[0], "parseable")) {
+ parseable = true;
+ } else {
+ return net_status_usage(c, argc, argv);
+ }
+
+ if (!parseable) {
+ d_printf(_("PID Username Group Machine"
+ " \n"
+ "-------------------------------------------"
+ "------------------------\n"));
+ }
+
+ sessionid_traverse_read(show_session, &parseable);
+ return 0;
+}
+
+static int show_share(const struct connections_data *crec,
+ void *state)
+{
+ struct server_id_buf tmp;
+
+ if (crec->cnum == TID_FIELD_INVALID)
+ return 0;
+
+ if (!process_exists(crec->pid)) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %s %-12s %s",
+ crec->servicename, server_id_str_buf(crec->pid, &tmp),
+ crec->machine,
+ time_to_asc(nt_time_to_unix(crec->start)));
+
+ return 0;
+}
+
+struct sessionids {
+ int num_entries;
+ struct sessionid *entries;
+};
+
+static int collect_pids(const char *key, struct sessionid *session,
+ void *private_data)
+{
+ struct sessionids *ids = (struct sessionids *)private_data;
+
+ if (!process_exists(session->pid))
+ return 0;
+
+ ids->num_entries += 1;
+ ids->entries = SMB_REALLOC_ARRAY(ids->entries, struct sessionid, ids->num_entries);
+ if (!ids->entries) {
+ ids->num_entries = 0;
+ return 0;
+ }
+ ids->entries[ids->num_entries-1] = *session;
+
+ return 0;
+}
+
+static int show_share_parseable(const struct connections_data *crec,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct server_id_buf tmp;
+ int i;
+ bool guest = true;
+
+ if (crec->cnum == TID_FIELD_INVALID)
+ return 0;
+
+ if (!process_exists(crec->pid)) {
+ return 0;
+ }
+
+ for (i=0; i<ids->num_entries; i++) {
+ struct server_id id = ids->entries[i].pid;
+ if (server_id_equal(&id, &crec->pid)) {
+ guest = false;
+ break;
+ }
+ }
+
+ d_printf("%s\\%s\\%s\\%s\\%s\\%s\\%s",
+ crec->servicename, server_id_str_buf(crec->pid, &tmp),
+ guest ? "" : uidtoname(ids->entries[i].uid),
+ guest ? "" : gidtoname(ids->entries[i].gid),
+ crec->machine,
+ guest ? "" : ids->entries[i].hostname,
+ time_to_asc(nt_time_to_unix(crec->start)));
+
+ return 0;
+}
+
+static int net_status_shares_parseable(struct net_context *c, int argc, const char **argv)
+{
+ struct sessionids ids;
+
+ ids.num_entries = 0;
+ ids.entries = NULL;
+
+ sessionid_traverse_read(collect_pids, &ids);
+
+ connections_forall_read(show_share_parseable, &ids);
+
+ SAFE_FREE(ids.entries);
+
+ return 0;
+}
+
+static int net_status_shares(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net status shares [parseable]\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display open user shares.\n"
+ " If parseable is specified, output is machine-"
+ "readable."));
+ return 0;
+ }
+
+ if (argc == 0) {
+
+ d_printf(_("\nService pid machine "
+ "Connected at\n"
+ "-------------------------------------"
+ "------------------\n"));
+
+ connections_forall_read(show_share, NULL);
+
+ return 0;
+ }
+
+ if ((argc != 1) || !strequal(argv[0], "parseable")) {
+ return net_status_usage(c, argc, argv);
+ }
+
+ return net_status_shares_parseable(c, argc, argv);
+}
+
+int net_status(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "sessions",
+ net_status_sessions,
+ NET_TRANSPORT_LOCAL,
+ N_("Show list of open sessions"),
+ N_("net status sessions [parseable]\n"
+ " If parseable is specified, output is presented "
+ "in a machine-parseable fashion.")
+ },
+ {
+ "shares",
+ net_status_shares,
+ NET_TRANSPORT_LOCAL,
+ N_("Show list of open shares"),
+ N_("net status shares [parseable]\n"
+ " If parseable is specified, output is presented "
+ "in a machine-parseable fashion.")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ return net_run_function(c, argc, argv, "net status", func);
+}
diff --git a/source3/utils/net_tdb.c b/source3/utils/net_tdb.c
new file mode 100644
index 0000000..29585eb
--- /dev/null
+++ b/source3/utils/net_tdb.c
@@ -0,0 +1,105 @@
+/*
+ * Samba Unix/Linux client library
+ * net tdb commands to query tdb record information
+ * Copyright (C) 2016, 2017 Christof Schmitt <cs@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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "locking/share_mode_lock.h"
+#include "locking/proto.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "librpc/gen_ndr/ndr_open_files.h"
+
+static int net_tdb_locking(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct share_mode_lock *lock;
+ DATA_BLOB blob = { .data = NULL };
+ struct file_id id = { .inode = 0 };
+ int ret = -1;
+ bool ok;
+
+ if (argc < 1) {
+ d_printf("Usage: net tdb locking <key> [ dump ]\n");
+ goto out;
+ }
+
+ ok = locking_init_readonly();
+ if (!ok) {
+ d_printf("locking_init_readonly failed\n");
+ goto out;
+ }
+
+ blob = strhex_to_data_blob(mem_ctx, argv[0]);
+ if (blob.length != sizeof(struct file_id)) {
+ d_printf("Invalid length %zu of key, expected %zu\n",
+ blob.length,
+ sizeof(struct file_id));
+ goto out;
+ }
+
+ memcpy(&id, blob.data, blob.length);
+
+ lock = fetch_share_mode_unlocked(mem_ctx, id);
+ if (lock == NULL) {
+ d_printf("Record with key %s not found.\n", argv[1]);
+ goto out;
+ }
+
+ if (argc == 2 && strequal(argv[1], "dump")) {
+ char *dump = share_mode_data_dump(mem_ctx, lock);
+ d_printf("%s\n", dump);
+ TALLOC_FREE(dump);
+ } else {
+ NTSTATUS status;
+ size_t num_share_modes = 0;
+
+ status = share_mode_count_entries(id, &num_share_modes);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr,
+ "Could not count share entries: %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ d_printf("Share path: %s\n",
+ share_mode_servicepath(lock));
+ d_printf("Name: %s\n",
+ share_mode_filename(mem_ctx, lock));
+ d_printf("Number of share modes: %zu\n", num_share_modes);
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+int net_tdb(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ { "locking",
+ net_tdb_locking,
+ NET_TRANSPORT_LOCAL,
+ N_("Show information for a record in locking.tdb"),
+ N_("net tdb locking <key>")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net tdb", func);
+}
diff --git a/source3/utils/net_time.c b/source3/utils/net_time.c
new file mode 100644
index 0000000..f58d62b
--- /dev/null
+++ b/source3/utils/net_time.c
@@ -0,0 +1,262 @@
+/*
+ Samba Unix/Linux SMB client library
+ net time command
+ Copyright (C) 2001 Andrew Tridgell (tridge@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+#include "includes.h"
+#include "utils/net.h"
+#include "libsmb/nmblib.h"
+#include "libsmb/namequery.h"
+#include "libsmb/libsmb.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+/*
+ return the time on a server. This does not require any authentication
+*/
+static time_t cli_servertime(const char *host,
+ const struct sockaddr_storage *dest_ss,
+ int *zone)
+{
+ time_t ret = 0;
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+
+ status = cli_connect_nb(host, dest_ss, 0, 0x20, lp_netbios_name(),
+ SMB_SIGNING_DEFAULT, 0, &cli);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
+ fprintf(stderr, "Can't contact server %s. NetBIOS support disabled,"
+ " Error %s\n", host, nt_errstr(status));
+ } else {
+ fprintf(stderr, "Can't contact server %s. Error %s\n",
+ host, nt_errstr(status));
+ }
+ goto done;
+ }
+
+ status = smbXcli_negprot(cli->conn,
+ cli->timeout,
+ lp_client_min_protocol(),
+ lp_client_max_protocol(),
+ NULL,
+ NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, _("Protocol negotiation failed: %s\n"),
+ nt_errstr(status));
+ goto done;
+ }
+
+ ret = cli_state_server_time(cli);
+ if (zone) *zone = smb1cli_conn_server_time_zone(cli->conn);
+
+done:
+ if (cli) {
+ cli_shutdown(cli);
+ }
+ return ret;
+}
+
+/* find the servers time on the opt_host host */
+static time_t nettime(struct net_context *c, int *zone)
+{
+ return cli_servertime(c->opt_host,
+ c->opt_have_ip? &c->opt_dest_ip : NULL, zone);
+}
+
+/* return a time as a string ready to be passed to /bin/date */
+static const char *systime(time_t t)
+{
+ struct tm *tm;
+
+ tm = localtime(&t);
+ if (!tm) {
+ return "unknown";
+ }
+
+ return talloc_asprintf(talloc_tos(), "%02d%02d%02d%02d%04d.%02d",
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
+ tm->tm_min, tm->tm_year + 1900, tm->tm_sec);
+}
+
+int net_time_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+"net time\n\tdisplays time on a server (-S server)\n\n"
+"net time system\n\tdisplays time on a server (-S server) in a format ready for /bin/date\n\n"
+"net time set\n\truns /bin/date with the time from the server (-S server)\n\n"
+"net time zone\n\tdisplays the timezone in hours from GMT on the remote server (-S server)\n\n"
+"\n"));
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+/* try to set the system clock */
+static int net_time_set(struct net_context *c, int argc, const char **argv)
+{
+ struct timeval tv;
+ int result;
+
+ if (c->display_usage || c->opt_host == NULL) {
+ d_printf( "%s\n"
+ "net time set\n"
+ " %s\n",
+ _("Usage:"),
+ _("Set local time to that of remote time "
+ "server (-S server) "));
+ return 0;
+ }
+
+ tv.tv_sec = nettime(c, NULL);
+ tv.tv_usec=0;
+
+ if (tv.tv_sec == 0) return -1;
+
+ result = settimeofday(&tv,NULL);
+
+ if (result)
+ d_fprintf(stderr, _("setting system clock failed. Error was (%s)\n"),
+ strerror(errno));
+
+ return result;
+}
+
+/* display the time on a remote box in a format ready for /bin/date */
+static int net_time_system(struct net_context *c, int argc, const char **argv)
+{
+ time_t t;
+
+ if (c->display_usage || c->opt_host == NULL) {
+ d_printf( "%s\n"
+ "net time system\n"
+ " %s\n",
+ _("Usage:"),
+ _("Output remote time server (-S server) "
+ "time in a format ready for /bin/date"));
+ return 0;
+ }
+
+ t = nettime(c, NULL);
+ if (t == 0) return -1;
+
+ printf("%s\n", systime(t));
+
+ return 0;
+}
+
+/* display the remote time server's offset to UTC */
+static int net_time_zone(struct net_context *c, int argc, const char **argv)
+{
+ int zone = 0;
+ int hours, mins;
+ char zsign;
+ time_t t;
+
+ if (c->display_usage || c->opt_host == NULL) {
+ d_printf( "%s\n"
+ "net time zone\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display the remote time server's (-S server) "
+ "offset to UTC"));
+ return 0;
+ }
+
+ t = nettime(c, &zone);
+
+ if (t == 0) return -1;
+
+ zsign = (zone > 0) ? '-' : '+';
+ if (zone < 0) zone = -zone;
+
+ zone /= 60;
+ hours = zone / 60;
+ mins = zone % 60;
+
+ printf("%c%02d%02d\n", zsign, hours, mins);
+
+ return 0;
+}
+
+/* display or set the time on a host */
+int net_time(struct net_context *c, int argc, const char **argv)
+{
+ time_t t;
+ struct functable func[] = {
+ {
+ "system",
+ net_time_system,
+ NET_TRANSPORT_LOCAL,
+ N_("Display time ready for /bin/date"),
+ N_("net time system\n"
+ " Display time ready for /bin/date")
+ },
+ {
+ "set",
+ net_time_set,
+ NET_TRANSPORT_LOCAL,
+ N_("Set the system time from time server"),
+ N_("net time set\n"
+ " Set the system time from time server")
+ },
+ {
+ "zone",
+ net_time_zone,
+ NET_TRANSPORT_LOCAL,
+ N_("Display timezone offset from UTC"),
+ N_("net time zone\n"
+ " Display timezone offset from UTC")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc != 0) {
+ return net_run_function(c, argc, argv, "net time", func);
+ }
+
+ if (c->display_usage) {
+ d_printf( "%s\n"
+ "net time\n"
+ " %s\n",
+ _("Usage:"),
+ _("Display the remote time server's time"));
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (c->opt_host == NULL && !c->opt_have_ip) {
+ bool ok;
+
+ ok = find_master_ip(c->opt_target_workgroup, &c->opt_dest_ip);
+ if (!ok) {
+ d_fprintf(stderr,
+ _("Could not locate a time server. "
+ "Try specifying a target host.\n"));
+ net_time_usage(c, argc, argv);
+ return -1;
+ }
+ c->opt_have_ip = true;
+ }
+
+ /* default - print the time */
+ t = cli_servertime(c->opt_host,
+ c->opt_have_ip? &c->opt_dest_ip : NULL,
+ NULL);
+ if (t == 0) return -1;
+
+ d_printf("%s", ctime(&t));
+ return 0;
+}
diff --git a/source3/utils/net_user.c b/source3/utils/net_user.c
new file mode 100644
index 0000000..9fb6f80
--- /dev/null
+++ b/source3/utils/net_user.c
@@ -0,0 +1,67 @@
+/*
+ Samba Unix/Linux SMB client library
+ net user commands
+ Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com)
+ Copyright (C) 2002 Andrew Tridgell (tridge@samba.org)
+ Copyright (C) 2008 Kai Blin (kai@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+
+int net_user_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("\nnet [<method>] user [misc. options] [targets]"
+ "\n\tList users\n\n"));
+ d_printf(_("net [<method>] user DELETE <name> [misc. options] [targets]"
+ "\n\tDelete specified user\n"));
+ d_printf(_("\nnet [<method>] user INFO <name> [misc. options] [targets]"
+ "\n\tList the domain groups of the specified user\n"));
+ d_printf(_("\nnet [<method>] user ADD <name> [password] [-c container] "
+ "[-F user flags] [misc. options]"
+ " [targets]\n\tAdd specified user\n"));
+ d_printf(_("\nnet [<method>] user RENAME <oldusername> <newusername>"
+ " [targets]\n\tRename specified user\n\n"));
+
+ net_common_methods_usage(c, argc, argv);
+ net_common_flags_usage(c, argc, argv);
+ d_printf(_("\t-C or --comment=<comment>\tdescriptive comment "
+ "(for add only)\n"));
+ d_printf(_("\t-c or --container=<container>\tLDAP container, defaults "
+ "to cn=Users (for add in ADS only)\n"));
+ return -1;
+}
+
+int net_user(struct net_context *c, int argc, const char **argv)
+{
+ if (argc < 1)
+ return net_user_usage(c, argc, argv);
+
+ if (strcasecmp_m(argv[0], "HELP") == 0) {
+ net_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (net_ads_check(c) == 0)
+ return net_ads_user(c, argc, argv);
+
+ /* if server is not specified, default to PDC? */
+ if (net_rpc_check(c, NET_FLAGS_PDC))
+ return net_rpc_user(c, argc, argv);
+
+ return net_rap_user(c, argc, argv);
+}
+
diff --git a/source3/utils/net_usershare.c b/source3/utils/net_usershare.c
new file mode 100644
index 0000000..e5826ee
--- /dev/null
+++ b/source3/utils/net_usershare.c
@@ -0,0 +1,1172 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+
+ Copyright (C) Jeremy Allison (jra@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "system/filesys.h"
+#include "utils/net.h"
+#include "../libcli/security/security.h"
+#include "lib/util/string_wrappers.h"
+
+struct {
+ const char *us_errstr;
+ enum usershare_err us_err;
+} us_errs [] = {
+ {"",USERSHARE_OK},
+ {N_("Malformed usershare file"), USERSHARE_MALFORMED_FILE},
+ {N_("Bad version number"), USERSHARE_BAD_VERSION},
+ {N_("Malformed path entry"), USERSHARE_MALFORMED_PATH},
+ {N_("Malformed comment entryfile"), USERSHARE_MALFORMED_COMMENT_DEF},
+ {N_("Malformed acl definition"), USERSHARE_MALFORMED_ACL_DEF},
+ {N_("Acl parse error"), USERSHARE_ACL_ERR},
+ {N_("Path not absolute"), USERSHARE_PATH_NOT_ABSOLUTE},
+ {N_("Path is denied"), USERSHARE_PATH_IS_DENIED},
+ {N_("Path not allowed"), USERSHARE_PATH_NOT_ALLOWED},
+ {N_("Path is not a directory"), USERSHARE_PATH_NOT_DIRECTORY},
+ {N_("System error"), USERSHARE_POSIX_ERR},
+ {N_("Malformed sharename definition"), USERSHARE_MALFORMED_SHARENAME_DEF},
+ {N_("Bad sharename (doesn't match filename)"), USERSHARE_BAD_SHARENAME},
+ {NULL,(enum usershare_err)-1}
+};
+
+static const char *get_us_error_code(enum usershare_err us_err)
+{
+ char *result;
+ int idx = 0;
+
+ while (us_errs[idx].us_errstr != NULL) {
+ if (us_errs[idx].us_err == us_err) {
+ return us_errs[idx].us_errstr;
+ }
+ idx++;
+ }
+
+ result = talloc_asprintf(talloc_tos(), _("Usershare error code (0x%x)"),
+ (unsigned int)us_err);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+/* The help subsystem for the USERSHARE subcommand */
+
+static int net_usershare_add_usage(struct net_context *c, int argc, const char **argv)
+{
+ char chr = *lp_winbind_separator();
+ d_printf(_(
+ "net usershare add [--long] <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>]\n"
+ "\tAdds the specified share name for this user.\n"
+ "\t<sharename> is the new share name.\n"
+ "\t<path> is the path on the filesystem to export.\n"
+ "\t<comment> is the optional comment for the new share.\n"
+ "\t<acl> is an optional share acl in the format \"DOMAIN%cname:X,DOMAIN%cname:X,....\"\n"
+ "\t<guest_ok=y> if present sets \"guest ok = yes\" on this usershare.\n"
+ "\t\t\"X\" represents a permission and can be any one of the characters f, r or d\n"
+ "\t\twhere \"f\" means full control, \"r\" means read-only, \"d\" means deny access.\n"
+ "\t\tname may be a domain user or group. For local users use the local server name "
+ "instead of \"DOMAIN\"\n"
+ "\t\tThe default acl is \"Everyone:r\" which allows everyone read-only access.\n"
+ "\tAdd --long to print the info on the newly added share.\n"),
+ chr, chr );
+ return -1;
+}
+
+static int net_usershare_delete_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net usershare delete <sharename>\n"
+ "\tdeletes the specified share name for this user.\n"));
+ return -1;
+}
+
+static int net_usershare_info_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net usershare info [--long] [wildcard sharename]\n"
+ "\tPrints out the path, comment and acl elements of shares that match the wildcard.\n"
+ "\tBy default only gives info on shares owned by the current user\n"
+ "\tAdd --long to apply this to all shares\n"
+ "\tOmit the sharename or use a wildcard of '*' to see all shares\n"));
+ return -1;
+}
+
+static int net_usershare_list_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_(
+ "net usershare list [--long] [wildcard sharename]\n"
+ "\tLists the names of all shares that match the wildcard.\n"
+ "\tBy default only lists shares owned by the current user\n"
+ "\tAdd --long to apply this to all shares\n"
+ "\tOmit the sharename or use a wildcard of '*' to see all shares\n"));
+ return -1;
+}
+
+int net_usershare_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(_("net usershare add <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>] to "
+ "add or change a user defined share.\n"
+ "net usershare delete <sharename> to delete a user defined share.\n"
+ "net usershare info [--long] [wildcard sharename] to print info about a user defined share.\n"
+ "net usershare list [--long] [wildcard sharename] to list user defined shares.\n"
+ "net usershare help\n"
+ "\nType \"net usershare help <option>\" to get more information on that option\n\n"));
+
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+/***************************************************************************
+***************************************************************************/
+
+static char *get_basepath(TALLOC_CTX *ctx)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *basepath = lp_usershare_path(ctx, lp_sub);
+
+ if (!basepath) {
+ return NULL;
+ }
+ if ((basepath[0] != '\0') && (basepath[strlen(basepath)-1] == '/')) {
+ basepath[strlen(basepath)-1] = '\0';
+ }
+ return basepath;
+}
+
+/***************************************************************************
+ Delete a single userlevel share.
+***************************************************************************/
+
+static int net_usershare_delete(struct net_context *c, int argc, const char **argv)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ char *us_path;
+ char *sharename;
+
+ if (argc != 1 || c->display_usage) {
+ return net_usershare_delete_usage(c, argc, argv);
+ }
+
+ if ((sharename = strlower_talloc(talloc_tos(), argv[0])) == NULL) {
+ d_fprintf(stderr, _("strlower_talloc failed\n"));
+ return -1;
+ }
+
+ if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) {
+ d_fprintf(stderr, _("net usershare delete: share name %s contains "
+ "invalid characters (any of %s)\n"),
+ sharename, INVALID_SHARENAME_CHARS);
+ TALLOC_FREE(sharename);
+ return -1;
+ }
+
+ us_path = talloc_asprintf(talloc_tos(),
+ "%s/%s",
+ lp_usershare_path(talloc_tos(), lp_sub),
+ sharename);
+ if (!us_path) {
+ TALLOC_FREE(sharename);
+ return -1;
+ }
+
+ if (unlink(us_path) != 0) {
+ d_fprintf(stderr, _("net usershare delete: unable to remove usershare %s. "
+ "Error was %s\n"),
+ us_path, strerror(errno));
+ TALLOC_FREE(sharename);
+ return -1;
+ }
+ TALLOC_FREE(sharename);
+ return 0;
+}
+
+/***************************************************************************
+ Data structures to handle a list of usershare files.
+***************************************************************************/
+
+struct file_list {
+ struct file_list *next, *prev;
+ const char *pathname;
+};
+
+static struct file_list *flist;
+
+/***************************************************************************
+***************************************************************************/
+
+static int get_share_list(TALLOC_CTX *ctx, const char *wcard, bool only_ours)
+{
+ DIR *dp;
+ struct dirent *de;
+ uid_t myuid = geteuid();
+ struct file_list *fl = NULL;
+ char *basepath = get_basepath(ctx);
+
+ if (!basepath) {
+ return -1;
+ }
+ dp = opendir(basepath);
+ if (!dp) {
+ d_fprintf(stderr,
+ _("get_share_list: cannot open usershare directory %s. "
+ "Error %s\n"),
+ basepath, strerror(errno) );
+ return -1;
+ }
+
+ while((de = readdir(dp)) != 0) {
+ SMB_STRUCT_STAT sbuf;
+ char *path;
+ const char *n = de->d_name;
+
+ /* Ignore . and .. */
+ if (*n == '.') {
+ if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
+ continue;
+ }
+ }
+
+ if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) {
+ d_fprintf(stderr,
+ _("get_share_list: ignoring bad share "
+ "name %s\n"), n);
+ continue;
+ }
+ path = talloc_asprintf(ctx,
+ "%s/%s",
+ basepath,
+ n);
+ if (!path) {
+ closedir(dp);
+ return -1;
+ }
+
+ if (sys_lstat(path, &sbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("get_share_list: can't lstat file %s. Error "
+ "was %s\n"),
+ path, strerror(errno) );
+ continue;
+ }
+
+ if (!S_ISREG(sbuf.st_ex_mode)) {
+ d_fprintf(stderr,
+ _("get_share_list: file %s is not a regular "
+ "file. Ignoring.\n"),
+ path );
+ continue;
+ }
+
+ if (only_ours && sbuf.st_ex_uid != myuid) {
+ continue;
+ }
+
+ if (!unix_wild_match(wcard, n)) {
+ continue;
+ }
+
+ /* (Finally) - add to list. */
+ fl = talloc(ctx, struct file_list);
+ if (!fl) {
+ closedir(dp);
+ return -1;
+ }
+ fl->pathname = talloc_strdup(ctx, n);
+ if (!fl->pathname) {
+ closedir(dp);
+ return -1;
+ }
+
+ DLIST_ADD(flist, fl);
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+enum us_priv_op { US_LIST_OP, US_INFO_OP};
+
+struct us_priv_info {
+ TALLOC_CTX *ctx;
+ enum us_priv_op op;
+ struct net_context *c;
+};
+
+/***************************************************************************
+ Call a function for every share on the list.
+***************************************************************************/
+
+static int process_share_list(int (*fn)(struct file_list *, void *), void *priv)
+{
+ struct file_list *fl;
+ int ret = 0;
+
+ for (fl = flist; fl; fl = fl->next) {
+ ret = (*fn)(fl, priv);
+ }
+
+ return ret;
+}
+
+/***************************************************************************
+ Info function.
+***************************************************************************/
+
+static int info_fn(struct file_list *fl, void *priv)
+{
+ SMB_STRUCT_STAT sbuf;
+ char **lines = NULL;
+ struct us_priv_info *pi = (struct us_priv_info *)priv;
+ TALLOC_CTX *ctx = pi->ctx;
+ struct net_context *c = pi->c;
+ int fd = -1;
+ int numlines = 0;
+ struct security_descriptor *psd = NULL;
+ char *basepath;
+ char *sharepath = NULL;
+ char *comment = NULL;
+ char *cp_sharename = NULL;
+ char *acl_str;
+ int num_aces;
+ char sep_str[2];
+ enum usershare_err us_err;
+ bool guest_ok = false;
+
+ sep_str[0] = *lp_winbind_separator();
+ sep_str[1] = '\0';
+
+ basepath = get_basepath(ctx);
+ if (!basepath) {
+ return -1;
+ }
+ basepath = talloc_asprintf_append(basepath,
+ "/%s",
+ fl->pathname);
+ if (!basepath) {
+ return -1;
+ }
+
+#ifdef O_NOFOLLOW
+ fd = open(basepath, O_RDONLY|O_NOFOLLOW, 0);
+#else
+ fd = open(basepath, O_RDONLY, 0);
+#endif
+
+ if (fd == -1) {
+ d_fprintf(stderr, _("info_fn: unable to open %s. %s\n"),
+ basepath, strerror(errno) );
+ return -1;
+ }
+
+ /* Paranoia... */
+ if (sys_fstat(fd, &sbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("info_fn: can't fstat file %s. Error was %s\n"),
+ basepath, strerror(errno) );
+ close(fd);
+ return -1;
+ }
+
+ if (!S_ISREG(sbuf.st_ex_mode)) {
+ d_fprintf(stderr,
+ _("info_fn: file %s is not a regular file. Ignoring.\n"),
+ basepath );
+ close(fd);
+ return -1;
+ }
+
+ lines = fd_lines_load(fd, &numlines, 10240, NULL);
+ close(fd);
+
+ if (lines == NULL) {
+ return -1;
+ }
+
+ /* Ensure it's well formed. */
+ us_err = parse_usershare_file(ctx, &sbuf, fl->pathname, -1, lines, numlines,
+ &sharepath,
+ &comment,
+ &cp_sharename,
+ &psd,
+ &guest_ok);
+
+ TALLOC_FREE(lines);
+
+ if (us_err != USERSHARE_OK) {
+ d_fprintf(stderr,
+ _("info_fn: file %s is not a well formed usershare "
+ "file.\n"),
+ basepath );
+ d_fprintf(stderr, _("info_fn: Error was %s.\n"),
+ get_us_error_code(us_err) );
+ return -1;
+ }
+
+ acl_str = talloc_strdup(ctx, "usershare_acl=");
+ if (!acl_str) {
+ return -1;
+ }
+
+ for (num_aces = 0; num_aces < psd->dacl->num_aces; num_aces++) {
+ const char *domain;
+ const char *name;
+ NTSTATUS ntstatus;
+
+ ntstatus = net_lookup_name_from_sid(c, ctx,
+ &psd->dacl->aces[num_aces].trustee,
+ &domain, &name);
+
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ if (domain && *domain) {
+ acl_str = talloc_asprintf_append(acl_str,
+ "%s%s",
+ domain,
+ sep_str);
+ if (!acl_str) {
+ return -1;
+ }
+ }
+ acl_str = talloc_asprintf_append(acl_str,
+ "%s",
+ name);
+ if (!acl_str) {
+ return -1;
+ }
+
+ } else {
+ struct dom_sid_buf sidstr;
+
+ acl_str = talloc_asprintf_append(
+ acl_str,
+ "%s",
+ dom_sid_str_buf(
+ &psd->dacl->aces[num_aces].trustee,
+ &sidstr));
+ if (!acl_str) {
+ return -1;
+ }
+ }
+ acl_str = talloc_asprintf_append(acl_str, ":");
+ if (!acl_str) {
+ return -1;
+ }
+
+ if (psd->dacl->aces[num_aces].type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ acl_str = talloc_asprintf_append(acl_str, "D,");
+ if (!acl_str) {
+ return -1;
+ }
+ } else {
+ if (psd->dacl->aces[num_aces].access_mask & GENERIC_ALL_ACCESS) {
+ acl_str = talloc_asprintf_append(acl_str, "F,");
+ } else {
+ acl_str = talloc_asprintf_append(acl_str, "R,");
+ }
+ if (!acl_str) {
+ return -1;
+ }
+ }
+ }
+
+ /* NOTE: This is smb.conf-like output. Do not translate. */
+ if (pi->op == US_INFO_OP) {
+ d_printf("[%s]\n", cp_sharename );
+ d_printf("path=%s\n", sharepath );
+ d_printf("comment=%s\n", comment);
+ d_printf("%s\n", acl_str);
+ d_printf("guest_ok=%c\n\n", guest_ok ? 'y' : 'n');
+ } else if (pi->op == US_LIST_OP) {
+ d_printf("%s\n", cp_sharename);
+ }
+
+ return 0;
+}
+
+/***************************************************************************
+ Print out info (internal detail) on userlevel shares.
+***************************************************************************/
+
+static int net_usershare_info(struct net_context *c, int argc, const char **argv)
+{
+ fstring wcard;
+ bool only_ours = true;
+ int ret = -1;
+ struct us_priv_info pi;
+ TALLOC_CTX *ctx;
+
+ fstrcpy(wcard, "*");
+
+ if (c->display_usage)
+ return net_usershare_info_usage(c, argc, argv);
+
+ if (c->opt_long_list_entries) {
+ only_ours = false;
+ }
+
+ switch (argc) {
+ case 0:
+ break;
+ case 1:
+ fstrcpy(wcard, argv[0]);
+ break;
+ default:
+ return net_usershare_info_usage(c, argc, argv);
+ }
+
+ if (!strlower_m(wcard)) {
+ return -1;
+ }
+
+ ctx = talloc_init("share_info");
+ ret = get_share_list(ctx, wcard, only_ours);
+ if (ret) {
+ return ret;
+ }
+
+ pi.ctx = ctx;
+ pi.op = US_INFO_OP;
+ pi.c = c;
+
+ ret = process_share_list(info_fn, &pi);
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/***************************************************************************
+ Count the current total number of usershares.
+***************************************************************************/
+
+static int count_num_usershares(void)
+{
+ DIR *dp;
+ struct dirent *de;
+ int num_usershares = 0;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *basepath = get_basepath(ctx);
+
+ if (!basepath) {
+ return -1;
+ }
+
+ dp = opendir(basepath);
+ if (!dp) {
+ d_fprintf(stderr,
+ _("count_num_usershares: cannot open usershare "
+ "directory %s. Error %s\n"),
+ basepath, strerror(errno) );
+ return -1;
+ }
+
+ while((de = readdir(dp)) != 0) {
+ SMB_STRUCT_STAT sbuf;
+ char *path;
+ const char *n = de->d_name;
+
+ /* Ignore . and .. */
+ if (*n == '.') {
+ if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) {
+ continue;
+ }
+ }
+
+ if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) {
+ d_fprintf(stderr,
+ _("count_num_usershares: ignoring bad share "
+ "name %s\n"), n);
+ continue;
+ }
+ path = talloc_asprintf(ctx,
+ "%s/%s",
+ basepath,
+ n);
+ if (!path) {
+ closedir(dp);
+ return -1;
+ }
+
+ if (sys_lstat(path, &sbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("count_num_usershares: can't lstat file %s. "
+ "Error was %s\n"),
+ path, strerror(errno) );
+ continue;
+ }
+
+ if (!S_ISREG(sbuf.st_ex_mode)) {
+ d_fprintf(stderr,
+ _("count_num_usershares: file %s is not a "
+ "regular file. Ignoring.\n"),
+ path );
+ continue;
+ }
+ num_usershares++;
+ }
+
+ closedir(dp);
+ return num_usershares;
+}
+
+/***************************************************************************
+ Add a single userlevel share.
+***************************************************************************/
+
+static int net_usershare_add(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *ctx = talloc_stackframe();
+ SMB_STRUCT_STAT sbuf;
+ SMB_STRUCT_STAT lsbuf;
+ char *sharename;
+ const char *cp_sharename;
+ char *full_path;
+ char *full_path_tmp;
+ const char *us_path;
+ const char *us_comment;
+ const char *arg_acl;
+ char *us_acl;
+ char *file_img;
+ int num_aces = 0;
+ int i;
+ int tmpfd;
+ const char *pacl;
+ size_t to_write;
+ uid_t myeuid = geteuid();
+ bool guest_ok = false;
+ int num_usershares;
+ mode_t mask;
+
+ us_comment = "";
+ arg_acl = "S-1-1-0:R";
+
+ if (c->display_usage) {
+ TALLOC_FREE(ctx);
+ return net_usershare_add_usage(c, argc, argv);
+ }
+
+ switch (argc) {
+ case 0:
+ case 1:
+ default:
+ TALLOC_FREE(ctx);
+ return net_usershare_add_usage(c, argc, argv);
+ case 2:
+ cp_sharename = argv[0];
+ sharename = strlower_talloc(ctx, argv[0]);
+ us_path = argv[1];
+ break;
+ case 3:
+ cp_sharename = argv[0];
+ sharename = strlower_talloc(ctx, argv[0]);
+ us_path = argv[1];
+ us_comment = argv[2];
+ break;
+ case 4:
+ cp_sharename = argv[0];
+ sharename = strlower_talloc(ctx, argv[0]);
+ us_path = argv[1];
+ us_comment = argv[2];
+ arg_acl = argv[3];
+ break;
+ case 5:
+ cp_sharename = argv[0];
+ sharename = strlower_talloc(ctx, argv[0]);
+ us_path = argv[1];
+ us_comment = argv[2];
+ arg_acl = argv[3];
+ if (strlen(arg_acl) == 0) {
+ arg_acl = "S-1-1-0:R";
+ }
+ if (!strnequal(argv[4], "guest_ok=", 9)) {
+ TALLOC_FREE(ctx);
+ return net_usershare_add_usage(c, argc, argv);
+ }
+ switch (argv[4][9]) {
+ case 'y':
+ case 'Y':
+ guest_ok = true;
+ break;
+ case 'n':
+ case 'N':
+ guest_ok = false;
+ break;
+ default:
+ TALLOC_FREE(ctx);
+ return net_usershare_add_usage(c, argc, argv);
+ }
+ break;
+ }
+
+ /* Ensure we're under the "usershare max shares" number. Advisory only. */
+ num_usershares = count_num_usershares();
+ if (num_usershares >= lp_usershare_max_shares()) {
+ d_fprintf(stderr,
+ _("net usershare add: maximum number of allowed "
+ "usershares (%d) reached\n"),
+ lp_usershare_max_shares() );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) {
+ d_fprintf(stderr, _("net usershare add: share name %s contains "
+ "invalid characters (any of %s)\n"),
+ sharename, INVALID_SHARENAME_CHARS);
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Disallow shares the same as users. */
+ if (getpwnam(sharename)) {
+ d_fprintf(stderr,
+ _("net usershare add: share name %s is already a valid "
+ "system user name\n"),
+ sharename );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Construct the full path for the usershare file. */
+ full_path = get_basepath(ctx);
+ if (!full_path) {
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+ full_path_tmp = talloc_asprintf(ctx,
+ "%s/:tmpXXXXXX",
+ full_path);
+ if (!full_path_tmp) {
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ full_path = talloc_asprintf_append(full_path,
+ "/%s",
+ sharename);
+ if (!full_path) {
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* The path *must* be absolute. */
+ if (us_path[0] != '/') {
+ d_fprintf(stderr,
+ _("net usershare add: path %s is not an absolute "
+ "path.\n"),
+ us_path);
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Check the directory to be shared exists. */
+ if (sys_stat(us_path, &sbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("net usershare add: cannot stat path %s to ensure "
+ "this is a directory. Error was %s\n"),
+ us_path, strerror(errno) );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ if (!S_ISDIR(sbuf.st_ex_mode)) {
+ d_fprintf(stderr,
+ _("net usershare add: path %s is not a directory.\n"),
+ us_path );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* If we're not root, check if we're restricted to sharing out directories
+ that we own only. */
+
+ if ((myeuid != 0) && lp_usershare_owner_only() && (myeuid != sbuf.st_ex_uid)) {
+ d_fprintf(stderr, _("net usershare add: cannot share path %s as "
+ "we are restricted to only sharing directories we own.\n"
+ "\tAsk the administrator to add the line \"usershare owner only = false\" \n"
+ "\tto the [global] section of the smb.conf to allow this.\n"),
+ us_path );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* No validation needed on comment. Now go through and validate the
+ acl string. Convert names to SID's as needed. Then run it through
+ parse_usershare_acl to ensure it's valid. */
+
+ /* Start off the string we'll append to. */
+ us_acl = talloc_strdup(ctx, "");
+ if (!us_acl) {
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ pacl = arg_acl;
+ num_aces = 1;
+
+ /* Add the number of ',' characters to get the number of aces. */
+ num_aces += count_chars(pacl,',');
+
+ for (i = 0; i < num_aces; i++) {
+ struct dom_sid sid;
+ struct dom_sid_buf buf;
+ const char *pcolon = strchr_m(pacl, ':');
+ const char *name;
+
+ if (pcolon == NULL) {
+ d_fprintf(stderr,
+ _("net usershare add: malformed acl %s "
+ "(missing ':').\n"),
+ pacl );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ switch(pcolon[1]) {
+ case 'f':
+ case 'F':
+ case 'd':
+ case 'r':
+ case 'R':
+ break;
+ default:
+ d_fprintf(stderr,
+ _("net usershare add: malformed acl %s "
+ "(access control must be 'r', 'f', "
+ "or 'd')\n"),
+ pacl );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ if (pcolon[2] != ',' && pcolon[2] != '\0') {
+ d_fprintf(stderr,
+ _("net usershare add: malformed terminating "
+ "character for acl %s\n"),
+ pacl );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Get the name */
+ if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) {
+ d_fprintf(stderr, _("talloc_strndup failed\n"));
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+ if (!string_to_sid(&sid, name)) {
+ /* Convert to a SID */
+ NTSTATUS ntstatus = net_lookup_sid_from_name(c, ctx, name, &sid);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ d_fprintf(stderr,
+ _("net usershare add: cannot convert "
+ "name \"%s\" to a SID. %s."),
+ name, get_friendly_nt_error_msg(ntstatus) );
+ if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_CONNECTION_REFUSED)) {
+ d_fprintf(stderr,
+ _(" Maybe smbd is not running.\n"));
+ } else {
+ d_fprintf(stderr, "\n");
+ }
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+ }
+ us_acl = talloc_asprintf_append(
+ us_acl,
+ "%s:%c,",
+ dom_sid_str_buf(&sid, &buf),
+ pcolon[1]);
+ if (us_acl == NULL) {
+ d_fprintf(stderr,
+ _("net usershare add: talloc_asprintf_append() failed\n"));
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Move to the next ACL entry. */
+ if (pcolon[2] == ',') {
+ pacl = &pcolon[3];
+ }
+ }
+
+ /* Remove the last ',' */
+ us_acl[strlen(us_acl)-1] = '\0';
+
+ if (guest_ok && !lp_usershare_allow_guests()) {
+ d_fprintf(stderr, _("net usershare add: guest_ok=y requested "
+ "but the \"usershare allow guests\" parameter is not "
+ "enabled by this server.\n"));
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Create a temporary filename for this share. */
+ mask = umask(S_IRWXO | S_IRWXG);
+ tmpfd = mkstemp(full_path_tmp);
+ umask(mask);
+
+ if (tmpfd == -1) {
+ d_fprintf(stderr,
+ _("net usershare add: cannot create tmp file %s\n"),
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ /* Ensure we opened the file we thought we did. */
+ if (sys_lstat(full_path_tmp, &lsbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("net usershare add: cannot lstat tmp file %s\n"),
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ /* Check this is the same as the file we opened. */
+ if (sys_fstat(tmpfd, &sbuf, false) != 0) {
+ d_fprintf(stderr,
+ _("net usershare add: cannot fstat tmp file %s\n"),
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ if (!S_ISREG(sbuf.st_ex_mode) || sbuf.st_ex_dev != lsbuf.st_ex_dev || sbuf.st_ex_ino != lsbuf.st_ex_ino) {
+ d_fprintf(stderr,
+ _("net usershare add: tmp file %s is not a regular "
+ "file ?\n"),
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ if (fchmod(tmpfd, 0644) == -1) {
+ d_fprintf(stderr,
+ _("net usershare add: failed to fchmod tmp file %s "
+ "to 0644\n"),
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ /* Create the in-memory image of the file. */
+ file_img = talloc_strdup(ctx, "#VERSION 2\npath=");
+ if (file_img == NULL) {
+ d_fprintf(stderr,
+ _("net usershare add: talloc_strdup() failed\n"));
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+ file_img = talloc_asprintf_append(file_img,
+ "%s\ncomment=%s\nusershare_acl=%s\n"
+ "guest_ok=%c\nsharename=%s\n",
+ us_path,
+ us_comment,
+ us_acl,
+ guest_ok ? 'y' : 'n',
+ cp_sharename);
+ if (file_img == NULL) {
+ d_fprintf(stderr,
+ _("net usershare add: talloc_asprintf_append() failed\n"));
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ to_write = strlen(file_img);
+
+ if (write(tmpfd, file_img, to_write) != to_write) {
+ d_fprintf(stderr,
+ _("net usershare add: failed to write %u bytes to "
+ "file %s. Error was %s\n"),
+ (unsigned int)to_write, full_path_tmp, strerror(errno));
+ unlink(full_path_tmp);
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ /* Attempt to replace any existing share by this name. */
+ if (rename(full_path_tmp, full_path) != 0) {
+ unlink(full_path_tmp);
+ d_fprintf(stderr,
+ _("net usershare add: failed to add share %s. Error "
+ "was %s\n"),
+ sharename, strerror(errno));
+ TALLOC_FREE(ctx);
+ close(tmpfd);
+ return -1;
+ }
+
+ close(tmpfd);
+
+ if (c->opt_long_list_entries) {
+ const char *my_argv[2];
+ my_argv[0] = sharename;
+ my_argv[1] = NULL;
+ net_usershare_info(c, 1, my_argv);
+ }
+
+ TALLOC_FREE(ctx);
+ return 0;
+}
+
+#if 0
+/***************************************************************************
+ List function.
+***************************************************************************/
+
+static int list_fn(struct file_list *fl, void *priv)
+{
+ d_printf("%s\n", fl->pathname);
+ return 0;
+}
+#endif
+
+/***************************************************************************
+ List userlevel shares.
+***************************************************************************/
+
+static int net_usershare_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ fstring wcard;
+ bool only_ours = true;
+ int ret = -1;
+ struct us_priv_info pi;
+ TALLOC_CTX *ctx;
+
+ fstrcpy(wcard, "*");
+
+ if (c->display_usage)
+ return net_usershare_list_usage(c, argc, argv);
+
+ if (c->opt_long_list_entries) {
+ only_ours = false;
+ }
+
+ switch (argc) {
+ case 0:
+ break;
+ case 1:
+ fstrcpy(wcard, argv[0]);
+ break;
+ default:
+ return net_usershare_list_usage(c, argc, argv);
+ }
+
+ if (!strlower_m(wcard)) {
+ return -1;
+ }
+
+ ctx = talloc_init("share_list");
+ ret = get_share_list(ctx, wcard, only_ours);
+ if (ret) {
+ return ret;
+ }
+
+ pi.ctx = ctx;
+ pi.op = US_LIST_OP;
+ pi.c = c;
+
+ ret = process_share_list(info_fn, &pi);
+ talloc_destroy(ctx);
+ return ret;
+}
+
+/***************************************************************************
+ Entry-point for all the USERSHARE functions.
+***************************************************************************/
+
+int net_usershare(struct net_context *c, int argc, const char **argv)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ DIR *dp;
+
+ struct functable func[] = {
+ {
+ "add",
+ net_usershare_add,
+ NET_TRANSPORT_LOCAL,
+ N_("Add/modify user defined share"),
+ N_("net usershare add\n"
+ " Add/modify user defined share")
+ },
+ {
+ "delete",
+ net_usershare_delete,
+ NET_TRANSPORT_LOCAL,
+ N_("Delete user defined share"),
+ N_("net usershare delete\n"
+ " Delete user defined share")
+ },
+ {
+ "info",
+ net_usershare_info,
+ NET_TRANSPORT_LOCAL,
+ N_("Display information about a user defined share"),
+ N_("net usershare info\n"
+ " Display information about a user defined share")
+ },
+ {
+ "list",
+ net_usershare_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List user defined shares"),
+ N_("net usershare list\n"
+ " List user defined shares")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (lp_usershare_max_shares() == 0) {
+ d_fprintf(stderr,
+ _("net usershare: usershares are currently "
+ "disabled\n"));
+ return -1;
+ }
+
+ dp = opendir(lp_usershare_path(talloc_tos(), lp_sub));
+ if (!dp) {
+ int err = errno;
+ d_fprintf(stderr,
+ _("net usershare: cannot open usershare directory %s. "
+ "Error %s\n"),
+ lp_usershare_path(talloc_tos(), lp_sub), strerror(err) );
+ if (err == EACCES) {
+ d_fprintf(stderr,
+ _("You do not have permission to create a "
+ "usershare. Ask your administrator to grant "
+ "you permissions to create a share.\n"));
+ } else if (err == ENOENT) {
+ d_fprintf(stderr,
+ _("Please ask your system administrator to "
+ "enable user sharing.\n"));
+ }
+ return -1;
+ }
+ closedir(dp);
+
+ return net_run_function(c, argc, argv, "net usershare", func);
+}
diff --git a/source3/utils/net_util.c b/source3/utils/net_util.c
new file mode 100644
index 0000000..f3b7755
--- /dev/null
+++ b/source3/utils/net_util.c
@@ -0,0 +1,614 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Helper routines for net
+ * Copyright (C) Volker Lendecke 2006
+ * Copyright (C) Kai Blin 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "includes.h"
+#include "utils/net.h"
+#include "libsmb/namequery.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../librpc/gen_ndr/ndr_dssetup_c.h"
+#include "secrets.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "lib/param/param.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/netlogon_creds_cli.h"
+#include "lib/cmdline/cmdline.h"
+
+NTSTATUS net_rpc_lookup_name(struct net_context *c,
+ TALLOC_CTX *mem_ctx, struct cli_state *cli,
+ const char *name, const char **ret_domain,
+ const char **ret_name, struct dom_sid *ret_sid,
+ enum lsa_SidType *ret_type)
+{
+ struct rpc_pipe_client *lsa_pipe = NULL;
+ struct policy_handle pol;
+ NTSTATUS status, result;
+ const char **dom_names;
+ struct dom_sid *sids;
+ enum lsa_SidType *types;
+ struct dcerpc_binding_handle *b;
+
+ ZERO_STRUCT(pol);
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, _("Could not initialise lsa pipe\n"));
+ return status;
+ }
+
+ b = lsa_pipe->binding_handle;
+
+ status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "open_policy %s: %s\n", _("failed"),
+ nt_errstr(status));
+ return status;
+ }
+
+ status = rpccli_lsa_lookup_names(lsa_pipe, mem_ctx, &pol, 1,
+ &name, &dom_names, 1, &sids, &types);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ /* This can happen easily, don't log an error */
+ goto done;
+ }
+
+ if (ret_domain != NULL) {
+ *ret_domain = dom_names[0];
+ }
+ if (ret_name != NULL) {
+ *ret_name = talloc_strdup(mem_ctx, name);
+ }
+ if (ret_sid != NULL) {
+ sid_copy(ret_sid, &sids[0]);
+ }
+ if (ret_type != NULL) {
+ *ret_type = types[0];
+ }
+
+ done:
+ if (is_valid_policy_hnd(&pol)) {
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+ }
+ TALLOC_FREE(lsa_pipe);
+
+ return status;
+}
+
+/****************************************************************************
+ Connect to \\server\service.
+****************************************************************************/
+
+NTSTATUS connect_to_service(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name,
+ const char *service_name,
+ const char *service_type)
+{
+ NTSTATUS nt_status;
+ int flags = 0;
+
+ if (strequal(service_type, "IPC")) {
+ flags |= CLI_FULL_CONNECTION_IPC;
+ }
+
+ nt_status = cli_full_connection_creds(cli_ctx, NULL, server_name,
+ server_ss, c->opt_port,
+ service_name, service_type,
+ c->creds,
+ flags);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, _("Could not connect to server %s\n"),
+ server_name);
+
+ /* Display a nicer message depending on the result */
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_LOGON_FAILURE))
+ d_fprintf(stderr,
+ _("The username or password was not "
+ "correct.\n"));
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_LOCKED_OUT))
+ d_fprintf(stderr, _("The account was locked out.\n"));
+
+ if (NT_STATUS_V(nt_status) ==
+ NT_STATUS_V(NT_STATUS_ACCOUNT_DISABLED))
+ d_fprintf(stderr, _("The account was disabled.\n"));
+ return nt_status;
+ }
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Connect to \\server\ipc$.
+****************************************************************************/
+
+NTSTATUS connect_to_ipc(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name)
+{
+ return connect_to_service(c, cli_ctx, server_ss, server_name, "IPC$",
+ "IPC");
+}
+
+/****************************************************************************
+ Connect to \\server\ipc$ anonymously.
+****************************************************************************/
+
+NTSTATUS connect_to_ipc_anonymous(struct net_context *c,
+ struct cli_state **cli_ctx,
+ const struct sockaddr_storage *server_ss,
+ const char *server_name)
+{
+ NTSTATUS nt_status;
+ struct cli_credentials *anon_creds = NULL;
+
+ anon_creds = cli_credentials_init_anon(c);
+ if (anon_creds == NULL) {
+ DBG_ERR("cli_credentials_init_anon() failed\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = cli_full_connection_creds(cli_ctx, c->opt_requester_name,
+ server_name, server_ss, c->opt_port,
+ "IPC$", "IPC",
+ anon_creds,
+ CLI_FULL_CONNECTION_IPC);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ } else {
+ DEBUG(1,("Cannot connect to server (anonymously). Error was %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+}
+
+/**
+ * Connect a server and open a given pipe
+ *
+ * @param cli_dst A cli_state
+ * @param pipe The pipe to open
+ * @param got_pipe boolean that stores if we got a pipe
+ *
+ * @return Normal NTSTATUS return.
+ **/
+NTSTATUS connect_dst_pipe(struct net_context *c, struct cli_state **cli_dst,
+ struct rpc_pipe_client **pp_pipe_hnd,
+ const struct ndr_interface_table *table)
+{
+ NTSTATUS nt_status;
+ char *server_name = SMB_STRDUP("127.0.0.1");
+ struct cli_state *cli_tmp = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+
+ if (server_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (c->opt_destination) {
+ SAFE_FREE(server_name);
+ if ((server_name = SMB_STRDUP(c->opt_destination)) == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ /* make a connection to a named pipe */
+ nt_status = connect_to_ipc(c, &cli_tmp, NULL, server_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ SAFE_FREE(server_name);
+ return nt_status;
+ }
+
+ nt_status = cli_rpc_pipe_open_noauth(cli_tmp, table,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("couldn't not initialize pipe\n"));
+ cli_shutdown(cli_tmp);
+ SAFE_FREE(server_name);
+ return nt_status;
+ }
+
+ *cli_dst = cli_tmp;
+ *pp_pipe_hnd = pipe_hnd;
+ SAFE_FREE(server_name);
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Use the local machine account (krb) and password for this session.
+****************************************************************************/
+
+int net_use_krb_machine_account(struct net_context *c)
+{
+ char *user_name = NULL;
+
+ if (!secrets_init()) {
+ d_fprintf(stderr,_("ERROR: Unable to open secrets database\n"));
+ exit(1);
+ }
+
+ c->opt_password = secrets_fetch_machine_password(
+ c->opt_target_workgroup, NULL, NULL);
+ if (asprintf(&user_name, "%s$@%s", lp_netbios_name(), lp_realm()) == -1) {
+ return -1;
+ }
+ c->opt_user_name = user_name;
+ c->opt_user_specified = true;
+
+ cli_credentials_set_machine_account(c->creds, c->lp_ctx);
+ return 0;
+}
+
+bool net_find_server(struct net_context *c,
+ const char *domain,
+ unsigned flags,
+ struct sockaddr_storage *server_ss,
+ char **server_name)
+{
+ const char *d = domain ? domain : c->opt_target_workgroup;
+
+ if (c->opt_host) {
+ *server_name = SMB_STRDUP(c->opt_host);
+ }
+
+ if (c->opt_have_ip) {
+ *server_ss = c->opt_dest_ip;
+ if (!*server_name) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &c->opt_dest_ip);
+ *server_name = SMB_STRDUP(addr);
+ }
+ } else if (*server_name) {
+ /* resolve the IP address */
+ if (!resolve_name(*server_name, server_ss, 0x20, false)) {
+ DEBUG(1,("Unable to resolve server name\n"));
+ return false;
+ }
+ } else if (flags & NET_FLAGS_PDC) {
+ fstring dc_name;
+ struct sockaddr_storage pdc_ss;
+
+ if (!get_pdc_ip(d, &pdc_ss)) {
+ DEBUG(1,("Unable to resolve PDC server address\n"));
+ return false;
+ }
+
+ if (is_zero_addr(&pdc_ss)) {
+ return false;
+ }
+
+ if (!name_status_find(d, 0x1b, 0x20, &pdc_ss, dc_name)) {
+ return false;
+ }
+
+ *server_name = SMB_STRDUP(dc_name);
+ *server_ss = pdc_ss;
+ } else if (flags & NET_FLAGS_DMB) {
+ struct sockaddr_storage msbrow_ss;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* if (!resolve_name(MSBROWSE, &msbrow_ip, 1, false)) */
+ if (!resolve_name(d, &msbrow_ss, 0x1B, false)) {
+ DEBUG(1,("Unable to resolve domain browser via name lookup\n"));
+ return false;
+ }
+ *server_ss = msbrow_ss;
+ print_sockaddr(addr, sizeof(addr), server_ss);
+ *server_name = SMB_STRDUP(addr);
+ } else if (flags & NET_FLAGS_MASTER) {
+ struct sockaddr_storage brow_ss;
+ char addr[INET6_ADDRSTRLEN];
+ if (!resolve_name(d, &brow_ss, 0x1D, false)) {
+ /* go looking for workgroups */
+ DEBUG(1,("Unable to resolve master browser via name lookup\n"));
+ return false;
+ }
+ *server_ss = brow_ss;
+ print_sockaddr(addr, sizeof(addr), server_ss);
+ *server_name = SMB_STRDUP(addr);
+ } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) {
+ if (!interpret_string_addr(server_ss,
+ "127.0.0.1", AI_NUMERICHOST)) {
+ DEBUG(1,("Unable to resolve 127.0.0.1\n"));
+ return false;
+ }
+ *server_name = SMB_STRDUP("127.0.0.1");
+ }
+
+ if (!*server_name) {
+ DEBUG(1,("no server to connect to\n"));
+ return false;
+ }
+
+ return true;
+}
+
+bool net_find_pdc(struct sockaddr_storage *server_ss,
+ fstring server_name,
+ const char *domain_name)
+{
+ if (!get_pdc_ip(domain_name, server_ss)) {
+ return false;
+ }
+ if (is_zero_addr(server_ss)) {
+ return false;
+ }
+
+ if (!name_status_find(domain_name, 0x1b, 0x20, server_ss, server_name)) {
+ return false;
+ }
+
+ return true;
+}
+
+NTSTATUS net_make_ipc_connection(struct net_context *c, unsigned flags,
+ struct cli_state **pcli)
+{
+ return net_make_ipc_connection_ex(c, c->opt_workgroup, NULL, NULL, flags, pcli);
+}
+
+NTSTATUS net_make_ipc_connection_ex(struct net_context *c ,const char *domain,
+ const char *server,
+ const struct sockaddr_storage *pss,
+ unsigned flags, struct cli_state **pcli)
+{
+ char *server_name = NULL;
+ struct sockaddr_storage server_ss;
+ struct cli_state *cli = NULL;
+ NTSTATUS nt_status;
+
+ if ( !server || !pss ) {
+ if (!net_find_server(c, domain, flags, &server_ss,
+ &server_name)) {
+ d_fprintf(stderr, _("Unable to find a suitable server "
+ "for domain %s\n"), domain);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+ } else {
+ server_name = SMB_STRDUP( server );
+ server_ss = *pss;
+ }
+
+ if (flags & NET_FLAGS_ANONYMOUS) {
+ nt_status = connect_to_ipc_anonymous(c, &cli, &server_ss,
+ server_name);
+ } else {
+ nt_status = connect_to_ipc(c, &cli, &server_ss,
+ server_name);
+ }
+
+ /* store the server in the affinity cache if it was a PDC */
+
+ if ( (flags & NET_FLAGS_PDC) && NT_STATUS_IS_OK(nt_status) )
+ saf_store(cli->server_domain, server_name);
+
+ SAFE_FREE(server_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, _("Connection failed: %s\n"),
+ nt_errstr(nt_status));
+ cli = NULL;
+ } else if (c->opt_request_timeout) {
+ cli_set_timeout(cli, c->opt_request_timeout * 1000);
+ }
+
+done:
+ if (pcli != NULL) {
+ *pcli = cli;
+ }
+ return nt_status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+/* TODO FIXME: Pass cli_creds via net_context and get rid of this function. */
+const char *net_prompt_pass(struct net_context *c, const char *user)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+
+ if (c->opt_password == NULL) {
+ c->opt_password = cli_credentials_get_password(creds);
+ }
+
+ return c->opt_password;
+}
+
+int net_run_function(struct net_context *c, int argc, const char **argv,
+ const char *whoami, struct functable *table)
+{
+ int i;
+
+ if (argc != 0) {
+ for (i=0; table[i].funcname != NULL; i++) {
+ if (strcasecmp_m(argv[0], table[i].funcname) == 0)
+ return table[i].fn(c, argc-1, argv+1);
+ }
+ }
+
+ if (c->display_usage == false) {
+ d_fprintf(stderr, _("Invalid command: %s %s\n"), whoami,
+ (argc > 0)?argv[0]:"");
+ }
+ d_printf(_("Usage:\n"));
+ for (i=0; table[i].funcname != NULL; i++) {
+ if(c->display_usage == false)
+ d_printf("%s %-15s %s\n", whoami, table[i].funcname,
+ _(table[i].description));
+ else
+ d_printf("%s\n", _(table[i].usage));
+ }
+
+ return c->display_usage?0:-1;
+}
+
+void net_display_usage_from_functable(struct functable *table)
+{
+ int i;
+ for (i=0; table[i].funcname != NULL; i++) {
+ d_printf("%s\n", _(table[i].usage));
+ }
+}
+
+void net_warn_member_options(void)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx != NULL) {
+ netlogon_creds_cli_warn_options(lp_ctx);
+ }
+
+ TALLOC_FREE(frame);
+}
+
+const char *net_share_type_str(int num_type)
+{
+ switch(num_type) {
+ case 0: return _("Disk");
+ case 1: return _("Print");
+ case 2: return _("Dev");
+ case 3: return _("IPC");
+ default: return _("Unknown");
+ }
+}
+
+static NTSTATUS net_scan_dc_noad(struct net_context *c,
+ struct cli_state *cli,
+ struct net_dc_info *dc_info)
+{
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct dcerpc_binding_handle *b;
+ NTSTATUS status, result;
+ struct policy_handle pol;
+ union lsa_PolicyInformation *info;
+
+ ZERO_STRUCTP(dc_info);
+ ZERO_STRUCT(pol);
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ b = pipe_hnd->binding_handle;
+
+ status = dcerpc_lsa_open_policy(b, mem_ctx,
+ false,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &pol,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(result)) {
+ status = result;
+ goto done;
+ }
+
+ dc_info->netbios_domain_name = talloc_strdup(mem_ctx, info->account_domain.name.string);
+ if (dc_info->netbios_domain_name == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ done:
+ if (is_valid_policy_hnd(&pol)) {
+ dcerpc_lsa_Close(b, mem_ctx, &pol, &result);
+ }
+
+ TALLOC_FREE(pipe_hnd);
+
+ return status;
+}
+
+NTSTATUS net_scan_dc(struct net_context *c,
+ struct cli_state *cli,
+ struct net_dc_info *dc_info)
+{
+ TALLOC_CTX *mem_ctx = talloc_tos();
+ struct rpc_pipe_client *dssetup_pipe = NULL;
+ struct dcerpc_binding_handle *dssetup_handle = NULL;
+ union dssetup_DsRoleInfo info;
+ NTSTATUS status;
+ WERROR werr;
+
+ ZERO_STRUCTP(dc_info);
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_dssetup,
+ &dssetup_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10,("net_scan_dc: failed to open dssetup pipe with %s, "
+ "retrying with lsa pipe\n", nt_errstr(status)));
+ return net_scan_dc_noad(c, cli, dc_info);
+ }
+ dssetup_handle = dssetup_pipe->binding_handle;
+
+ status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(dssetup_handle, mem_ctx,
+ DS_ROLE_BASIC_INFORMATION,
+ &info,
+ &werr);
+ TALLOC_FREE(dssetup_pipe);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = werror_to_ntstatus(werr);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ dc_info->is_dc = (info.basic.role & (DS_ROLE_PRIMARY_DC|DS_ROLE_BACKUP_DC));
+ dc_info->is_pdc = (info.basic.role & DS_ROLE_PRIMARY_DC);
+ dc_info->is_ad = (info.basic.flags & DS_ROLE_PRIMARY_DS_RUNNING);
+ dc_info->is_mixed_mode = (info.basic.flags & DS_ROLE_PRIMARY_DS_MIXED_MODE);
+ dc_info->netbios_domain_name = talloc_strdup(mem_ctx, info.basic.domain);
+ dc_info->dns_domain_name = talloc_strdup(mem_ctx, info.basic.dns_domain);
+ dc_info->forest_name = talloc_strdup(mem_ctx, info.basic.forest);
+
+ return NT_STATUS_OK;
+}
diff --git a/source3/utils/net_vfs.c b/source3/utils/net_vfs.c
new file mode 100644
index 0000000..410eef3
--- /dev/null
+++ b/source3/utils/net_vfs.c
@@ -0,0 +1,469 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Distributed SMB/CIFS Server Management Utility
+ * Copyright (C) 2019 Ralph Boehme <slow@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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include <talloc.h>
+#include <tevent.h>
+#include <ftw.h>
+#include "system/filesys.h"
+#include "system/passwd.h"
+#include "lib/param/loadparm.h"
+#include "lib/param/param.h"
+#include "libcli/security/security.h"
+#include "smbd/proto.h"
+#include "locking/share_mode_lock.h"
+#include "locking/proto.h"
+#include "auth.h"
+#include "client.h"
+#include "util_sd.h"
+#include "lib/adouble.h"
+#include "lib/string_replace.h"
+#include "utils/net.h"
+#include "lib/global_contexts.h"
+
+#define NET_VFS_CMD_STREAM_TO_ADOUBLE "stream2adouble"
+
+static struct net_vfs_state {
+ TALLOC_CTX *mem_ctx;
+ struct net_context *c;
+ struct auth_session_info *session_info;
+ struct conn_struct_tos *conn_tos;
+} state;
+
+static void net_vfs_usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "net vfs [OPTIONS] <share> ....\n");
+}
+
+static void net_vfs_getntacl_usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "net vfs getntacl <share> <path>\n");
+}
+
+static void net_vfs_stream_to_appledouble_usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE
+ " [OPTIONS] <share> <path> [<path> ...]\n"
+ "Options:\n"
+ " --verbose verbose output\n"
+ " --continue continue on error\n"
+ " --recursive traverse directory hierarchy\n"
+ " --follow-symlinks follow symlinks\n");
+}
+
+static bool net_vfs_make_session_info(struct auth_session_info **session_info)
+{
+ NTSTATUS status;
+
+ if (non_root_mode()) {
+ struct passwd *p = NULL;
+
+ p = getpwuid(geteuid());
+ if (p == NULL) {
+ fprintf(stderr, "getpwuid(%d) failed\n", geteuid());
+ return false;
+ }
+
+ status = make_session_info_from_username(state.mem_ctx,
+ p->pw_name,
+ false,
+ session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "session_info from username failed\n");
+ return false;
+ }
+
+ return true;
+ }
+
+ status = init_system_session_info(state.mem_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "init_system_session_info failed\n");
+ return false;
+ }
+
+ status = make_session_info_system(state.mem_ctx, session_info);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "make_session_info_system failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int net_vfs_init(struct net_context *c, int argc, const char **argv)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *service = NULL;
+ char *share_root = NULL;
+ int snum;
+ NTSTATUS status;
+ bool ok;
+ int rc = 1;
+
+ state = (struct net_vfs_state) {
+ .c = c,
+ .mem_ctx = c,
+ };
+
+ if (argc < 1) {
+ net_vfs_usage();
+ goto done;
+ }
+
+ if (geteuid() != 0 && !uid_wrapper_enabled()) {
+ fprintf(stderr, "'net vfs' must be run as root.\n");
+ goto done;
+ }
+
+ smb_init_locale();
+ umask(0);
+ sec_init();
+ setup_logging("net", DEBUG_STDOUT);
+ lpcfg_set_cmdline(c->lp_ctx, "log level", "0");
+
+ ok = lp_load_with_registry_shares(get_dyn_CONFIGFILE());
+ if (!ok) {
+ fprintf(stderr, "lp_load_with_registry_shares failed\n");
+ goto done;
+ }
+
+ ok = locking_init();
+ if (!ok) {
+ fprintf(stderr, "locking init failed\n");
+ goto done;
+ }
+
+ ok = net_vfs_make_session_info(&state.session_info);
+ if (!ok) {
+ goto done;
+ }
+
+ service = argv[0];
+ snum = lp_servicenumber(service);
+ if (snum == -1) {
+ fprintf(stderr, "unknown service: %s\n", service);
+ goto done;
+ }
+
+ share_root = lp_path(state.mem_ctx, lp_sub, snum);
+ if (share_root == NULL) {
+ fprintf(stderr, "Failed to find share root for service: %s\n",
+ service);
+ goto done;
+ }
+
+ status = create_conn_struct_tos_cwd(global_messaging_context(),
+ snum,
+ share_root,
+ state.session_info,
+ &state.conn_tos);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ state.conn_tos->conn->share_access = FILE_GENERIC_ALL;
+ state.conn_tos->conn->read_only = false;
+ file_init(state.conn_tos->conn->sconn);
+
+ ok = become_user_without_service_by_session(state.conn_tos->conn,
+ state.session_info);
+ if (!ok) {
+ fprintf(stderr,
+ "become_user_without_service_by_session failed\n");
+ goto done;
+ }
+
+ rc = 0;
+done:
+ return rc;
+}
+
+static int net_vfs_get_ntacl(struct net_context *net,
+ int argc,
+ const char **argv)
+{
+ const char *path = NULL;
+ struct smb_filename *smb_fname = NULL;
+ files_struct *fsp = NULL;
+ struct security_descriptor *sd = NULL;
+ NTSTATUS status;
+ int ret;
+ int rc = 1;
+
+ if (argc < 2 || net->display_usage) {
+ net_vfs_getntacl_usage();
+ goto done;
+ }
+
+ ret = net_vfs_init(net, argc, argv);
+ if (ret != 0) {
+ goto done;
+ }
+
+ path = argv[1];
+ smb_fname = synthetic_smb_fname(state.mem_ctx,
+ path,
+ NULL,
+ NULL,
+ 0,
+ 0);
+ if (smb_fname == NULL) {
+ goto done;
+ }
+
+ ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname);
+ if (ret != 0) {
+ fprintf(stderr, "stat [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname), strerror(errno));
+ goto done;
+ }
+
+ status = openat_pathref_fsp(state.conn_tos->conn->cwd_fsp, smb_fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("openat_pathref_fsp [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname), nt_errstr(status));
+ goto done;
+ }
+
+ status = SMB_VFS_CREATE_FILE(
+ state.conn_tos->conn,
+ NULL, /* req */
+ NULL,
+ smb_fname,
+ FILE_READ_ATTRIBUTES|READ_CONTROL_ACCESS,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN,
+ 0, /* create_options */
+ 0, /* file_attributes */
+ INTERNAL_OPEN_ONLY, /* oplock_request */
+ NULL, /* lease */
+ 0, /* allocation_size */
+ 0, /* private_flags */
+ NULL, /* sd */
+ NULL, /* ea_list */
+ &fsp,
+ NULL, /* info */
+ NULL, NULL); /* create context */
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname), nt_errstr(status));
+ goto done;
+ }
+
+ status = SMB_VFS_FGET_NT_ACL(fsp,
+ SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL,
+ talloc_tos(),
+ &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("SMB_VFS_FGET_NT_ACL [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname), nt_errstr(status));
+ goto done;
+ }
+
+ status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("close_file [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status));
+ goto done;
+ }
+
+ sec_desc_print(NULL, stdout, sd, true);
+
+ rc = 0;
+done:
+ TALLOC_FREE(sd);
+
+ if (fsp != NULL) {
+ status = close_file_free(NULL, &fsp, NORMAL_CLOSE);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("close_file_free() [%s] failed: %s\n",
+ smb_fname_str_dbg(smb_fname),
+ nt_errstr(status));
+ rc = 1;
+ }
+ }
+ return rc;
+}
+
+static bool do_unfruit(const char *path)
+{
+ struct smb_filename *smb_fname = NULL;
+ char *p = NULL;
+ bool converted;
+ int ret;
+ bool ok;
+
+ p = strrchr_m(path, '/');
+ if (p != NULL) {
+ if (p[1] == '.' && p[2] == '_') {
+ return true;
+ }
+ }
+
+ smb_fname = synthetic_smb_fname(state.mem_ctx,
+ path,
+ NULL,
+ NULL,
+ 0,
+ 0);
+ if (smb_fname == NULL) {
+ return false;
+ }
+
+ ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname);
+ if (ret != 0) {
+ fprintf(stderr, "%s: %s\n", path, strerror(errno));
+ if (state.c->opt_continue_on_error) {
+ return true;
+ }
+ return false;
+ }
+
+ ok = ad_unconvert(state.mem_ctx,
+ state.conn_tos->conn->vfs_handles,
+ macos_string_replace_map,
+ smb_fname,
+ &converted);
+ if (!ok) {
+ fprintf(stderr, "Converting failed: %s\n", path);
+ if (state.c->opt_continue_on_error) {
+ return true;
+ }
+ return false;
+ }
+
+ if (converted) {
+ fprintf(stdout, "Converted: %s\n", path);
+ } else if (state.c->opt_verbose) {
+ fprintf(stdout, "%s\n", path);
+ }
+ return true;
+}
+
+static int nftw_cb(const char *path,
+ const struct stat *sb,
+ int typeflag,
+ struct FTW *ftwbuf)
+{
+ bool ok;
+
+ if (typeflag == FTW_SL) {
+ if (state.c->opt_verbose) {
+ fprintf(stdout, "Ignoring symlink: %s\n", path);
+ }
+ return 0;
+ }
+
+ ok = do_unfruit(path);
+ if (!ok) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_vfs_stream_to_appledouble(struct net_context *net,
+ int argc,
+ const char **argv)
+{
+ int i;
+ int ret;
+ bool ok;
+ int rc = 1;
+
+ if (argc < 2 || net->display_usage) {
+ net_vfs_stream_to_appledouble_usage();
+ goto done;
+ }
+
+ ret = net_vfs_init(net, argc, argv);
+ if (ret != 0) {
+ goto done;
+ }
+
+ for (i = 1; i < argc; i++) {
+ const char *path = argv[i];
+
+ if (path[0] == '/') {
+ fprintf(stderr, "ignoring absolute path: %s\n", path);
+ if (state.c->opt_continue_on_error) {
+ continue;
+ }
+ goto done;
+ }
+
+ if (!state.c->opt_recursive) {
+ ok = do_unfruit(path);
+ if (!ok) {
+ if (!state.c->opt_continue_on_error) {
+ goto done;
+ }
+ }
+ continue;
+ }
+
+ ret = nftw(path,
+ nftw_cb,
+ 256,
+ state.c->opt_follow_symlink ? 0 : FTW_PHYS);
+ if (ret != 0) {
+ fprintf(stderr, "%s: %s\n", path, strerror(errno));
+ if (!state.c->opt_continue_on_error) {
+ goto done;
+ }
+ }
+ }
+
+ rc = 0;
+
+done:
+ return rc;
+}
+
+static struct functable func[] = {
+ {
+ "getntacl",
+ net_vfs_get_ntacl,
+ NET_TRANSPORT_LOCAL,
+ N_("Display security descriptor of a file or directory"),
+ N_("net vfs getntacl <share> <path> [<path> ...]")
+ },
+ {
+ NET_VFS_CMD_STREAM_TO_ADOUBLE,
+ net_vfs_stream_to_appledouble,
+ NET_TRANSPORT_LOCAL,
+ N_("Convert streams to AppleDouble files"),
+ N_("net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE " [OPTIONS] <share> <path> [<path> ...]")
+ },
+ {NULL, NULL, 0, NULL, NULL}
+};
+
+int net_vfs(struct net_context *c, int argc, const char **argv)
+{
+ return net_run_function(c, argc, argv, "net vfs", func);
+}
diff --git a/source3/utils/net_witness.c b/source3/utils/net_witness.c
new file mode 100644
index 0000000..accff5b
--- /dev/null
+++ b/source3/utils/net_witness.c
@@ -0,0 +1,2361 @@
+/*
+ * Samba Unix/Linux client library
+ * net witness commands to manage smb witness registrations
+ * Copyright (C) 2023 Stefan Metzmacher
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "utils/net.h"
+#include "messages.h"
+#include "serverid.h"
+#include "lib/util/util_tdb.h"
+#include "source3/include/util_tdb.h"
+#include "libcli/security/dom_sid.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_rbt.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/param/param.h"
+#include "librpc/gen_ndr/ndr_rpcd_witness.h"
+#include <regex.h>
+
+struct json_object;
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#endif /* HAVE_JANSSON */
+
+#undef strcasecmp
+
+static struct db_context *net_witness_open_registration_db(void)
+{
+ static struct db_context *db;
+ char *global_path = NULL;
+
+ if (db != NULL) {
+ return db;
+ }
+
+ global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb");
+ if (global_path == NULL) {
+ return NULL;
+ }
+
+ db = db_open(NULL,
+ global_path,
+ 0, /* hash_size */
+ TDB_DEFAULT |
+ TDB_CLEAR_IF_FIRST |
+ TDB_INCOMPATIBLE_HASH,
+ O_RDONLY,
+ 0600,
+ DBWRAP_LOCK_ORDER_1,
+ DBWRAP_FLAG_NONE);
+ TALLOC_FREE(global_path);
+ if (db == NULL) {
+ return NULL;
+ }
+
+ return db;
+}
+
+struct net_witness_scan_registrations_action_state {
+ bool (*prepare_fn)(void *private_data);
+ bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg);
+ NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg);
+ void *private_data;
+};
+
+struct net_witness_scan_registrations_regex {
+ regex_t regex;
+ bool valid;
+};
+
+struct net_witness_scan_registrations_state {
+ struct net_context *c;
+ struct net_witness_scan_registrations_regex net_name;
+ struct net_witness_scan_registrations_regex share_name;
+ struct net_witness_scan_registrations_regex ip_address;
+ struct net_witness_scan_registrations_regex client_computer;
+ struct json_object *message_json;
+#ifdef HAVE_JANSSON
+ struct json_object filters_json;
+ struct json_object registrations_json;
+#endif
+ const struct net_witness_scan_registrations_action_state *action;
+ NTSTATUS error;
+};
+
+static bool net_witness_scan_registrations_regex_init(
+ struct net_witness_scan_registrations_state *state,
+ struct net_witness_scan_registrations_regex *r,
+ const char *option, const char *value);
+static bool net_witness_scan_registrations_regex_match(
+ struct net_witness_scan_registrations_regex *r,
+ const char *name, const char *value);
+static void net_witness_scan_registrations_regex_free(
+ struct net_witness_scan_registrations_regex *r);
+
+static bool net_witness_scan_registrations_match(
+ struct net_witness_scan_registrations_state *state,
+ const struct rpcd_witness_registration *rg)
+{
+ if (state->net_name.valid) {
+ bool match;
+
+ match = net_witness_scan_registrations_regex_match(
+ &state->net_name,
+ "net_name",
+ rg->net_name);
+ if (!match) {
+ return false;
+ }
+ }
+
+ if (state->share_name.valid) {
+ bool match;
+
+ match = net_witness_scan_registrations_regex_match(
+ &state->share_name,
+ "share_name",
+ rg->share_name);
+ if (!match) {
+ return false;
+ }
+ }
+
+ if (state->ip_address.valid) {
+ bool match;
+
+ match = net_witness_scan_registrations_regex_match(
+ &state->ip_address,
+ "ip_address",
+ rg->ip_address);
+ if (!match) {
+ return false;
+ }
+ }
+
+ if (state->client_computer.valid) {
+ bool match;
+
+ match = net_witness_scan_registrations_regex_match(
+ &state->client_computer,
+ "client_computer_name",
+ rg->client_computer_name);
+ if (!match) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static bool net_witness_scan_registrations_regex_init(
+ struct net_witness_scan_registrations_state *state,
+ struct net_witness_scan_registrations_regex *r,
+ const char *option, const char *value)
+{
+#ifdef HAVE_JANSSON
+ struct net_context *c = state->c;
+#endif /* HAVE_JANSSON */
+ int ret;
+
+ r->valid = false;
+
+ if (value == NULL) {
+ return true;
+ }
+
+ ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB);
+ if (ret != 0) {
+ fstring buf = { 0,};
+ regerror(ret, &r->regex, buf, sizeof(buf));
+ d_printf("regcomp(%s) failed for %s: "
+ "%d: %s\n", value, option, ret, buf);
+ return false;
+ }
+
+#ifdef HAVE_JANSSON
+ if (c->opt_json) {
+ ret = json_add_string(&state->filters_json,
+ option,
+ value);
+ if (ret != 0) {
+ return false;
+ }
+ }
+#endif /* HAVE_JANSSON */
+
+ r->valid = true;
+ return true;
+}
+
+static bool net_witness_scan_registrations_regex_match(
+ struct net_witness_scan_registrations_regex *r,
+ const char *name, const char *value)
+{
+ int ret;
+
+ if (!r->valid) {
+ return false;
+ }
+
+ if (value == NULL) {
+ /*
+ * without a share name,
+ * we match against an empty
+ * string.
+ */
+ value = "";
+ }
+
+ ret = regexec(&r->regex, value, 0, NULL, 0);
+ if (ret == REG_NOMATCH) {
+ return false;
+ }
+
+ return true;
+}
+
+static void net_witness_scan_registrations_regex_free(
+ struct net_witness_scan_registrations_regex *r)
+{
+ if (r->valid) {
+ regfree(&r->regex);
+ r->valid = false;
+ }
+}
+
+static bool net_witness_scan_registrations_init(
+ struct net_witness_scan_registrations_state *state)
+{
+ struct net_context *c = state->c;
+ bool ok;
+
+ if (c->opt_json) {
+#ifdef HAVE_JANSSON
+ state->filters_json = json_new_object();
+ if (json_is_invalid(&state->filters_json)) {
+ return false;
+ }
+
+ if (c->opt_witness_registration != NULL) {
+ int ret;
+
+ ret = json_add_string(&state->filters_json,
+ "--witness-registration",
+ c->opt_witness_registration);
+ if (ret != 0) {
+ return false;
+ }
+ }
+
+ if (c->opt_witness_apply_to_all != 0) {
+ int ret;
+
+ ret = json_add_bool(&state->filters_json,
+ "--witness-apply-to-all",
+ c->opt_witness_apply_to_all != 0);
+ if (ret != 0) {
+ return false;
+ }
+ }
+
+ state->registrations_json = json_new_object();
+ if (json_is_invalid(&state->registrations_json)) {
+ return false;
+ }
+#else /* not HAVE_JANSSON */
+ d_fprintf(stderr, _("JSON support not available\n"));
+ return false;
+#endif /* not HAVE_JANSSON */
+ }
+
+ ok = net_witness_scan_registrations_regex_init(state,
+ &state->net_name,
+ "--witness-net-name",
+ c->opt_witness_net_name);
+ if (!ok) {
+ return false;
+ }
+
+ ok = net_witness_scan_registrations_regex_init(state,
+ &state->share_name,
+ "--witness-share-name",
+ c->opt_witness_share_name);
+ if (!ok) {
+ return false;
+ }
+
+ ok = net_witness_scan_registrations_regex_init(state,
+ &state->ip_address,
+ "--witness-ip-address",
+ c->opt_witness_ip_address);
+ if (!ok) {
+ return false;
+ }
+
+ ok = net_witness_scan_registrations_regex_init(state,
+ &state->client_computer,
+ "--witness-client-computer-name",
+ c->opt_witness_client_computer_name);
+ if (!ok) {
+ return false;
+ }
+
+ ok = state->action->prepare_fn(state->action->private_data);
+ if (!ok) {
+ return false;
+ }
+
+ if (!c->opt_json) {
+ d_printf("%-36s %-20s %-15s %-20s %s\n",
+ "Registration-UUID:",
+ "NetName",
+ "ShareName",
+ "IpAddress",
+ "ClientComputerName");
+ d_printf("%-36s-%-20s-%-15s-%-20s-%s\n",
+ "------------------------------------",
+ "--------------------",
+ "------------------",
+ "--------------------",
+ "------------------");
+ }
+
+ return true;
+}
+
+static bool net_witness_scan_registrations_finish(
+ struct net_witness_scan_registrations_state *state)
+{
+#ifdef HAVE_JANSSON
+ struct net_context *c = state->c;
+ struct json_object root_json = json_empty_object;
+ TALLOC_CTX *frame = NULL;
+ const char *json_str = NULL;
+ int ret;
+
+ if (!c->opt_json) {
+ return true;
+ }
+
+ frame = talloc_stackframe();
+
+ root_json = json_new_object();
+ if (json_is_invalid(&root_json)) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ ret = json_add_object(&root_json,
+ "filters",
+ &state->filters_json);
+ if (ret != 0) {
+ json_free(&root_json);
+ TALLOC_FREE(frame);
+ return false;
+ }
+ state->filters_json = json_empty_object;
+
+ if (state->message_json != NULL) {
+ ret = json_add_object(&root_json,
+ "message",
+ state->message_json);
+ if (ret != 0) {
+ json_free(&root_json);
+ TALLOC_FREE(frame);
+ return false;
+ }
+ *state->message_json = json_empty_object;
+ }
+
+ ret = json_add_object(&root_json,
+ "registrations",
+ &state->registrations_json);
+ if (ret != 0) {
+ json_free(&root_json);
+ TALLOC_FREE(frame);
+ return false;
+ }
+ state->registrations_json = json_empty_object;
+
+ json_str = json_to_string(frame, &root_json);
+ json_free(&root_json);
+ if (json_str == NULL) {
+ TALLOC_FREE(frame);
+ return false;
+ }
+
+ d_printf("%s\n", json_str);
+ TALLOC_FREE(frame);
+ return true;
+#else /* not HAVE_JANSSON */
+ return true;
+#endif /* not HAVE_JANSSON */
+}
+
+static void net_witness_scan_registrations_free(
+ struct net_witness_scan_registrations_state *state)
+{
+#ifdef HAVE_JANSSON
+ if (!json_is_invalid(&state->filters_json)) {
+ json_free(&state->filters_json);
+ }
+ if (!json_is_invalid(&state->registrations_json)) {
+ json_free(&state->registrations_json);
+ }
+#endif /* HAVE_JANSSON */
+
+ net_witness_scan_registrations_regex_free(&state->net_name);
+ net_witness_scan_registrations_regex_free(&state->share_name);
+ net_witness_scan_registrations_regex_free(&state->ip_address);
+ net_witness_scan_registrations_regex_free(&state->client_computer);
+}
+
+#ifdef HAVE_JANSSON
+static int dump_registration_json(struct json_object *registrations_json,
+ const char *key_str,
+ const struct rpcd_witness_registration *rg)
+{
+ struct json_object jsobj = json_empty_object;
+ struct json_object flags_json = json_empty_object;
+ struct json_object context_json = json_empty_object;
+ struct json_object serverid_json = json_empty_object;
+ struct json_object auth_json = json_empty_object;
+ struct json_object connection_json = json_empty_object;
+ struct timeval tv;
+ struct dom_sid_buf sid_buf;
+ int ret = 0;
+
+ jsobj = json_new_object();
+ if (json_is_invalid(&jsobj)) {
+ d_fprintf(stderr, _("error setting up JSON value\n"));
+ goto failure;
+ }
+
+ ret = json_add_flags32(&jsobj, "version", rg->version);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "net_name", rg->net_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "share_name", rg->share_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "ip_address", rg->ip_address);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ flags_json = json_new_object();
+ if (json_is_invalid(&flags_json)) {
+ goto failure;
+ }
+
+ ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION",
+ (rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ?
+ true : false);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&flags_json, "int", rg->flags);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_flags32(&flags_json, "hex", rg->flags);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "flags", &flags_json);
+ if (ret != 0) {
+ goto failure;
+ }
+ flags_json = json_empty_object;
+
+ ret = json_add_int(&jsobj, "timeout", rg->timeout);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ context_json = json_new_object();
+ if (json_is_invalid(&context_json)) {
+ goto failure;
+ }
+
+ ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "context_handle", &context_json);
+ if (ret != 0) {
+ goto failure;
+ }
+ context_json = json_empty_object;
+
+ serverid_json = json_new_object();
+ if (json_is_invalid(&serverid_json)) {
+ goto failure;
+ }
+
+ ret = json_add_int(&serverid_json, "pid", rg->server_id.pid);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "server_id", &serverid_json);
+ if (ret != 0) {
+ goto failure;
+ }
+ serverid_json = json_empty_object;
+
+ auth_json = json_new_object();
+ if (json_is_invalid(&auth_json)) {
+ goto failure;
+ }
+
+ ret = json_add_string(&auth_json, "account_name", rg->account_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&auth_json, "domain_name", rg->domain_name);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&auth_json,
+ "account_sid",
+ dom_sid_str_buf(&rg->account_sid, &sid_buf));
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "auth", &auth_json);
+ if (ret != 0) {
+ goto failure;
+ }
+ auth_json = json_empty_object;
+
+ connection_json = json_new_object();
+ if (json_is_invalid(&connection_json)) {
+ goto failure;
+ }
+
+ ret = json_add_string(&connection_json, "local_address", rg->local_address);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_string(&connection_json, "remote_address", rg->remote_address);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(&jsobj, "connection", &connection_json);
+ if (ret != 0) {
+ goto failure;
+ }
+ connection_json = json_empty_object;
+
+ nttime_to_timeval(&tv, rg->registration_time);
+ ret = json_add_time(&jsobj, "registration_time", tv);
+ if (ret != 0) {
+ goto failure;
+ }
+
+ ret = json_add_object(registrations_json, key_str, &jsobj);
+ if (ret != 0) {
+ goto failure;
+ }
+ jsobj = json_empty_object;
+
+failure:
+ if (!json_is_invalid(&connection_json)) {
+ json_free(&connection_json);
+ }
+ if (!json_is_invalid(&auth_json)) {
+ json_free(&auth_json);
+ }
+ if (!json_is_invalid(&serverid_json)) {
+ json_free(&serverid_json);
+ }
+ if (!json_is_invalid(&context_json)) {
+ json_free(&context_json);
+ }
+ if (!json_is_invalid(&flags_json)) {
+ json_free(&flags_json);
+ }
+ if (!json_is_invalid(&jsobj)) {
+ json_free(&jsobj);
+ }
+
+ return ret;
+}
+#endif /* HAVE_JANSSON */
+
+static NTSTATUS net_witness_scan_registrations_dump_rg(
+ struct net_witness_scan_registrations_state *state,
+ const struct rpcd_witness_registration *rg)
+{
+ struct net_context *c = state->c;
+ struct GUID_txt_buf key_buf;
+ const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf);
+
+ if (c->opt_json) {
+#ifdef HAVE_JANSSON
+ int ret;
+
+ ret = dump_registration_json(&state->registrations_json,
+ key_str,
+ rg);
+ if (ret != 0) {
+ d_fprintf(stderr, "dump_registration_json(%s) failed\n",
+ key_str);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+#endif /* HAVE_JANSSON */
+ return NT_STATUS_OK;
+ }
+
+ d_printf("%-36s %-20s %-15s %-20s %s\n",
+ key_str,
+ rg->net_name,
+ rg->share_name ? rg->share_name : "''",
+ rg->ip_address,
+ rg->client_computer_name);
+
+ return NT_STATUS_OK;
+}
+
+static void net_witness_scan_registrations_parser(TDB_DATA key,
+ TDB_DATA val,
+ void *private_data)
+{
+ struct net_witness_scan_registrations_state *state =
+ (struct net_witness_scan_registrations_state *)private_data;
+ DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize);
+ struct rpcd_witness_registration rg;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *frame = NULL;
+ bool match = false;
+
+ if (val_blob.length == 0) {
+ return;
+ }
+
+ frame = talloc_stackframe();
+
+ ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg,
+ (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:"
+ "key '%s' ndr_pull_struct_blob - %s\n",
+ tdb_data_dbg(key),
+ ndr_errstr(ndr_err));
+ state->error = ndr_map_error2ntstatus(ndr_err);
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ if (!serverid_exists(&rg.server_id)) {
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration, &rg);
+ }
+
+ match = net_witness_scan_registrations_match(state, &rg);
+ if (!NT_STATUS_IS_OK(state->error)) {
+ TALLOC_FREE(frame);
+ return;
+ }
+ if (!match) {
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ match = state->action->match_fn(state->action->private_data, &rg);
+ if (!match) {
+ TALLOC_FREE(frame);
+ return;
+ }
+
+ state->error = state->action->process_fn(state->action->private_data, &rg);
+ if (NT_STATUS_IS_OK(state->error)) {
+ state->error = net_witness_scan_registrations_dump_rg(state,
+ &rg);
+ }
+ TALLOC_FREE(frame);
+}
+
+static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data)
+{
+ struct net_witness_scan_registrations_state *state =
+ (struct net_witness_scan_registrations_state *)private_data;
+ TDB_DATA key = dbwrap_record_get_key(rec);
+ TDB_DATA val = dbwrap_record_get_value(rec);
+
+ net_witness_scan_registrations_parser(key, val, private_data);
+
+ if (!NT_STATUS_IS_OK(state->error)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_witness_scan_registrations(struct net_context *c,
+ struct json_object *message_json,
+ const struct net_witness_scan_registrations_action_state *action)
+{
+ struct net_witness_scan_registrations_state state = {
+ .c = c,
+ .message_json = message_json,
+ .action = action,
+ };
+ struct db_context *db = NULL;
+ NTSTATUS status;
+ bool ok;
+
+ db = net_witness_open_registration_db();
+ if (db == NULL) {
+ d_printf("net_witness_open_registration_db() failed\n");
+ return -1;
+ }
+
+ ok = net_witness_scan_registrations_init(&state);
+ if (!ok) {
+ d_printf("net_witness_scan_registrations_init() failed\n");
+ return -1;
+ }
+
+ if (c->opt_witness_registration != NULL) {
+ const char *key_str = c->opt_witness_registration;
+ DATA_BLOB key_blob = data_blob_string_const(key_str);
+ TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length);
+
+ status = dbwrap_parse_record(db,
+ key,
+ net_witness_scan_registrations_parser,
+ &state);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dbwrap_parse_record(%s) failed: %s\n",
+ key_str, nt_errstr(status));
+ net_witness_scan_registrations_free(&state);
+ return -1;
+ }
+ if (!NT_STATUS_IS_OK(state.error)) {
+ d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n",
+ key_str, nt_errstr(state.error));
+ net_witness_scan_registrations_free(&state);
+ return -1;
+ }
+ } else {
+ status = dbwrap_traverse_read(db,
+ net_witness_scan_registrations_traverse_cb,
+ &state,
+ NULL); /* count */
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("dbwrap_traverse_read() failed\n");
+ net_witness_scan_registrations_free(&state);
+ return -1;
+ }
+ if (!NT_STATUS_IS_OK(state.error)) {
+ d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n",
+ nt_errstr(state.error));
+ net_witness_scan_registrations_free(&state);
+ return -1;
+ }
+ }
+
+ ok = net_witness_scan_registrations_finish(&state);
+ if (!ok) {
+ d_printf("net_witness_scan_registrations_finish() failed\n");
+ return -1;
+ }
+
+ net_witness_scan_registrations_free(&state);
+ return 0;
+}
+
+struct net_witness_list_state {
+ struct net_context *c;
+};
+
+static bool net_witness_list_prepare_fn(void *private_data)
+{
+ return true;
+}
+
+static bool net_witness_list_match_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ return true;
+}
+
+static NTSTATUS net_witness_list_process_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ return NT_STATUS_OK;
+}
+
+static void net_witness_filter_usage(void)
+{
+ d_printf(" Note: Only supported with clustering=yes!\n\n");
+ d_printf(" Machine readable output can be generated with "
+ "the following option:\n"
+ "\n"
+ " --json\n"
+ "\n");
+ d_printf(" The selection of registrations can be limited by "
+ "the following options:\n"
+ "\n"
+ " --witness-registration=REGISTRATION_UUID\n"
+ " This does a direct lookup for REGISTRATION_UUID\n"
+ " instead of doing a database traversal.\n"
+ "\n"
+ " The following options all take a "
+ "POSIX Extended Regular Expression,\n"
+ " which can further filter the selection of "
+ "registrations.\n"
+ " These options are applied as logical AND, "
+ "but each REGEX \n"
+ " allows specifying multiple strings using "
+ "the pipe symbol.\n"
+ "\n"
+ " --witness-net-name=REGEX\n"
+ " This specifies the 'server name' the client\n"
+ " registered for monitoring.\n"
+ "\n"
+ " --witness-share-name=REGEX\n"
+ " This specifies the 'share name' the client\n"
+ " registered for monitoring.\n"
+ " Note that the share name is optional in the\n"
+ " registration, otherwise an empty string is \n"
+ " matched.\n"
+ "\n"
+ " --witness-ip-address=REGEX\n"
+ " This specifies the ip address the client\n"
+ " registered for monitoring.\n"
+ "\n"
+ " --witness-client-computer-name=REGEX\n"
+ " This specifies the client computer name the client\n"
+ " specified in the registration.\n"
+ " Note it is just a string chosen by the "
+ "client itself.\n"
+ "\n");
+}
+
+static void net_witness_list_usage(void)
+{
+ d_printf("%s\n"
+ "net witness list\n"
+ " %s\n\n",
+ _("Usage:"),
+ _("List witness registrations "
+ "from rpcd_witness_registration.tdb"));
+ net_witness_filter_usage();
+}
+
+static int net_witness_list(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_witness_list_state state = { .c = c, };
+ struct net_witness_scan_registrations_action_state action = {
+ .prepare_fn = net_witness_list_prepare_fn,
+ .match_fn = net_witness_list_match_fn,
+ .process_fn = net_witness_list_process_fn,
+ .private_data = &state,
+ };
+ int ret = -1;
+
+ if (c->display_usage) {
+ net_witness_list_usage();
+ goto out;
+ }
+
+ if (argc != 0) {
+ net_witness_list_usage();
+ goto out;
+ }
+
+ if (!lp_clustering()) {
+ d_printf("ERROR: Only supported with clustering=yes!\n\n");
+ goto out;
+ }
+
+ ret = net_witness_scan_registrations(c, NULL, &action);
+ if (ret != 0) {
+ d_printf("net_witness_scan_registrations() failed\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+struct net_witness_client_move_state {
+ struct net_context *c;
+ struct rpcd_witness_registration_updateB m;
+ char *headline;
+};
+
+static bool net_witness_client_move_prepare_fn(void *private_data)
+{
+ struct net_witness_client_move_state *state =
+ (struct net_witness_client_move_state *)private_data;
+
+ if (state->headline != NULL) {
+ d_printf("%s\n", state->headline);
+ TALLOC_FREE(state->headline);
+ }
+
+ return true;
+}
+
+static bool net_witness_client_move_match_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ return true;
+}
+
+static NTSTATUS net_witness_client_move_process_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ struct net_witness_client_move_state *state =
+ (struct net_witness_client_move_state *)private_data;
+ struct net_context *c = state->c;
+ struct rpcd_witness_registration_updateB update = {
+ .context_handle = rg->context_handle,
+ .type = state->m.type,
+ .update = state->m.update,
+ };
+ DATA_BLOB blob = { .length = 0, };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ if (state->headline != NULL) {
+ d_printf("%s\n", state->headline);
+ TALLOC_FREE(state->headline);
+ }
+
+ SMB_ASSERT(update.type != 0);
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
+ (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ status = messaging_send(c->msg_ctx,
+ rg->server_id,
+ MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
+ &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void net_witness_update_usage(void)
+{
+ d_printf(" If the update should be applied to all registrations\n"
+ " it needs to be explicitly specified:\n"
+ "\n"
+ " --witness-apply-to-all\n"
+ " This selects all registrations.\n"
+ " Note: This is mutual exclusive to "
+ "the above options.\n"
+ "\n");
+}
+
+static bool net_witness_verify_update_options(struct net_context *c)
+{
+ if (c->opt_witness_registration == NULL &&
+ c->opt_witness_net_name == NULL &&
+ c->opt_witness_share_name == NULL &&
+ c->opt_witness_ip_address == NULL &&
+ c->opt_witness_client_computer_name == NULL &&
+ c->opt_witness_apply_to_all == 0)
+ {
+ d_printf("--witness-apply-to-all or "
+ "at least one of following requires:\n"
+ "--witness-registration\n"
+ "--witness-net-name\n"
+ "--witness-share-name\n"
+ "--witness-ip-address\n"
+ "--witness-client-computer-name\n");
+ return false;
+ }
+
+ if (c->opt_witness_apply_to_all == 0) {
+ return true;
+ }
+
+ if (c->opt_witness_registration != NULL ||
+ c->opt_witness_net_name != NULL ||
+ c->opt_witness_share_name != NULL ||
+ c->opt_witness_ip_address != NULL ||
+ c->opt_witness_client_computer_name != NULL)
+ {
+ d_printf("--witness-apply-to-all not allowed "
+ "together with the following options:\n"
+ "--witness-registration\n"
+ "--witness-net-name\n"
+ "--witness-share-name\n"
+ "--witness-ip-address\n"
+ "--witness-client-computer-name\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void net_witness_move_usage(const char *name)
+{
+ d_printf(" The content of the %s notification contains ip addresses\n"
+ " specified by (exactly one) of the following options:\n"
+ "\n"
+ " --witness-new-node=NODEID\n"
+ " By specifying a NODEID all ip addresses\n"
+ " currently available on the given node are\n"
+ " included in the response.\n"
+ " By specifying '-1' as NODEID all ip addresses\n"
+ " of the cluster are included in the response.\n"
+ "\n"
+ " --witness-new-ip=IPADDRESS\n"
+ " By specifying an IPADDRESS only the specified\n"
+ " ip address is included in the response.\n"
+ "\n",
+ name);
+}
+
+static bool net_witness_verify_move_options(struct net_context *c,
+ uint32_t *new_node,
+ bool *is_ipv4,
+ bool *is_ipv6)
+{
+ bool ok;
+
+ *new_node = NONCLUSTER_VNN;
+ *is_ipv4 = false;
+ *is_ipv6 = false;
+
+ ok = net_witness_verify_update_options(c);
+ if (!ok) {
+ return false;
+ }
+
+ if (c->opt_witness_new_ip != NULL &&
+ c->opt_witness_new_node != -2)
+ {
+ d_printf("--witness-new-ip and "
+ "--witness-new-node are not allowed together\n");
+ return false;
+ }
+
+ if (c->opt_witness_new_ip == NULL &&
+ c->opt_witness_new_node == -2)
+ {
+ d_printf("--witness-new-ip or --witness-new-node required\n");
+ return false;
+ }
+
+ if (c->opt_witness_new_node != -2) {
+ *new_node = c->opt_witness_new_node;
+ return true;
+ }
+
+ if (is_ipaddress_v4(c->opt_witness_new_ip)) {
+ *is_ipv4 = true;
+ return true;
+ }
+
+ if (is_ipaddress_v6(c->opt_witness_new_ip)) {
+ *is_ipv6 = true;
+ return true;
+ }
+
+ d_printf("Invalid ip address for --witness-new-ip=%s\n",
+ c->opt_witness_new_ip);
+ return false;
+}
+
+#ifdef HAVE_JANSSON
+static bool net_witness_move_message_json(struct net_context *c,
+ const char *msg_type,
+ struct json_object *pmessage_json)
+{
+ struct json_object message_json = json_empty_object;
+ int ret;
+
+ message_json = json_new_object();
+ if (json_is_invalid(&message_json)) {
+ return false;
+ }
+
+ ret = json_add_string(&message_json,
+ "type",
+ msg_type);
+ if (ret != 0) {
+ json_free(&message_json);
+ return false;
+ }
+
+ if (c->opt_witness_new_ip != NULL) {
+ ret = json_add_string(&message_json,
+ "new_ip",
+ c->opt_witness_new_ip);
+ if (ret != 0) {
+ return false;
+ }
+ } else if (c->opt_witness_new_node != -1) {
+ ret = json_add_int(&message_json,
+ "new_node",
+ c->opt_witness_new_node);
+ if (ret != 0) {
+ return false;
+ }
+ } else {
+ ret = json_add_bool(&message_json,
+ "all_nodes",
+ true);
+ if (ret != 0) {
+ return false;
+ }
+ }
+
+ *pmessage_json = message_json;
+ return true;
+}
+#endif /* HAVE_JANSSON */
+
+static void net_witness_client_move_usage(void)
+{
+ d_printf("%s\n"
+ "net witness client-move\n"
+ " %s\n\n",
+ _("Usage:"),
+ _("Generate client move notifications for "
+ "witness registrations to a new ip or node"));
+ net_witness_filter_usage();
+ net_witness_update_usage();
+ net_witness_move_usage("CLIENT_MOVE");
+}
+
+static int net_witness_client_move(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_witness_client_move_state state = { .c = c, };
+ struct rpcd_witness_registration_updateB *m = &state.m;
+#ifdef HAVE_JANSSON
+ struct json_object _message_json = json_empty_object;
+#endif /* HAVE_JANSSON */
+ struct json_object *message_json = NULL;
+ struct net_witness_scan_registrations_action_state action = {
+ .prepare_fn = net_witness_client_move_prepare_fn,
+ .match_fn = net_witness_client_move_match_fn,
+ .process_fn = net_witness_client_move_process_fn,
+ .private_data = &state,
+ };
+ int ret = -1;
+ const char *msg_type = NULL;
+ uint32_t new_node = NONCLUSTER_VNN;
+ bool is_ipv4 = false;
+ bool is_ipv6 = false;
+ bool ok;
+
+ if (c->display_usage) {
+ net_witness_client_move_usage();
+ goto out;
+ }
+
+ if (argc != 0) {
+ net_witness_client_move_usage();
+ goto out;
+ }
+
+ if (!lp_clustering()) {
+ d_printf("ERROR: Only supported with clustering=yes!\n\n");
+ goto out;
+ }
+
+ ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
+ if (!ok) {
+ goto out;
+ }
+
+ if (is_ipv4) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV4;
+ m->update.client_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
+ msg_type = "CLIENT_MOVE_TO_IPV4";
+ state.headline = talloc_asprintf(frame,
+ "CLIENT_MOVE_TO_IPV4: %s",
+ c->opt_witness_new_ip);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else if (is_ipv6) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV6;
+ m->update.client_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
+ msg_type = "CLIENT_MOVE_TO_IPV6";
+ state.headline = talloc_asprintf(frame,
+ "CLIENT_MOVE_TO_IPV6: %s",
+ c->opt_witness_new_ip);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else if (new_node != NONCLUSTER_VNN) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
+ m->update.client_move_to_node.new_node = new_node;
+ msg_type = "CLIENT_MOVE_TO_NODE";
+ state.headline = talloc_asprintf(frame,
+ "CLIENT_MOVE_TO_NODE: %u",
+ new_node);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE;
+ m->update.client_move_to_node.new_node = NONCLUSTER_VNN;
+ msg_type = "CLIENT_MOVE_TO_NODE";
+ state.headline = talloc_asprintf(frame,
+ "CLIENT_MOVE_TO_NODE: ALL");
+ if (state.headline == NULL) {
+ goto out;
+ }
+ }
+
+#ifdef HAVE_JANSSON
+ if (c->opt_json) {
+ TALLOC_FREE(state.headline);
+
+ ok = net_witness_move_message_json(c,
+ msg_type,
+ &_message_json);
+ if (!ok) {
+ d_printf("net_witness_move_message_json(%s) failed\n",
+ msg_type);
+ goto out;
+ }
+
+ message_json = &_message_json;
+ }
+#else /* not HAVE_JANSSON */
+ (void)msg_type;
+#endif /* not HAVE_JANSSON */
+
+ ret = net_witness_scan_registrations(c, message_json, &action);
+ if (ret != 0) {
+ d_printf("net_witness_scan_registrations() failed\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+#ifdef HAVE_JANSSON
+ if (!json_is_invalid(&_message_json)) {
+ json_free(&_message_json);
+ }
+#endif /* HAVE_JANSSON */
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+struct net_witness_share_move_state {
+ struct net_context *c;
+ struct rpcd_witness_registration_updateB m;
+ char *headline;
+};
+
+static bool net_witness_share_move_prepare_fn(void *private_data)
+{
+ struct net_witness_share_move_state *state =
+ (struct net_witness_share_move_state *)private_data;
+
+ if (state->headline != NULL) {
+ d_printf("%s\n", state->headline);
+ TALLOC_FREE(state->headline);
+ }
+
+ return true;
+}
+
+static bool net_witness_share_move_match_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ if (rg->share_name == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+static NTSTATUS net_witness_share_move_process_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ struct net_witness_share_move_state *state =
+ (struct net_witness_share_move_state *)private_data;
+ struct net_context *c = state->c;
+ struct rpcd_witness_registration_updateB update = {
+ .context_handle = rg->context_handle,
+ .type = state->m.type,
+ .update = state->m.update,
+ };
+ DATA_BLOB blob = { .length = 0, };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ SMB_ASSERT(update.type != 0);
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
+ (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ status = messaging_send(c->msg_ctx,
+ rg->server_id,
+ MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
+ &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void net_witness_share_move_usage(void)
+{
+ d_printf("%s\n"
+ "net witness share-move\n"
+ " %s\n\n",
+ _("Usage:"),
+ _("Generate share move notifications for "
+ "witness registrations to a new ip or node"));
+ net_witness_filter_usage();
+ net_witness_update_usage();
+ d_printf(" Note: This only applies to registrations with "
+ "a non empty share name!\n\n");
+ net_witness_move_usage("SHARE_MOVE");
+}
+
+static int net_witness_share_move(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_witness_share_move_state state = { .c = c, };
+ struct rpcd_witness_registration_updateB *m = &state.m;
+#ifdef HAVE_JANSSON
+ struct json_object _message_json = json_empty_object;
+#endif /* HAVE_JANSSON */
+ struct json_object *message_json = NULL;
+ struct net_witness_scan_registrations_action_state action = {
+ .prepare_fn = net_witness_share_move_prepare_fn,
+ .match_fn = net_witness_share_move_match_fn,
+ .process_fn = net_witness_share_move_process_fn,
+ .private_data = &state,
+ };
+ int ret = -1;
+ const char *msg_type = NULL;
+ uint32_t new_node = NONCLUSTER_VNN;
+ bool is_ipv4 = false;
+ bool is_ipv6 = false;
+ bool ok;
+
+ if (c->display_usage) {
+ net_witness_share_move_usage();
+ goto out;
+ }
+
+ if (argc != 0) {
+ net_witness_share_move_usage();
+ goto out;
+ }
+
+ if (!lp_clustering()) {
+ d_printf("ERROR: Only supported with clustering=yes!\n\n");
+ goto out;
+ }
+
+ ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6);
+ if (!ok) {
+ goto out;
+ }
+
+ if (is_ipv4) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV4;
+ m->update.share_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip;
+ msg_type = "SHARE_MOVE_TO_IPV4";
+ state.headline = talloc_asprintf(frame,
+ "SHARE_MOVE_TO_IPV4: %s",
+ c->opt_witness_new_ip);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else if (is_ipv6) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV6;
+ m->update.share_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip;
+ msg_type = "SHARE_MOVE_TO_IPV6";
+ state.headline = talloc_asprintf(frame,
+ "SHARE_MOVE_TO_IPV6: %s",
+ c->opt_witness_new_ip);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else if (new_node != NONCLUSTER_VNN) {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
+ m->update.share_move_to_node.new_node = new_node;
+ msg_type = "SHARE_MOVE_TO_NODE";
+ state.headline = talloc_asprintf(frame,
+ "SHARE_MOVE_TO_NODE: %u",
+ new_node);
+ if (state.headline == NULL) {
+ goto out;
+ }
+ } else {
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE;
+ m->update.share_move_to_node.new_node = NONCLUSTER_VNN;
+ msg_type = "SHARE_MOVE_TO_NODE";
+ state.headline = talloc_asprintf(frame,
+ "SHARE_MOVE_TO_NODE: ALL");
+ if (state.headline == NULL) {
+ goto out;
+ }
+ }
+
+#ifdef HAVE_JANSSON
+ if (c->opt_json) {
+ TALLOC_FREE(state.headline);
+
+ ok = net_witness_move_message_json(c,
+ msg_type,
+ &_message_json);
+ if (!ok) {
+ d_printf("net_witness_move_message_json(%s) failed\n",
+ msg_type);
+ goto out;
+ }
+
+ message_json = &_message_json;
+ }
+#else /* not HAVE_JANSSON */
+ (void)msg_type;
+#endif /* not HAVE_JANSSON */
+
+ ret = net_witness_scan_registrations(c, message_json, &action);
+ if (ret != 0) {
+ d_printf("net_witness_scan_registrations() failed\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+#ifdef HAVE_JANSSON
+ if (!json_is_invalid(&_message_json)) {
+ json_free(&_message_json);
+ }
+#endif /* HAVE_JANSSON */
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+struct net_witness_force_unregister_state {
+ struct net_context *c;
+ struct rpcd_witness_registration_updateB m;
+ char *headline;
+};
+
+static bool net_witness_force_unregister_prepare_fn(void *private_data)
+{
+ struct net_witness_force_unregister_state *state =
+ (struct net_witness_force_unregister_state *)private_data;
+
+ if (state->headline != NULL) {
+ d_printf("%s\n", state->headline);
+ TALLOC_FREE(state->headline);
+ }
+
+ return true;
+}
+
+static bool net_witness_force_unregister_match_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ return true;
+}
+
+static NTSTATUS net_witness_force_unregister_process_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ struct net_witness_force_unregister_state *state =
+ (struct net_witness_force_unregister_state *)private_data;
+ struct net_context *c = state->c;
+ struct rpcd_witness_registration_updateB update = {
+ .context_handle = rg->context_handle,
+ .type = state->m.type,
+ .update = state->m.update,
+ };
+ DATA_BLOB blob = { .length = 0, };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ SMB_ASSERT(update.type != 0);
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
+ (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ status = messaging_send(c->msg_ctx,
+ rg->server_id,
+ MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
+ &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void net_witness_force_unregister_usage(void)
+{
+ d_printf("%s\n"
+ "net witness force-unregister\n"
+ " %s\n\n",
+ _("Usage:"),
+ _("Force unregistrations for witness registrations"));
+ net_witness_filter_usage();
+ net_witness_update_usage();
+ d_printf(" The selected registrations are removed on "
+ "the server and\n"
+ " any pending AsyncNotify request will get "
+ "a NOT_FOUND error.\n"
+ "\n"
+ " Typically this triggers a clean re-registration "
+ "on the client.\n"
+ "\n");
+}
+
+static int net_witness_force_unregister(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_witness_force_unregister_state state = { .c = c, };
+ struct rpcd_witness_registration_updateB *m = &state.m;
+#ifdef HAVE_JANSSON
+ struct json_object _message_json = json_empty_object;
+#endif /* HAVE_JANSSON */
+ struct json_object *message_json = NULL;
+ struct net_witness_scan_registrations_action_state action = {
+ .prepare_fn = net_witness_force_unregister_prepare_fn,
+ .match_fn = net_witness_force_unregister_match_fn,
+ .process_fn = net_witness_force_unregister_process_fn,
+ .private_data = &state,
+ };
+ int ret = -1;
+ bool ok;
+
+ if (c->display_usage) {
+ net_witness_force_unregister_usage();
+ goto out;
+ }
+
+ if (argc != 0) {
+ net_witness_force_unregister_usage();
+ goto out;
+ }
+
+ if (!lp_clustering()) {
+ d_printf("ERROR: Only supported with clustering=yes!\n\n");
+ goto out;
+ }
+
+ ok = net_witness_verify_update_options(c);
+ if (!ok) {
+ goto out;
+ }
+
+ m->type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER;
+
+ state.headline = talloc_asprintf(frame, "FORCE_UNREGISTER:");
+ if (state.headline == NULL) {
+ goto out;
+ }
+
+#ifdef HAVE_JANSSON
+ if (c->opt_json) {
+ TALLOC_FREE(state.headline);
+
+ _message_json = json_new_object();
+ if (json_is_invalid(&_message_json)) {
+ goto out;
+ }
+
+ ret = json_add_string(&_message_json,
+ "type",
+ "FORCE_UNREGISTER");
+ if (ret != 0) {
+ goto out;
+ }
+
+ message_json = &_message_json;
+ }
+#endif /* HAVE_JANSSON */
+
+ ret = net_witness_scan_registrations(c, message_json, &action);
+ if (ret != 0) {
+ d_printf("net_witness_scan_registrations() failed\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+#ifdef HAVE_JANSSON
+ if (!json_is_invalid(&_message_json)) {
+ json_free(&_message_json);
+ }
+#endif /* HAVE_JANSSON */
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+struct net_witness_force_response_state {
+ struct net_context *c;
+ struct rpcd_witness_registration_updateB m;
+#ifdef HAVE_JANSSON
+ struct json_object json_root;
+#endif /* HAVE_JANSSON */
+ char *headline;
+};
+
+#ifdef HAVE_JANSSON
+static NTSTATUS net_witness_force_response_parse_rc(
+ struct net_witness_force_response_state *state,
+ json_t *jsmsg,
+ TALLOC_CTX *mem_ctx,
+ size_t mi,
+ union witness_notifyResponse_message *message)
+{
+ struct witness_ResourceChange *rc = &message->resource_change;
+ json_t *jsctype = NULL;
+ json_int_t ctype;
+ json_t *jscname = NULL;
+ const char *cname = NULL;
+
+ if (!json_is_object(jsmsg)) {
+ DBG_ERR("'message[%zu]' needs to be an object\n", mi);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ jsctype = json_object_get(jsmsg, "type");
+ if (jsctype == NULL) {
+ DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!json_is_integer(jsctype)) {
+ DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ctype = json_integer_value(jsctype);
+
+ jscname = json_object_get(jsmsg, "name");
+ if (jscname == NULL) {
+ DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!json_is_string(jscname)) {
+ DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ cname = json_string_value(jscname);
+
+ rc->type = ctype;
+ rc->name = talloc_strdup(mem_ctx, cname);
+ if (rc->name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS net_witness_force_response_parse_ipl(
+ struct net_witness_force_response_state *state,
+ json_t *jsmsg,
+ TALLOC_CTX *mem_ctx,
+ size_t mi,
+ union witness_notifyResponse_message *message)
+{
+ struct witness_IPaddrInfoList *ipl =
+ &message->client_move;
+ size_t ai, num_addrs = 0;
+ struct witness_IPaddrInfo *addrs = NULL;
+
+ if (!json_is_array(jsmsg)) {
+ DBG_ERR("'messages[%zu]' needs to be an array\n", mi);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ num_addrs = json_array_size(jsmsg);
+ if (num_addrs > UINT32_MAX) {
+ DBG_ERR("Too many elements in 'messages[%zu]': %zu\n",
+ mi, num_addrs);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ addrs = talloc_zero_array(mem_ctx,
+ struct witness_IPaddrInfo,
+ num_addrs);
+ if (addrs == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (ai = 0; ai < num_addrs; ai++) {
+ struct witness_IPaddrInfo *info =
+ &addrs[ai];
+ json_t *jsaddr = json_array_get(jsmsg, ai);
+ json_t *jsflags = NULL;
+ json_int_t flags;
+ json_t *jsipv4 = NULL;
+ const char *ipv4 = NULL;
+ json_t *jsipv6 = NULL;
+ const char *ipv6 = NULL;
+
+ if (!json_is_object(jsaddr)) {
+ DBG_ERR("'messages[%zu][%zu]' needs to be an object\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ jsflags = json_object_get(jsaddr, "flags");
+ if (jsflags == NULL) {
+ DBG_ERR("'messages[%zu][%zu]['flags']' missing\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!json_is_integer(jsflags)) {
+ DBG_ERR("'messages[%zu][%zu]['flags']' "
+ "needs to be an integer\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ flags = json_integer_value(jsflags);
+
+ jsipv4 = json_object_get(jsaddr, "ipv4");
+ if (jsipv4 != NULL) {
+ if (!json_is_string(jsipv4)) {
+ DBG_ERR("'messages[%zu][%zu]['ipv4']' "
+ "needs to be a string\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ipv4 = json_string_value(jsipv4);
+ if (!is_ipaddress_v4(ipv4)) {
+ DBG_ERR("'messages[%zu][%zu]['ipv4']' "
+ "needs to be a valid ipv4 address\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ ipv4 = "0.0.0.0";
+ }
+
+ jsipv6 = json_object_get(jsaddr, "ipv6");
+ if (jsipv6 != NULL) {
+ if (!json_is_string(jsipv6)) {
+ DBG_ERR("'messages[%zu][%zu]['ipv6']' "
+ "needs to be a string\n",
+ mi, ai);
+ DBG_ERR("%s: INVALID_PARAMETER\n", __location__);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ ipv6 = json_string_value(jsipv6);
+ if (!is_ipaddress_v6(ipv6)) {
+ DBG_ERR("'messages[%zu][%zu]['ipv4']' "
+ "needs to be a valid ipv6 address\n",
+ mi, ai);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ } else {
+ ipv6 = "::";
+ }
+
+ info->flags = flags;
+ info->ipv4 = talloc_strdup(addrs, ipv4);
+ if (info->ipv4 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->ipv6 = talloc_strdup(addrs, ipv6);
+ if (info->ipv6 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ ipl->num = num_addrs;
+ ipl->addr = addrs;
+
+ return NT_STATUS_OK;
+}
+#endif /* HAVE_JANSSON */
+
+static NTSTATUS net_witness_force_response_parse(struct net_witness_force_response_state *state)
+{
+#ifdef HAVE_JANSSON
+ struct net_context *c = state->c;
+ struct rpcd_witness_registration_update_force_response *force = NULL;
+ struct witness_notifyResponse *response = NULL;
+ size_t mi, num_messages = 0;
+ union witness_notifyResponse_message *messages = NULL;
+ json_t *jsroot = NULL;
+ json_t *jsresult = NULL;
+ json_t *jsresponse = NULL;
+ json_t *jstype = NULL;
+ json_t *jsmessages = NULL;
+
+ if (c->opt_witness_forced_response != NULL) {
+ const char *str = c->opt_witness_forced_response;
+ size_t flags = JSON_REJECT_DUPLICATES;
+ json_error_t jserror;
+
+ jsroot = json_loads(str, flags, &jserror);
+ if (jsroot == NULL) {
+ DBG_ERR("Invalid JSON in "
+ "--witness-forced-response='%s'\n",
+ str);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ state->json_root = (struct json_object) {
+ .root = jsroot,
+ .valid = true,
+ };
+ }
+
+ state->m.type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_RESPONSE;
+ force = &state->m.update.force_response;
+ force->response = NULL;
+ force->result = WERR_OK;
+
+ if (jsroot == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ jsresult = json_object_get(jsroot, "result");
+ if (jsresult != NULL) {
+ int val_type = json_typeof(jsresult);
+
+ switch (val_type) {
+ case JSON_INTEGER: {
+ json_int_t val = json_integer_value(jsresult);
+
+ if (val > UINT32_MAX) {
+ DBG_ERR("Invalid 'result' value: %d\n",
+ (int) val);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (val < 0) {
+ DBG_ERR("invalid 'result' value: %d\n",
+ (int) val);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ force->result = W_ERROR(val);
+ }; break;
+ default:
+ DBG_ERR("Invalid json type for 'result' - needs integer\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ jsresponse = json_object_get(jsroot, "response");
+ if (jsresponse == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ if (!json_is_object(jsresponse)) {
+ DBG_ERR("Invalid json type 'response' needs object\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ response = talloc_zero(talloc_tos(), struct witness_notifyResponse);
+ if (response == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ jstype = json_object_get(jsresponse, "type");
+ if (jstype == NULL) {
+ DBG_ERR("Missing 'type' element in 'response'\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ {
+ int val_type = json_typeof(jstype);
+
+ switch (val_type) {
+ case JSON_INTEGER: {
+ json_int_t val = json_integer_value(jstype);
+
+ if (val > WITNESS_NOTIFY_IP_CHANGE) {
+ DBG_ERR("invalid 'type' value in 'response': "
+ "%d\n", (int) val);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (val < WITNESS_NOTIFY_RESOURCE_CHANGE) {
+ DBG_ERR("invalid 'type' value in 'response': "
+ "%d\n", (int) val);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ response->type = val;
+ }; break;
+ default:
+ DBG_ERR("Invalid json type for 'type' in 'response' "
+ "- needs integer\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ force->response = response;
+
+ jsmessages = json_object_get(jsresponse, "messages");
+ if (jsmessages == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ if (!json_is_array(jsmessages)) {
+ DBG_ERR("'messages' in 'response' needs to be an array\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ num_messages = json_array_size(jsmessages);
+ if (num_messages > UINT32_MAX) {
+ DBG_ERR("Too many elements in 'messages': %zu\n",
+ num_messages);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ messages = talloc_zero_array(response,
+ union witness_notifyResponse_message,
+ num_messages);
+ if (messages == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (mi = 0; mi < num_messages; mi++) {
+ json_t *jsmsg = json_array_get(jsmessages, mi);
+ union witness_notifyResponse_message *message = &messages[mi];
+ NTSTATUS status;
+
+ switch (response->type) {
+ case WITNESS_NOTIFY_RESOURCE_CHANGE:
+ status = net_witness_force_response_parse_rc(state,
+ jsmsg,
+ messages,
+ mi,
+ message);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *fn =
+ "net_witness_force_response_parse_rc";
+ DBG_ERR("%s failed: %s\n",
+ fn, nt_errstr(status));
+ return status;
+ }
+
+ break;
+ case WITNESS_NOTIFY_CLIENT_MOVE:
+ case WITNESS_NOTIFY_SHARE_MOVE:
+ case WITNESS_NOTIFY_IP_CHANGE:
+ status = net_witness_force_response_parse_ipl(state,
+ jsmsg,
+ messages,
+ mi,
+ message);
+ if (!NT_STATUS_IS_OK(status)) {
+ const char *fn =
+ "net_witness_force_response_parse_ipl";
+ DBG_ERR("%s failed: %s\n",
+ fn, nt_errstr(status));
+ return status;
+ }
+
+ break;
+ }
+ }
+
+ response->num = num_messages;
+ response->messages = messages;
+
+ return NT_STATUS_OK;
+#else /* not HAVE_JANSSON */
+ d_fprintf(stderr, _("JSON support not available\n"));
+ return NT_STATUS_NOT_IMPLEMENTED;
+#endif /* not HAVE_JANSSON */
+}
+
+static bool net_witness_force_response_prepare_fn(void *private_data)
+{
+ struct net_witness_force_response_state *state =
+ (struct net_witness_force_response_state *)private_data;
+
+ if (state->headline != NULL) {
+ d_printf("%s\n", state->headline);
+ TALLOC_FREE(state->headline);
+ }
+
+ return true;
+}
+
+static bool net_witness_force_response_match_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ return true;
+}
+
+static NTSTATUS net_witness_force_response_process_fn(void *private_data,
+ const struct rpcd_witness_registration *rg)
+{
+ struct net_witness_force_response_state *state =
+ (struct net_witness_force_response_state *)private_data;
+ struct net_context *c = state->c;
+ struct rpcd_witness_registration_updateB update = {
+ .context_handle = rg->context_handle,
+ .type = state->m.type,
+ .update = state->m.update,
+ };
+ DATA_BLOB blob = { .length = 0, };
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ SMB_ASSERT(update.type != 0);
+
+ if (DEBUGLVL(DBGLVL_DEBUG)) {
+ NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update,
+ (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ status = ndr_map_error2ntstatus(ndr_err);
+ DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ status = messaging_send(c->msg_ctx,
+ rg->server_id,
+ MSG_RPCD_WITNESS_REGISTRATION_UPDATE,
+ &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("messaging_send() - %s\n", nt_errstr(status));
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static void net_witness_force_response_usage(void)
+{
+ d_printf("%s\n"
+ "net witness force-response\n"
+ " %s\n\n",
+ _("Usage:"),
+ _("Force an AsyncNotify response based on "
+ "json input (mostly for testing)"));
+ net_witness_filter_usage();
+ net_witness_update_usage();
+ d_printf(" Note this is designed for testing and debugging!\n"
+ "\n"
+ " In short it is not designed to be used by "
+ "administrators,\n"
+ " but developers and automated tests.\n"
+ "\n"
+ " By default an empty response with WERR_OK is generated,\n"
+ " but basically any valid response can be specified by a\n"
+ " specifying a JSON string:\n"
+ "\n"
+ " --witness-forced-response=JSON\n"
+ " This allows the generation of very complex\n"
+ " witness_notifyResponse structures.\n"
+ "\n"
+ " As this is for developers, please read the code\n"
+ " in order to understand all possible values\n"
+ " of the JSON string format...\n"
+ "\n"
+ " Simple examples are:\n"
+ "\n"
+ "# Resource Change:\n%s\n"
+ "\n"
+ "# Client Move:\n%s\n"
+ "\n"
+ "# Share Move:\n%s\n"
+ "\n"
+ "# IP Change:\n%s\n"
+ "\n",
+ "'{ \"result\": 0, \"response\": { \"type\": 1, "
+ "\"messages\": [ { "
+ "\"type\": 255 , "
+ "\"name\": \"some-resource-name\" "
+ "} ]"
+ "}}'",
+ "'{ \"result\": 0, \"response\": { \"type\": 2, "
+ "\"messages\": ["
+ "[{ "
+ "\"flags\": 9, "
+ "\"ipv4\": \"10.0.10.1\" "
+ "}]"
+ "]"
+ "}}'",
+ "'{ \"result\": 0, \"response\": { \"type\": 3, "
+ "\"messages\": ["
+ "[{ "
+ "\"flags\": 9, "
+ "\"ipv4\": \"10.0.10.1\" "
+ "}]"
+ "]"
+ "}}'",
+ "'{ \"result\": 0, \"response\": { \"type\": 4, "
+ "\"messages\": ["
+ "[{ "
+ "\"flags\": 9, "
+ "\"ipv4\": \"10.0.10.1\" "
+ "}]"
+ "]"
+ "}}'");
+}
+
+static int net_witness_force_response(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_witness_force_response_state state = { .c = c, };
+#ifdef HAVE_JANSSON
+ struct json_object _message_json = json_empty_object;
+#endif /* HAVE_JANSSON */
+ struct json_object *message_json = NULL;
+ struct net_witness_scan_registrations_action_state action = {
+ .prepare_fn = net_witness_force_response_prepare_fn,
+ .match_fn = net_witness_force_response_match_fn,
+ .process_fn = net_witness_force_response_process_fn,
+ .private_data = &state,
+ };
+ NTSTATUS status;
+ int ret = -1;
+ bool ok;
+
+ if (c->display_usage) {
+ net_witness_force_response_usage();
+ goto out;
+ }
+
+ if (argc != 0) {
+ net_witness_force_response_usage();
+ goto out;
+ }
+
+ if (!lp_clustering()) {
+ d_printf("ERROR: Only supported with clustering=yes!\n\n");
+ goto out;
+ }
+
+ ok = net_witness_verify_update_options(c);
+ if (!ok) {
+ goto out;
+ }
+
+ status = net_witness_force_response_parse(&state);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("net_witness_force_response_parse failed: %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ state.headline = talloc_asprintf(frame, "FORCE_RESPONSE:%s%s",
+ c->opt_witness_forced_response != NULL ?
+ " " : "",
+ c->opt_witness_forced_response != NULL ?
+ c->opt_witness_forced_response : "");
+
+ if (state.headline == NULL) {
+ goto out;
+ }
+
+#ifdef HAVE_JANSSON
+ if (c->opt_json) {
+ TALLOC_FREE(state.headline);
+
+ _message_json = json_new_object();
+ if (json_is_invalid(&_message_json)) {
+ goto out;
+ }
+
+ ret = json_add_string(&_message_json,
+ "type",
+ "FORCE_RESPONSE");
+ if (ret != 0) {
+ goto out;
+ }
+
+ if (!json_is_invalid(&state.json_root)) {
+ ret = json_add_object(&_message_json,
+ "json",
+ &state.json_root);
+ if (ret != 0) {
+ goto out;
+ }
+ state.json_root = json_empty_object;
+ }
+ message_json = &_message_json;
+ }
+#endif /* HAVE_JANSSON */
+
+ ret = net_witness_scan_registrations(c, message_json, &action);
+ if (ret != 0) {
+ d_printf("net_witness_scan_registrations() failed\n");
+ goto out;
+ }
+
+ ret = 0;
+out:
+#ifdef HAVE_JANSSON
+ if (!json_is_invalid(&_message_json)) {
+ json_free(&_message_json);
+ }
+ if (!json_is_invalid(&state.json_root)) {
+ json_free(&state.json_root);
+ }
+#endif /* HAVE_JANSSON */
+ TALLOC_FREE(frame);
+ return ret;
+}
+
+int net_witness(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "list",
+ net_witness_list,
+ NET_TRANSPORT_LOCAL,
+ N_("List witness registrations "
+ "from rpcd_witness_registration.tdb"),
+ N_("net witness list\n"
+ " List witness registrations "
+ "from rpcd_witness_registration.tdb"),
+ },
+ {
+ "client-move",
+ net_witness_client_move,
+ NET_TRANSPORT_LOCAL,
+ N_("Generate client move notifications for "
+ "witness registrations to a new ip or node"),
+ N_("net witness client-move\n"
+ " Generate client move notifications for "
+ "witness registrations to a new ip or node"),
+ },
+ {
+ "share-move",
+ net_witness_share_move,
+ NET_TRANSPORT_LOCAL,
+ N_("Generate share move notifications for "
+ "witness registrations to a new ip or node"),
+ N_("net witness share-move\n"
+ " Generate share move notifications for "
+ "witness registrations to a new ip or node"),
+ },
+ {
+ "force-unregister",
+ net_witness_force_unregister,
+ NET_TRANSPORT_LOCAL,
+ N_("Force unregistrations for witness registrations"),
+ N_("net witness force-unregister\n"
+ " Force unregistrations for "
+ "witness registrations"),
+ },
+ {
+ "force-response",
+ net_witness_force_response,
+ NET_TRANSPORT_LOCAL,
+ N_("Force an AsyncNotify response based on "
+ "json input (mostly for testing)"),
+ N_("net witness force-response\n"
+ " Force an AsyncNotify response based on "
+ "json input (mostly for testing)"),
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net witness", func);
+}
diff --git a/source3/utils/netlookup.c b/source3/utils/netlookup.c
new file mode 100644
index 0000000..aaf78b0
--- /dev/null
+++ b/source3/utils/netlookup.c
@@ -0,0 +1,218 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Name lookup.
+
+ Copyright (C) Jeremy Allison 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/net.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "libsmb/libsmb.h"
+
+/********************************************************
+ Connection cachine struct. Goes away when ctx destroyed.
+********************************************************/
+
+struct con_struct {
+ bool failed_connect;
+ NTSTATUS err;
+ struct cli_state *cli;
+ struct rpc_pipe_client *lsapipe;
+ struct policy_handle pol;
+};
+
+static struct con_struct *cs;
+
+/********************************************************
+ Close connection on context destruction.
+********************************************************/
+
+static int cs_destructor(struct con_struct *p)
+{
+ if (cs->cli) {
+ cli_shutdown(cs->cli);
+ }
+ cs = NULL;
+ return 0;
+}
+
+/********************************************************
+ Create the connection to localhost.
+********************************************************/
+
+static struct con_struct *create_cs(struct net_context *c,
+ TALLOC_CTX *ctx, NTSTATUS *perr)
+{
+ NTSTATUS nt_status;
+ struct sockaddr_storage loopback_ss;
+ struct cli_credentials *anon_creds = NULL;
+
+ *perr = NT_STATUS_OK;
+
+ if (!interpret_string_addr(&loopback_ss, "127.0.0.1", AI_NUMERICHOST)) {
+ *perr = NT_STATUS_INVALID_PARAMETER;
+ return NULL;
+ }
+
+ if (cs) {
+ if (cs->failed_connect) {
+ *perr = cs->err;
+ return NULL;
+ }
+ return cs;
+ }
+
+ cs = talloc(ctx, struct con_struct);
+ if (!cs) {
+ *perr = NT_STATUS_NO_MEMORY;
+ return NULL;
+ }
+
+ anon_creds = cli_credentials_init_anon(cs);
+ if (anon_creds == NULL) {
+ TALLOC_FREE(cs);
+ *perr = NT_STATUS_NO_MEMORY;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(cs);
+ talloc_set_destructor(cs, cs_destructor);
+
+ nt_status = cli_full_connection_creds(&cs->cli, lp_netbios_name(), lp_netbios_name(),
+ &loopback_ss, 0,
+ "IPC$", "IPC",
+ anon_creds,
+ CLI_FULL_CONNECTION_IPC);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("create_cs: Connect failed. Error was %s\n", nt_errstr(nt_status)));
+ cs->failed_connect = true;
+ cs->err = nt_status;
+ *perr = nt_status;
+ return NULL;
+ }
+
+ nt_status = cli_rpc_pipe_open_noauth(cs->cli,
+ &ndr_table_lsarpc,
+ &cs->lsapipe);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("create_cs: open LSA pipe failed. Error was %s\n", nt_errstr(nt_status)));
+ cs->failed_connect = true;
+ cs->err = nt_status;
+ *perr = nt_status;
+ return NULL;
+ }
+
+ nt_status = rpccli_lsa_open_policy(cs->lsapipe, ctx, true,
+ SEC_FLAG_MAXIMUM_ALLOWED,
+ &cs->pol);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(2,("create_cs: rpccli_lsa_open_policy failed. Error was %s\n", nt_errstr(nt_status)));
+ cs->failed_connect = true;
+ cs->err = nt_status;
+ *perr = nt_status;
+ return NULL;
+ }
+
+ return cs;
+}
+
+/********************************************************
+ Do a lookup_sids call to localhost.
+ Check if the local machine is authoritative for this sid. We can't
+ check if this is our SID as that's stored in the root-read-only
+ secrets.tdb.
+ The local smbd will also ask winbindd for us, so we don't have to.
+********************************************************/
+
+NTSTATUS net_lookup_name_from_sid(struct net_context *c,
+ TALLOC_CTX *ctx,
+ struct dom_sid *psid,
+ const char **ppdomain,
+ const char **ppname)
+{
+ NTSTATUS nt_status;
+ struct con_struct *csp = NULL;
+ char **domains;
+ char **names;
+ enum lsa_SidType *types;
+
+ *ppdomain = NULL;
+ *ppname = NULL;
+
+ csp = create_cs(c, ctx, &nt_status);
+ if (csp == NULL) {
+ return nt_status;
+ }
+
+ nt_status = rpccli_lsa_lookup_sids(csp->lsapipe, ctx,
+ &csp->pol,
+ 1, psid,
+ &domains,
+ &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ *ppdomain = domains[0];
+ *ppname = names[0];
+ /* Don't care about type here. */
+
+ /* Converted OK */
+ return NT_STATUS_OK;
+}
+
+/********************************************************
+ Do a lookup_names call to localhost.
+********************************************************/
+
+NTSTATUS net_lookup_sid_from_name(struct net_context *c, TALLOC_CTX *ctx,
+ const char *full_name, struct dom_sid *pret_sid)
+{
+ NTSTATUS nt_status;
+ struct con_struct *csp = NULL;
+ struct dom_sid *sids = NULL;
+ enum lsa_SidType *types = NULL;
+
+ csp = create_cs(c, ctx, &nt_status);
+ if (csp == NULL) {
+ return nt_status;
+ }
+
+ nt_status = rpccli_lsa_lookup_names(csp->lsapipe, ctx,
+ &csp->pol,
+ 1,
+ &full_name,
+ NULL, 1,
+ &sids, &types);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ *pret_sid = sids[0];
+
+ /* Converted OK */
+ return NT_STATUS_OK;
+}
diff --git a/source3/utils/nmblookup.c b/source3/utils/nmblookup.c
new file mode 100644
index 0000000..9523f71
--- /dev/null
+++ b/source3/utils/nmblookup.c
@@ -0,0 +1,468 @@
+/*
+ Unix SMB/CIFS implementation.
+ NBT client - used to lookup netbios names
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Jelmer Vernooij 2003 (Conversion to popt)
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "libsmb/nmblib.h"
+#include "libsmb/namequery.h"
+#include "lib/util/string_wrappers.h"
+
+static bool give_flags = false;
+static bool use_bcast = true;
+static bool got_bcast = false;
+static struct sockaddr_storage bcast_addr;
+static bool recursion_desired = false;
+static bool translate_addresses = false;
+static int ServerFD= -1;
+static bool RootPort = false;
+static bool find_status = false;
+
+/****************************************************************************
+ Open the socket communication.
+**************************************************************************/
+
+static bool open_sockets(void)
+{
+ struct sockaddr_storage ss;
+ const char *sock_addr = lp_nbt_client_socket_address();
+
+ if (!interpret_string_addr(&ss, sock_addr,
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ DEBUG(0,("open_sockets: unable to get socket address "
+ "from string %s\n", sock_addr));
+ return false;
+ }
+ ServerFD = open_socket_in(
+ SOCK_DGRAM, &ss, (RootPort ? 137 : 0), true);
+ if (ServerFD < 0) {
+ if (RootPort) {
+ DBG_ERR("open_socket_in failed: %s\n",
+ strerror(-ServerFD));
+ } else {
+ DBG_NOTICE("open_socket_in failed: %s\n",
+ strerror(-ServerFD));
+ }
+ return false;
+ }
+
+ set_socket_options( ServerFD, "SO_BROADCAST" );
+
+ DEBUG(3, ("Socket opened.\n"));
+ return true;
+}
+
+/****************************************************************************
+turn a node status flags field into a string
+****************************************************************************/
+static char *node_status_flags(unsigned char flags)
+{
+ static fstring ret;
+ fstrcpy(ret,"");
+
+ fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " ");
+ if ((flags & 0x60) == 0x00) fstrcat(ret,"B ");
+ if ((flags & 0x60) == 0x20) fstrcat(ret,"P ");
+ if ((flags & 0x60) == 0x40) fstrcat(ret,"M ");
+ if ((flags & 0x60) == 0x60) fstrcat(ret,"H ");
+ if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> ");
+ if (flags & 0x08) fstrcat(ret,"<CONFLICT> ");
+ if (flags & 0x04) fstrcat(ret,"<ACTIVE> ");
+ if (flags & 0x02) fstrcat(ret,"<PERMANENT> ");
+
+ return ret;
+}
+
+/****************************************************************************
+ Turn the NMB Query flags into a string.
+****************************************************************************/
+
+static char *query_flags(int flags)
+{
+ static fstring ret1;
+ fstrcpy(ret1, "");
+
+ if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response ");
+ if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative ");
+ if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated ");
+ if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired ");
+ if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available ");
+ if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast ");
+
+ return ret1;
+}
+
+/****************************************************************************
+ Do a node status query.
+****************************************************************************/
+
+static bool do_node_status(const char *name,
+ int type,
+ struct sockaddr_storage *pss)
+{
+ struct nmb_name nname;
+ size_t count = 0;
+ size_t i, j;
+ struct node_status *addrs;
+ struct node_status_extra extra;
+ fstring cleanname;
+ char addr[INET6_ADDRSTRLEN];
+ NTSTATUS status;
+
+ print_sockaddr(addr, sizeof(addr), pss);
+ d_printf("Looking up status of %s\n",addr);
+ make_nmb_name(&nname, name, type);
+ status = node_status_query(talloc_tos(), &nname, pss,
+ &addrs, &count, &extra);
+ if (NT_STATUS_IS_OK(status)) {
+ for (i=0;i<count;i++) {
+ pull_ascii_fstring(cleanname, addrs[i].name);
+ for (j=0;cleanname[j];j++) {
+ if (!isprint((int)cleanname[j])) {
+ cleanname[j] = '.';
+ }
+ }
+ d_printf("\t%-15s <%02x> - %s\n",
+ cleanname,addrs[i].type,
+ node_status_flags(addrs[i].flags));
+ }
+ d_printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n",
+ extra.mac_addr[0], extra.mac_addr[1],
+ extra.mac_addr[2], extra.mac_addr[3],
+ extra.mac_addr[4], extra.mac_addr[5]);
+ d_printf("\n");
+ TALLOC_FREE(addrs);
+ return true;
+ } else {
+ d_printf("No reply from %s\n\n",addr);
+ return false;
+ }
+}
+
+
+/****************************************************************************
+ Send out one query.
+****************************************************************************/
+
+static bool query_one(const char *lookup, unsigned int lookup_type)
+{
+ size_t j, count = 0;
+ uint8_t flags = 0;
+ struct sockaddr_storage *ip_list=NULL;
+ NTSTATUS status = NT_STATUS_NOT_FOUND;
+
+ if (got_bcast) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &bcast_addr);
+ d_printf("querying %s on %s\n", lookup, addr);
+ status = name_query(lookup,lookup_type,use_bcast,
+ use_bcast?true:recursion_desired,
+ &bcast_addr, talloc_tos(),
+ &ip_list, &count, &flags);
+ } else {
+ status = name_resolve_bcast(talloc_tos(),
+ lookup,
+ lookup_type,
+ &ip_list,
+ &count);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return false;
+ }
+
+ if (give_flags) {
+ d_printf("Flags: %s\n", query_flags(flags));
+ }
+
+ for (j=0;j<count;j++) {
+ char addr[INET6_ADDRSTRLEN];
+ if (translate_addresses) {
+ char h_name[MAX_DNS_NAME_LENGTH];
+ h_name[0] = '\0';
+ if (sys_getnameinfo((const struct sockaddr *)&ip_list[j],
+ sizeof(struct sockaddr_storage),
+ h_name, sizeof(h_name),
+ NULL, 0,
+ NI_NAMEREQD)) {
+ continue;
+ }
+ d_printf("%s, ", h_name);
+ }
+ print_sockaddr(addr, sizeof(addr), &ip_list[j]);
+ d_printf("%s %s<%02x>\n", addr,lookup, lookup_type);
+ /* We can only do find_status if the ip address returned
+ was valid - ie. name_query returned true.
+ */
+ if (find_status) {
+ if (!do_node_status(lookup, lookup_type, &ip_list[j])) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+ }
+ }
+
+ TALLOC_FREE(ip_list);
+
+ return NT_STATUS_IS_OK(status);
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+enum nmblookup_cmdline_options {
+ CMDLINE_RECURSIVE = 1,
+};
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ unsigned int lookup_type = 0x0;
+ fstring lookup;
+ static bool find_master=False;
+ static bool lookup_by_ip = False;
+ poptContext pc = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ int rc = 0;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "broadcast",
+ .shortName = 'B',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'B',
+ .descrip = "Specify address to use for broadcasts",
+ .argDescrip = "BROADCAST-ADDRESS",
+ },
+ {
+ .longName = "flags",
+ .shortName = 'f',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'f',
+ .descrip = "List the NMB flags returned",
+ },
+ {
+ .longName = "unicast",
+ .shortName = 'U',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'U',
+ .descrip = "Specify address to use for unicast",
+ },
+ {
+ .longName = "master-browser",
+ .shortName = 'M',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'M',
+ .descrip = "Search for a master browser",
+ },
+ {
+ .longName = "recursion",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = CMDLINE_RECURSIVE,
+ .descrip = "Set recursion desired in package",
+ },
+ {
+ .longName = "status",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'S',
+ .descrip = "Lookup node status as well",
+ },
+ {
+ .longName = "translate",
+ .shortName = 'T',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'T',
+ .descrip = "Translate IP addresses into names",
+ },
+ {
+ .longName = "root-port",
+ .shortName = 'r',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'r',
+ .descrip = "Use root port 137 (Win95 only replies to this)",
+ },
+ {
+ .longName = "lookup-by-ip",
+ .shortName = 'A',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'A',
+ .descrip = "Do a node status on <name> as an IP Address",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ *lookup = 0;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "<NODE> ...");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'f':
+ give_flags = true;
+ break;
+ case 'M':
+ find_master = true;
+ break;
+ case CMDLINE_RECURSIVE:
+ recursion_desired = true;
+ break;
+ case 'S':
+ find_status = true;
+ break;
+ case 'r':
+ RootPort = true;
+ break;
+ case 'A':
+ lookup_by_ip = true;
+ break;
+ case 'B':
+ if (interpret_string_addr(&bcast_addr,
+ poptGetOptArg(pc),
+ NI_NUMERICHOST)) {
+ got_bcast = True;
+ use_bcast = True;
+ }
+ break;
+ case 'U':
+ if (interpret_string_addr(&bcast_addr,
+ poptGetOptArg(pc),
+ 0)) {
+ got_bcast = True;
+ use_bcast = False;
+ }
+ break;
+ case 'T':
+ translate_addresses = !translate_addresses;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptGetArg(pc); /* Remove argv[0] */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ rc = 1;
+ goto out;
+ }
+
+ if (!open_sockets()) {
+ rc = 1;
+ goto out;
+ }
+
+ while(poptPeekArg(pc)) {
+ char *p;
+ struct in_addr ip;
+ size_t nbt_len;
+
+ fstrcpy(lookup,poptGetArg(pc));
+
+ if(lookup_by_ip) {
+ struct sockaddr_storage ss;
+ ip = interpret_addr2(lookup);
+ in_addr_to_sockaddr_storage(&ss, ip);
+ fstrcpy(lookup,"*");
+ if (!do_node_status(lookup, lookup_type, &ss)) {
+ rc = 1;
+ }
+ continue;
+ }
+
+ if (find_master) {
+ if (*lookup == '-') {
+ fstrcpy(lookup,"\01\02__MSBROWSE__\02");
+ lookup_type = 1;
+ } else {
+ lookup_type = 0x1d;
+ }
+ }
+
+ p = strchr_m(lookup,'#');
+ if (p) {
+ *p = '\0';
+ sscanf(++p,"%x",&lookup_type);
+ }
+
+ nbt_len = strlen(lookup);
+ if (nbt_len > MAX_NETBIOSNAME_LEN - 1) {
+ d_printf("The specified netbios name [%s] is too long!\n",
+ lookup);
+ continue;
+ }
+
+
+ if (!query_one(lookup, lookup_type)) {
+ rc = 1;
+ d_printf( "name_query failed to find name %s", lookup );
+ if( 0 != lookup_type ) {
+ d_printf( "#%02x", lookup_type );
+ }
+ d_printf( "\n" );
+ }
+ }
+
+out:
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return rc;
+}
diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c
new file mode 100644
index 0000000..6660a31
--- /dev/null
+++ b/source3/utils/ntlm_auth.c
@@ -0,0 +1,2856 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
+ Copyright (C) Robert O'Callahan 2006 (added cached credential code).
+ Copyright (C) Kai Blin <kai@samba.org> 2008
+ Copyright (C) Simo Sorce 2010
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/param/param.h"
+#include "lib/cmdline/cmdline.h"
+#include "libcli/security/security.h"
+#include "utils/ntlm_auth.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "auth/ntlmssp/ntlmssp.h"
+#include "auth/gensec/gensec.h"
+#include "auth/gensec/gensec_internal.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/crypto/gse.h"
+#include "smb_krb5.h"
+#include "lib/util/tiniparser.h"
+#include "librpc/gen_ndr/krb5pac.h"
+#include "auth/common_auth.h"
+#include "source3/include/auth.h"
+#include "source3/auth/proto.h"
+#include "nsswitch/libwbclient/wbclient.h"
+#include "nsswitch/winbind_struct_protocol.h"
+#include "nsswitch/libwbclient/wbclient_internal.h"
+#include "lib/param/loadparm.h"
+#include "lib/util/base64.h"
+#include "cmdline_contexts.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/util/string_wrappers.h"
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+
+#ifdef HAVE_KRB5
+#include "auth/kerberos/pac_utils.h"
+#endif
+
+#ifndef PAM_WINBIND_CONFIG_FILE
+#define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf"
+#endif
+
+#define WINBIND_KRB5_AUTH 0x00000080
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+#define INITIAL_BUFFER_SIZE 300
+#define MAX_BUFFER_SIZE 630000
+
+enum stdio_helper_mode {
+ SQUID_2_4_BASIC,
+ SQUID_2_5_BASIC,
+ SQUID_2_5_NTLMSSP,
+ NTLMSSP_CLIENT_1,
+ GSS_SPNEGO_SERVER,
+ GSS_SPNEGO_CLIENT,
+ NTLM_SERVER_1,
+ NTLM_CHANGE_PASSWORD_1,
+ NUM_HELPER_MODES
+};
+
+enum ntlm_auth_cli_state {
+ CLIENT_INITIAL = 0,
+ CLIENT_RESPONSE,
+ CLIENT_FINISHED,
+ CLIENT_ERROR
+};
+
+struct ntlm_auth_state {
+ TALLOC_CTX *mem_ctx;
+ enum stdio_helper_mode helper_mode;
+ enum ntlm_auth_cli_state cli_state;
+ struct ntlmssp_state *ntlmssp_state;
+ uint32_t neg_flags;
+ char *want_feature_list;
+ bool have_session_key;
+ DATA_BLOB session_key;
+ DATA_BLOB initial_message;
+ void *gensec_private_1;
+};
+typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state, char *buf,
+ int length, void **private2);
+
+static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1);
+
+static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ stdio_helper_function fn, void **private2);
+
+static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2);
+
+static const struct {
+ enum stdio_helper_mode mode;
+ const char *name;
+ stdio_helper_function fn;
+} stdio_helper_protocols[] = {
+ { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request},
+ { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request},
+ { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request},
+ { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request},
+ { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request},
+ { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request},
+ { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request},
+ { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request},
+ { NUM_HELPER_MODES, NULL, NULL}
+};
+
+const char *opt_username;
+const char *opt_domain;
+const char *opt_workstation;
+const char *opt_password;
+static DATA_BLOB opt_challenge;
+static DATA_BLOB opt_lm_response;
+static DATA_BLOB opt_nt_response;
+static int request_lm_key;
+static int request_user_session_key;
+static int use_cached_creds;
+static int offline_logon;
+static int opt_allow_mschapv2;
+
+static const char *require_membership_of;
+static const char *require_membership_of_sid;
+static const char *opt_pam_winbind_conf;
+
+const char *opt_target_service;
+const char *opt_target_hostname;
+
+
+/* This is a bit hairy, but the basic idea is to do a password callback
+ to the calling application. The callback comes from within gensec */
+
+static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state, char *buf, int length,
+ void **password)
+{
+ DATA_BLOB in;
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("query [%s] invalid\n", buf));
+ printf("BH Query invalid\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ in = base64_decode_data_blob(buf + 3);
+ } else {
+ in = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ *password = talloc_strndup(NULL,
+ (const char *)in.data, in.length);
+
+ if (*password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ printf("BH Out of memory\n");
+ data_blob_free(&in);
+ return;
+ }
+
+ printf("OK\n");
+ data_blob_free(&in);
+ return;
+ }
+ DEBUG(1, ("Asked for (and expected) a password\n"));
+ printf("BH Expected a password\n");
+ data_blob_free(&in);
+}
+
+/**
+ * Callback for password credentials. This is not async, and when
+ * GENSEC and the credentials code is made async, it will look rather
+ * different.
+ */
+
+static const char *get_password(struct cli_credentials *credentials)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ char *password = NULL;
+ struct ntlm_auth_state *state;
+
+ state = talloc_zero(frame, struct ntlm_auth_state);
+ if (state == NULL) {
+ DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
+ fprintf(stderr, "ERR\n");
+ exit(1);
+ }
+
+ state->mem_ctx = state;
+
+ /* Ask for a password */
+ printf("PW\n");
+
+ manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password);
+ talloc_steal(credentials, password);
+ TALLOC_FREE(frame);
+ return password;
+}
+
+/**
+ * A limited set of features are defined with text strings as needed
+ * by ntlm_auth
+ *
+ */
+static void gensec_want_feature_list(struct gensec_security *state, char* feature_list)
+{
+ if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY);
+ }
+ if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SIGN\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SIGN);
+ }
+ if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_SEAL\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_SEAL);
+ }
+ if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) {
+ DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n"));
+ gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE);
+ }
+}
+
+static char winbind_separator(void)
+{
+ struct wbcInterfaceDetails *details;
+ wbcErr ret;
+ static bool got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ret = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(ret)) {
+ d_fprintf(stderr, "could not obtain winbind separator!\n");
+ return *lp_winbind_separator();
+ }
+
+ sep = details->winbind_separator;
+
+ wbcFreeMemory(details);
+
+ got_sep = True;
+
+ if (!sep) {
+ d_fprintf(stderr, "winbind separator was NULL!\n");
+ return *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+const char *get_winbind_domain(void)
+{
+ struct wbcInterfaceDetails *details;
+ wbcErr ret;
+
+ static fstring winbind_domain;
+ if (*winbind_domain) {
+ return winbind_domain;
+ }
+
+ /* Send off request */
+
+ ret = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(ret)) {
+ DEBUG(1, ("could not obtain winbind domain name!\n"));
+ return lp_workgroup();
+ }
+
+ fstrcpy(winbind_domain, details->netbios_domain);
+
+ wbcFreeMemory(details);
+
+ return winbind_domain;
+
+}
+
+const char *get_winbind_netbios_name(void)
+{
+ struct wbcInterfaceDetails *details;
+ wbcErr ret;
+
+ static fstring winbind_netbios_name;
+
+ if (*winbind_netbios_name) {
+ return winbind_netbios_name;
+ }
+
+ /* Send off request */
+
+ ret = wbcInterfaceDetails(&details);
+ if (!WBC_ERROR_IS_OK(ret)) {
+ DEBUG(1, ("could not obtain winbind netbios name!\n"));
+ return lp_netbios_name();
+ }
+
+ fstrcpy(winbind_netbios_name, details->netbios_name);
+
+ wbcFreeMemory(details);
+
+ return winbind_netbios_name;
+
+}
+
+DATA_BLOB get_challenge(void)
+{
+ static DATA_BLOB chal;
+ if (opt_challenge.length)
+ return opt_challenge;
+
+ chal = data_blob(NULL, 8);
+
+ generate_random_buffer(chal.data, chal.length);
+ return chal;
+}
+
+/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the
+ form DOMAIN/user into a domain and a user */
+
+static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain,
+ fstring user)
+{
+
+ char *p = strchr(domuser,winbind_separator());
+
+ if (!p) {
+ return False;
+ }
+
+ fstrcpy(user, p+1);
+ fstrcpy(domain, domuser);
+ domain[PTR_DIFF(p, domuser)] = 0;
+ return strupper_m(domain);
+}
+
+static bool get_require_membership_sid(void) {
+ fstring domain, name, sidbuf;
+ struct wbcDomainSid sid;
+ enum wbcSidType type;
+ wbcErr ret;
+
+ if (!require_membership_of) {
+ return True;
+ }
+
+ if (require_membership_of_sid) {
+ return True;
+ }
+
+ /* Otherwise, ask winbindd for the name->sid request */
+
+ if (!parse_ntlm_auth_domain_user(require_membership_of,
+ domain, name)) {
+ DEBUG(0, ("Could not parse %s into separate domain/name parts!\n",
+ require_membership_of));
+ return False;
+ }
+
+ ret = wbcLookupName(domain, name, &sid, &type);
+ if (!WBC_ERROR_IS_OK(ret)) {
+ DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
+ require_membership_of));
+ return False;
+ }
+
+ wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf));
+
+ require_membership_of_sid = SMB_STRDUP(sidbuf);
+
+ if (require_membership_of_sid)
+ return True;
+
+ return False;
+}
+
+/*
+ * Get some configuration from pam_winbind.conf to see if we
+ * need to contact trusted domain
+ */
+
+int get_pam_winbind_config(void)
+{
+ int ctrl = 0;
+ struct tiniparser_dictionary *d = NULL;
+
+ if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) {
+ opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE;
+ }
+
+ d = tiniparser_load(opt_pam_winbind_conf);
+
+ if (!d) {
+ return 0;
+ }
+
+ if (tiniparser_getboolean(d, "global:krb5_auth", false)) {
+ ctrl |= WINBIND_KRB5_AUTH;
+ }
+
+ tiniparser_freedict(d);
+
+ return ctrl;
+}
+
+/* Authenticate a user with a plaintext password */
+
+static bool check_plaintext_auth(const char *user, const char *pass,
+ bool stdout_diagnostics)
+{
+ struct winbindd_request request;
+ struct winbindd_response response;
+ wbcErr ret;
+
+ if (!get_require_membership_sid()) {
+ return False;
+ }
+
+ /* Send off request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ fstrcpy(request.data.auth.user, user);
+ fstrcpy(request.data.auth.pass, pass);
+ if (require_membership_of_sid) {
+ strlcpy(request.data.auth.require_membership_of_sid,
+ require_membership_of_sid,
+ sizeof(request.data.auth.require_membership_of_sid));
+ }
+
+ if (offline_logon) {
+ request.flags |= WBFLAG_PAM_CACHED_LOGIN;
+ }
+
+ ret = wbcRequestResponse(NULL, WINBINDD_PAM_AUTH,
+ &request, &response);
+
+ /* Display response */
+
+ if (stdout_diagnostics) {
+ if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
+ d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n");
+ }
+
+ d_printf("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status);
+ } else {
+ if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
+ DEBUG(1, ("Reading winbind reply failed! (0x01)\n"));
+ }
+
+ DEBUG(3, ("%s: %s (0x%x)\n",
+ response.data.auth.nt_status_string,
+ response.data.auth.error_string,
+ response.data.auth.nt_status));
+ }
+
+ return WBC_ERROR_IS_OK(ret);
+}
+
+/* authenticate a user with an encrypted username/password */
+
+NTSTATUS contact_winbind_auth_crap(const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32_t flags,
+ uint32_t extra_logon_parameters,
+ uint8_t lm_key[8],
+ uint8_t user_session_key[16],
+ uint8_t *pauthoritative,
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ wbcErr ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ *pauthoritative = 1;
+
+ if (!get_require_membership_sid()) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ request.flags = flags;
+
+ request.data.auth_crap.logon_parameters = extra_logon_parameters
+ | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
+ if (opt_allow_mschapv2) {
+ request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2;
+ }
+
+ if (require_membership_of_sid)
+ fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid);
+
+ fstrcpy(request.data.auth_crap.user, username);
+ fstrcpy(request.data.auth_crap.domain, domain);
+
+ fstrcpy(request.data.auth_crap.workstation,
+ workstation);
+
+ memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8));
+
+ if (lm_response && lm_response->length) {
+ size_t capped_lm_response_len = MIN(
+ lm_response->length,
+ sizeof(request.data.auth_crap.lm_resp));
+
+ memcpy(request.data.auth_crap.lm_resp,
+ lm_response->data,
+ capped_lm_response_len);
+ request.data.auth_crap.lm_resp_len = capped_lm_response_len;
+ }
+
+ if (nt_response && nt_response->length) {
+ if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) {
+ request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB;
+ request.extra_len = nt_response->length;
+ request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len);
+ if (request.extra_data.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(request.extra_data.data, nt_response->data,
+ nt_response->length);
+
+ } else {
+ memcpy(request.data.auth_crap.nt_resp,
+ nt_response->data, nt_response->length);
+ }
+ request.data.auth_crap.nt_resp_len = nt_response->length;
+ }
+
+ ret = wbcRequestResponsePriv(
+ NULL,
+ WINBINDD_PAM_AUTH_CRAP,
+ &request,
+ &response);
+ SAFE_FREE(request.extra_data.data);
+
+ /* Display response */
+
+ if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ if (error_string)
+ *error_string = smb_xstrdup("Reading winbind reply failed!");
+ winbindd_free_response(&response);
+ return nt_status;
+ }
+
+ nt_status = (NT_STATUS(response.data.auth.nt_status));
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ if (error_string)
+ *error_string = smb_xstrdup(response.data.auth.error_string);
+ *pauthoritative = response.data.auth.authoritative;
+ winbindd_free_response(&response);
+ return nt_status;
+ }
+
+ if ((flags & WBFLAG_PAM_LMKEY) && lm_key) {
+ memcpy(lm_key, response.data.auth.first_8_lm_hash,
+ sizeof(response.data.auth.first_8_lm_hash));
+ }
+ if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) {
+ memcpy(user_session_key, response.data.auth.user_session_key,
+ sizeof(response.data.auth.user_session_key));
+ }
+
+ if (flags & WBFLAG_PAM_UNIX_NAME) {
+ *unix_name = SMB_STRDUP(response.data.auth.unix_username);
+ if (!*unix_name) {
+ winbindd_free_response(&response);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ winbindd_free_response(&response);
+ return nt_status;
+}
+
+/* contact server to change user password using auth crap */
+static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username,
+ const char *domain,
+ const DATA_BLOB new_nt_pswd,
+ const DATA_BLOB old_nt_hash_enc,
+ const DATA_BLOB new_lm_pswd,
+ const DATA_BLOB old_lm_hash_enc,
+ char **error_string)
+{
+ NTSTATUS nt_status;
+ wbcErr ret;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!get_require_membership_sid())
+ {
+ if(error_string)
+ *error_string = smb_xstrdup("Can't get membership sid.");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if(username != NULL)
+ fstrcpy(request.data.chng_pswd_auth_crap.user, username);
+ if(domain != NULL)
+ fstrcpy(request.data.chng_pswd_auth_crap.domain,domain);
+
+ if(new_nt_pswd.length)
+ {
+ memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd));
+ request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length;
+ }
+
+ if(old_nt_hash_enc.length)
+ {
+ memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc));
+ request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length;
+ }
+
+ if(new_lm_pswd.length)
+ {
+ memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd));
+ request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length;
+ }
+
+ if(old_lm_hash_enc.length)
+ {
+ memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc));
+ request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length;
+ }
+
+ ret = wbcRequestResponse(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,
+ &request, &response);
+
+ /* Display response */
+
+ if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0))
+ {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ if (error_string)
+ *error_string = smb_xstrdup("Reading winbind reply failed!");
+ winbindd_free_response(&response);
+ return nt_status;
+ }
+
+ nt_status = (NT_STATUS(response.data.auth.nt_status));
+ if (!NT_STATUS_IS_OK(nt_status))
+ {
+ if (error_string)
+ *error_string = smb_xstrdup(response.data.auth.error_string);
+ winbindd_free_response(&response);
+ return nt_status;
+ }
+
+ winbindd_free_response(&response);
+
+ return nt_status;
+}
+
+/*
+ * This function does not create a full auth_session_info, just enough
+ * for the caller to get the "unix" username
+ */
+static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context,
+ TALLOC_CTX *mem_ctx,
+ void *server_returned_info,
+ const char *original_user_name,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info_out)
+{
+ const char *unix_username = (const char *)server_returned_info;
+ struct dom_sid *sids = NULL;
+ struct auth_session_info *session_info = NULL;
+
+ session_info = talloc_zero(mem_ctx, struct auth_session_info);
+ if (session_info == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
+ if (session_info->unix_info == NULL) {
+ TALLOC_FREE(session_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info,
+ unix_username);
+ if (session_info->unix_info->unix_name == NULL) {
+ TALLOC_FREE(session_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This is not a full session_info - it is not created
+ * correctly and misses any claims etc, because all we
+ * actually use in the caller is the unix username.
+ *
+ * Therefore so no claims need to be added and
+ * se_access_check() will never run.
+ */
+ session_info->security_token
+ = security_token_initialise(talloc_tos(),
+ CLAIMS_EVALUATION_INVALID_STATE);
+ if (session_info->security_token == NULL) {
+ TALLOC_FREE(session_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ sids = talloc_zero_array(session_info->security_token,
+ struct dom_sid, 3);
+ if (sids == NULL) {
+ TALLOC_FREE(session_info);
+ return NT_STATUS_NO_MEMORY;
+ }
+ sid_copy(&sids[0], &global_sid_World);
+ sid_copy(&sids[1], &global_sid_Network);
+ sid_copy(&sids[2], &global_sid_Authenticated_Users);
+
+ session_info->security_token->num_sids = talloc_array_length(sids);
+ session_info->security_token->sids = sids;
+
+ *session_info_out = session_info;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx,
+ TALLOC_CTX *mem_ctx,
+ struct smb_krb5_context *smb_krb5_context,
+ DATA_BLOB *pac_blob,
+ const char *princ_name,
+ const struct tsocket_address *remote_address,
+ uint32_t session_info_flags,
+ struct auth_session_info **session_info)
+{
+ TALLOC_CTX *tmp_ctx;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ char *unixuser;
+ NTSTATUS status;
+ const char *domain = "";
+ const char *user = "";
+
+ tmp_ctx = talloc_new(mem_ctx);
+ if (!tmp_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (pac_blob) {
+#ifdef HAVE_KRB5
+ status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL,
+ NULL, NULL, 0, &logon_info);
+#else
+ status = NT_STATUS_ACCESS_DENIED;
+#endif
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+ } else {
+ status = NT_STATUS_ACCESS_DENIED;
+ DBG_WARNING("Kerberos ticket for[%s] has no PAC: %s\n",
+ princ_name, nt_errstr(status));
+ goto done;
+ }
+
+ if (logon_info->info3.base.account_name.string != NULL) {
+ user = logon_info->info3.base.account_name.string;
+ } else {
+ user = "";
+ }
+ if (logon_info->info3.base.logon_domain.string != NULL) {
+ domain = logon_info->info3.base.logon_domain.string;
+ } else {
+ domain = "";
+ }
+
+ if (strlen(user) == 0 || strlen(domain) == 0) {
+ status = NT_STATUS_ACCESS_DENIED;
+ DBG_WARNING("Kerberos ticket for[%s] has invalid "
+ "account_name[%s]/logon_domain[%s]: %s\n",
+ princ_name,
+ logon_info->info3.base.account_name.string,
+ logon_info->info3.base.logon_domain.string,
+ nt_errstr(status));
+ goto done;
+ }
+
+ DBG_NOTICE("Kerberos ticket principal name is [%s] "
+ "account_name[%s]/logon_domain[%s]\n",
+ princ_name, user, domain);
+
+ if (!strequal(domain, lp_workgroup())) {
+ if (!lp_allow_trusted_domains()) {
+ status = NT_STATUS_LOGON_FAILURE;
+ goto done;
+ }
+ }
+
+ unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user);
+ if (!unixuser) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info);
+
+done:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+
+
+/**
+ * Return the challenge as determined by the authentication subsystem
+ * @return an 8 byte random challenge
+ */
+
+static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx,
+ uint8_t chal[8])
+{
+ if (auth_ctx->challenge.data.length == 8) {
+ DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n",
+ auth_ctx->challenge.set_by));
+ memcpy(chal, auth_ctx->challenge.data.data, 8);
+ return NT_STATUS_OK;
+ }
+
+ if (!auth_ctx->challenge.set_by) {
+ generate_random_buffer(chal, 8);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+ auth_ctx->challenge.set_by = "random";
+ }
+
+ DEBUG(10,("auth_get_challenge: challenge set by %s\n",
+ auth_ctx->challenge.set_by));
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * NTLM2 authentication modifies the effective challenge,
+ * @param challenge The new challenge value
+ */
+static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by)
+{
+ auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by);
+
+ auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8);
+ NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * Check the password on an NTLMSSP login.
+ *
+ * Return the session keys used on the connection.
+ */
+
+struct winbind_pw_check_state {
+ uint8_t authoritative;
+ void *server_info;
+ DATA_BLOB nt_session_key;
+ DATA_BLOB lm_session_key;
+};
+
+static struct tevent_req *winbind_pw_check_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth4_context *auth4_context,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct winbind_pw_check_state *state = NULL;
+ NTSTATUS nt_status;
+ char *error_string = NULL;
+ uint8_t lm_key[8];
+ uint8_t user_sess_key[16];
+ char *unix_name = NULL;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct winbind_pw_check_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ nt_status = contact_winbind_auth_crap(
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ user_info->workstation_name,
+ &auth4_context->challenge.data,
+ &user_info->password.response.lanman,
+ &user_info->password.response.nt,
+ WBFLAG_PAM_LMKEY |
+ WBFLAG_PAM_USER_SESSION_KEY |
+ WBFLAG_PAM_UNIX_NAME,
+ 0,
+ lm_key, user_sess_key,
+ &state->authoritative,
+ &error_string,
+ &unix_name);
+
+ if (tevent_req_nterror(req, nt_status)) {
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ DBG_ERR("Login for user [%s]\\[%s]@[%s] failed due "
+ "to [%s]\n",
+ user_info->client.domain_name,
+ user_info->client.account_name,
+ user_info->workstation_name,
+ error_string ?
+ error_string :
+ "unknown error (NULL)");
+ } else {
+ DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due "
+ "to [%s]\n",
+ user_info->client.domain_name,
+ user_info->client.account_name,
+ user_info->workstation_name,
+ error_string ?
+ error_string :
+ "unknown error (NULL)");
+ }
+ goto done;
+ }
+
+ if (!all_zero(lm_key, 8)) {
+ state->lm_session_key = data_blob_talloc(state, NULL, 16);
+ if (tevent_req_nomem(state->lm_session_key.data, req)) {
+ goto done;
+ }
+ memcpy(state->lm_session_key.data, lm_key, 8);
+ memset(state->lm_session_key.data+8, '\0', 8);
+ }
+ if (!all_zero(user_sess_key, 16)) {
+ state->nt_session_key = data_blob_talloc(
+ state, user_sess_key, 16);
+ if (tevent_req_nomem(state->nt_session_key.data, req)) {
+ goto done;
+ }
+ }
+ state->server_info = talloc_strdup(state, unix_name);
+ if (tevent_req_nomem(state->server_info, req)) {
+ goto done;
+ }
+ tevent_req_done(req);
+
+done:
+ SAFE_FREE(error_string);
+ SAFE_FREE(unix_name);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS winbind_pw_check_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *pauthoritative,
+ void **server_returned_info,
+ DATA_BLOB *nt_session_key,
+ DATA_BLOB *lm_session_key)
+{
+ struct winbind_pw_check_state *state = tevent_req_data(
+ req, struct winbind_pw_check_state);
+ NTSTATUS status;
+
+ if (pauthoritative != NULL) {
+ *pauthoritative = state->authoritative;
+ }
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (server_returned_info != NULL) {
+ *server_returned_info = talloc_move(
+ mem_ctx, &state->server_info);
+ }
+ if (nt_session_key != NULL) {
+ *nt_session_key = (DATA_BLOB) {
+ .data = talloc_move(
+ mem_ctx, &state->nt_session_key.data),
+ .length = state->nt_session_key.length,
+ };
+ }
+ if (lm_session_key != NULL) {
+ *lm_session_key = (DATA_BLOB) {
+ .data = talloc_move(
+ mem_ctx, &state->lm_session_key.data),
+ .length = state->lm_session_key.length,
+ };
+ }
+
+ return NT_STATUS_OK;
+}
+
+struct local_pw_check_state {
+ uint8_t authoritative;
+ void *server_info;
+ DATA_BLOB nt_session_key;
+ DATA_BLOB lm_session_key;
+};
+
+static struct tevent_req *local_pw_check_send(
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct auth4_context *auth4_context,
+ const struct auth_usersupplied_info *user_info)
+{
+ struct tevent_req *req = NULL;
+ struct local_pw_check_state *state = NULL;
+ struct samr_Password lm_pw, nt_pw;
+ NTSTATUS nt_status;
+
+ req = tevent_req_create(
+ mem_ctx, &state, struct local_pw_check_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->authoritative = 1;
+
+ nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
+
+ nt_status = ntlm_password_check(
+ state,
+ true,
+ NTLM_AUTH_ON,
+ 0,
+ &auth4_context->challenge.data,
+ &user_info->password.response.lanman,
+ &user_info->password.response.nt,
+ user_info->client.account_name,
+ user_info->client.account_name,
+ user_info->client.domain_name,
+ &lm_pw,
+ &nt_pw,
+ &state->nt_session_key,
+ &state->lm_session_key);
+
+ if (tevent_req_nterror(req, nt_status)) {
+ DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due to "
+ "[%s]\n",
+ user_info->client.domain_name,
+ user_info->client.account_name,
+ user_info->workstation_name,
+ nt_errstr(nt_status));
+ return tevent_req_post(req, ev);
+ }
+
+ state->server_info = talloc_asprintf(
+ state,
+ "%s%c%s",
+ user_info->client.domain_name,
+ *lp_winbind_separator(),
+ user_info->client.account_name);
+ if (tevent_req_nomem(state->server_info, req)) {
+ return tevent_req_post(req, ev);
+ }
+
+ tevent_req_done(req);
+ return tevent_req_post(req, ev);
+}
+
+static NTSTATUS local_pw_check_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ uint8_t *pauthoritative,
+ void **server_returned_info,
+ DATA_BLOB *nt_session_key,
+ DATA_BLOB *lm_session_key)
+{
+ struct local_pw_check_state *state = tevent_req_data(
+ req, struct local_pw_check_state);
+ NTSTATUS status;
+
+ if (pauthoritative != NULL) {
+ *pauthoritative = state->authoritative;
+ }
+
+ if (tevent_req_is_nterror(req, &status)) {
+ return status;
+ }
+
+ if (server_returned_info != NULL) {
+ *server_returned_info = talloc_move(
+ mem_ctx, &state->server_info);
+ }
+ if (nt_session_key != NULL) {
+ *nt_session_key = (DATA_BLOB) {
+ .data = talloc_move(
+ mem_ctx, &state->nt_session_key.data),
+ .length = state->nt_session_key.length,
+ };
+ }
+ if (lm_session_key != NULL) {
+ *lm_session_key = (DATA_BLOB) {
+ .data = talloc_move(
+ mem_ctx, &state->lm_session_key.data),
+ .length = state->lm_session_key.length,
+ };
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gensec_security **gensec_security_out)
+{
+ struct gensec_security *gensec_security = NULL;
+ NTSTATUS nt_status;
+ TALLOC_CTX *tmp_ctx;
+ const struct gensec_security_ops **backends = NULL;
+ struct gensec_settings *gensec_settings = NULL;
+ size_t idx = 0;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
+ if (gensec_settings == NULL) {
+ DEBUG(10, ("lpcfg_gensec_settings failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ backends = talloc_zero_array(gensec_settings,
+ const struct gensec_security_ops *, 4);
+ if (backends == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gensec_settings->backends = backends;
+
+ gensec_init();
+
+ /* These need to be in priority order, krb5 before NTLMSSP */
+#if defined(HAVE_KRB5)
+ backends[idx++] = &gensec_gse_krb5_security_ops;
+#endif
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+
+ nt_status = gensec_client_start(NULL, &gensec_security,
+ gensec_settings);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_unlink(tmp_ctx, gensec_settings);
+
+ if (opt_target_service != NULL) {
+ nt_status = gensec_set_target_service(gensec_security,
+ opt_target_service);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+ }
+
+ if (opt_target_hostname != NULL) {
+ nt_status = gensec_set_target_hostname(gensec_security,
+ opt_target_hostname);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+ }
+
+ *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw)
+{
+ struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context);
+ if (auth4_context == NULL) {
+ DEBUG(10, ("failed to allocate auth4_context\n"));
+ return NULL;
+ }
+ auth4_context->generate_session_info = ntlm_auth_generate_session_info;
+ auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac;
+ auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge;
+ auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge;
+ if (local_pw) {
+ auth4_context->check_ntlm_password_send = local_pw_check_send;
+ auth4_context->check_ntlm_password_recv = local_pw_check_recv;
+ } else {
+ auth4_context->check_ntlm_password_send =
+ winbind_pw_check_send;
+ auth4_context->check_ntlm_password_recv =
+ winbind_pw_check_recv;
+ }
+ auth4_context->private_data = NULL;
+ return auth4_context;
+}
+
+static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct gensec_security **gensec_security_out)
+{
+ struct gensec_security *gensec_security;
+ NTSTATUS nt_status;
+
+ TALLOC_CTX *tmp_ctx;
+ const struct gensec_security_ops **backends;
+ struct gensec_settings *gensec_settings;
+ size_t idx = 0;
+ struct cli_credentials *server_credentials;
+
+ struct auth4_context *auth4_context;
+
+ tmp_ctx = talloc_new(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password);
+ if (auth4_context == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx);
+ if (lp_ctx == NULL) {
+ DEBUG(10, ("lpcfg_gensec_settings failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * This should be a 'netbios domain -> DNS domain'
+ * mapping, and can currently validly return NULL on
+ * poorly configured systems.
+ *
+ * This is used for the NTLMSSP server
+ *
+ */
+ if (opt_password) {
+ gensec_settings->server_netbios_name = lp_netbios_name();
+ gensec_settings->server_netbios_domain = lp_workgroup();
+ } else {
+ gensec_settings->server_netbios_name = get_winbind_netbios_name();
+ gensec_settings->server_netbios_domain = get_winbind_domain();
+ }
+
+ gensec_settings->server_dns_domain = strlower_talloc(gensec_settings,
+ get_mydnsdomname(talloc_tos()));
+ gensec_settings->server_dns_name = strlower_talloc(gensec_settings,
+ get_mydnsfullname());
+
+ backends = talloc_zero_array(gensec_settings,
+ const struct gensec_security_ops *, 4);
+
+ if (backends == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ gensec_settings->backends = backends;
+
+ gensec_init();
+
+ /* These need to be in priority order, krb5 before NTLMSSP */
+#if defined(HAVE_KRB5)
+ backends[idx++] = &gensec_gse_krb5_security_ops;
+#endif
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP);
+
+ backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO);
+
+ /*
+ * This is anonymous for now, because we just use it
+ * to set the kerberos state at the moment
+ */
+ server_credentials = cli_credentials_init_anon(tmp_ctx);
+ if (!server_credentials) {
+ DBG_ERR("Failed to init server credentials\n");
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cli_credentials_set_conf(server_credentials, lp_ctx);
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
+ cli_credentials_set_kerberos_state(server_credentials,
+ CRED_USE_KERBEROS_DESIRED,
+ CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_kerberos_state(server_credentials,
+ CRED_USE_KERBEROS_DISABLED,
+ CRED_SPECIFIED);
+ }
+
+ nt_status = gensec_server_start(tmp_ctx, gensec_settings,
+ auth4_context, &gensec_security);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ gensec_set_credentials(gensec_security, server_credentials);
+
+ /*
+ * TODO: Allow the caller to pass their own description here
+ * via a command-line option
+ */
+ nt_status = gensec_set_target_service_description(gensec_security,
+ "ntlm_auth");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ TALLOC_FREE(tmp_ctx);
+ return nt_status;
+ }
+
+ talloc_unlink(tmp_ctx, lp_ctx);
+ talloc_unlink(tmp_ctx, server_credentials);
+ talloc_unlink(tmp_ctx, gensec_settings);
+ talloc_unlink(tmp_ctx, auth4_context);
+
+ *gensec_security_out = talloc_steal(mem_ctx, gensec_security);
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
+ return;
+}
+
+static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ char *user, *pass;
+ user=buf;
+
+ pass=(char *)memchr(buf,' ',length);
+ if (!pass) {
+ DEBUG(2, ("Password not found. Denying access\n"));
+ printf("ERR\n");
+ return;
+ }
+ *pass='\0';
+ pass++;
+
+ if (state->helper_mode == SQUID_2_5_BASIC) {
+ char *end = rfc1738_unescape(user);
+ if (end == NULL || (end - user) != strlen(user)) {
+ DEBUG(2, ("Badly rfc1738 encoded username: %s; "
+ "denying access\n", user));
+ printf("ERR\n");
+ return;
+ }
+ end = rfc1738_unescape(pass);
+ if (end == NULL || (end - pass) != strlen(pass)) {
+ DEBUG(2, ("Badly encoded password for %s; "
+ "denying access\n", user));
+ printf("ERR\n");
+ return;
+ }
+ }
+
+ if (check_plaintext_auth(user, pass, False)) {
+ printf("OK\n");
+ } else {
+ printf("ERR\n");
+ }
+}
+
+static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ char *buf, int length, void **private1)
+{
+ DATA_BLOB in;
+ DATA_BLOB out = data_blob(NULL, 0);
+ char *out_base64 = NULL;
+ const char *reply_arg = NULL;
+ struct gensec_ntlm_state {
+ struct gensec_security *gensec_state;
+ const char *set_password;
+ };
+ struct gensec_ntlm_state *state;
+
+ NTSTATUS nt_status;
+ bool first = false;
+ const char *reply_code;
+ struct cli_credentials *creds;
+
+ static char *want_feature_list = NULL;
+ static DATA_BLOB session_key;
+
+ TALLOC_CTX *mem_ctx;
+
+ mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx");
+ if (mem_ctx == NULL) {
+ printf("BH No Memory\n");
+ exit(1);
+ }
+
+ if (*private1) {
+ state = talloc_get_type(*private1, struct gensec_ntlm_state);
+ if (state == NULL) {
+ DBG_WARNING("*private1 is of type %s\n",
+ talloc_get_name(*private1));
+ printf("BH *private1 is of type %s\n",
+ talloc_get_name(*private1));
+ exit(1);
+ }
+ } else {
+ state = talloc_zero(NULL, struct gensec_ntlm_state);
+ if (!state) {
+ printf("BH No Memory\n");
+ exit(1);
+ }
+ *private1 = state;
+ if (opt_password) {
+ state->set_password = opt_password;
+ }
+ }
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("query [%s] invalid\n", buf));
+ printf("BH Query invalid\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ if(strncmp(buf, "SF ", 3) == 0) {
+ DEBUG(10, ("Setting flags to negotiate\n"));
+ talloc_free(want_feature_list);
+ want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3);
+ printf("OK\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+ in = base64_decode_data_blob_talloc(mem_ctx, buf + 3);
+ } else {
+ in = data_blob(NULL, 0);
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (state->gensec_state) {
+ talloc_free(state->gensec_state);
+ state->gensec_state = NULL;
+ }
+ } else if ( (strncmp(buf, "OK", 2) == 0)) {
+ /* Just return BH, like ntlm_auth from Samba 3 does. */
+ printf("BH Command expected\n");
+ talloc_free(mem_ctx);
+ return;
+ } else if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "KK ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) &&
+ (strncmp(buf, "UG", 2) != 0) &&
+ (strncmp(buf, "PW ", 3) != 0) &&
+ (strncmp(buf, "GK", 2) != 0) &&
+ (strncmp(buf, "GF", 2) != 0)) {
+ DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf));
+ printf("BH SPNEGO request invalid prefix\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ /* setup gensec */
+ if (!(state->gensec_state)) {
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_CLIENT:
+ /*
+ * cached credentials are only supported by
+ * NTLMSSP_CLIENT_1 for now.
+ */
+ use_cached_creds = false;
+ FALL_THROUGH;
+ case NTLMSSP_CLIENT_1:
+ /* setup the client side */
+
+ if (state->set_password != NULL) {
+ use_cached_creds = false;
+ }
+
+ if (use_cached_creds) {
+ struct wbcCredentialCacheParams params;
+ struct wbcCredentialCacheInfo *info = NULL;
+ struct wbcAuthErrorInfo *error = NULL;
+ wbcErr wbc_status;
+
+ params.account_name = opt_username;
+ params.domain_name = opt_domain;
+ params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP;
+ params.num_blobs = 0;
+ params.blobs = NULL;
+
+ wbc_status = wbcCredentialCache(&params, &info,
+ &error);
+ wbcFreeMemory(error);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ use_cached_creds = false;
+ }
+ wbcFreeMemory(info);
+ }
+
+ nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx,
+ &state->gensec_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("BH GENSEC mech failed to start: %s\n",
+ nt_errstr(nt_status));
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ creds = cli_credentials_init(state->gensec_state);
+ cli_credentials_set_conf(creds, lp_ctx);
+ if (opt_username) {
+ cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED);
+ }
+ if (opt_domain) {
+ cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED);
+ }
+ if (use_cached_creds) {
+ gensec_want_feature(state->gensec_state,
+ GENSEC_FEATURE_NTLM_CCACHE);
+ } else if (state->set_password) {
+ cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED);
+ } else {
+ cli_credentials_set_password_callback(creds, get_password);
+ }
+ if (opt_workstation) {
+ cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED);
+ }
+
+ gensec_set_credentials(state->gensec_state, creds);
+
+ break;
+ case GSS_SPNEGO_SERVER:
+ case SQUID_2_5_NTLMSSP:
+ {
+ nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx,
+ &state->gensec_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("BH GENSEC mech failed to start: %s\n",
+ nt_errstr(nt_status));
+ talloc_free(mem_ctx);
+ return;
+ }
+ break;
+ }
+ default:
+ talloc_free(mem_ctx);
+ abort();
+ }
+
+ gensec_want_feature_list(state->gensec_state, want_feature_list);
+
+ /* Session info is not complete, do not pass to auth log */
+ gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG);
+
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_CLIENT:
+ case GSS_SPNEGO_SERVER:
+ nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO);
+ if (!in.length) {
+ first = true;
+ }
+ break;
+ case NTLMSSP_CLIENT_1:
+ if (!in.length) {
+ first = true;
+ }
+ FALL_THROUGH;
+ case SQUID_2_5_NTLMSSP:
+ nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP);
+ break;
+ default:
+ talloc_free(mem_ctx);
+ abort();
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status)));
+ printf("BH GENSEC mech failed to start\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ }
+
+ /* update */
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ state->set_password = talloc_strndup(state,
+ (const char *)in.data,
+ in.length);
+
+ cli_credentials_set_password(gensec_get_credentials(state->gensec_state),
+ state->set_password,
+ CRED_SPECIFIED);
+ printf("OK\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strncmp(buf, "GK", 2) == 0) {
+ char *base64_key;
+ DEBUG(10, ("Requested session key\n"));
+ nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key);
+ if(!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status)));
+ printf("BH No session key\n");
+ talloc_free(mem_ctx);
+ return;
+ } else {
+ base64_key = base64_encode_data_blob(state, session_key);
+ SMB_ASSERT(base64_key != NULL);
+ printf("GK %s\n", base64_key);
+ talloc_free(base64_key);
+ }
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ if (strncmp(buf, "GF", 2) == 0) {
+ uint32_t neg_flags;
+
+ DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n"));
+
+ neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state);
+ if (neg_flags == 0) {
+ printf("BH\n");
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ printf("GF 0x%08x\n", neg_flags);
+ talloc_free(mem_ctx);
+ return;
+ }
+
+ nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out);
+
+ /* don't leak 'bad password'/'no such user' info to the network client */
+ nt_status = nt_status_squash(nt_status);
+
+ if (out.length) {
+ out_base64 = base64_encode_data_blob(mem_ctx, out);
+ SMB_ASSERT(out_base64 != NULL);
+ } else {
+ out_base64 = NULL;
+ }
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ reply_arg = "*";
+ if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) {
+ reply_code = "YR";
+ } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
+ reply_code = "KK";
+ } else if (state->gensec_state->gensec_role == GENSEC_SERVER) {
+ reply_code = "TT";
+ } else {
+ abort();
+ }
+
+
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ reply_code = "BH NT_STATUS_ACCESS_DENIED";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) {
+ reply_code = "BH NT_STATUS_UNSUCCESSFUL";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ reply_code = "NA";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status)));
+ } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) {
+ struct auth_session_info *session_info;
+
+ nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ reply_code = "BH Failed to retrieve session info";
+ reply_arg = nt_errstr(nt_status);
+ DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status)));
+ } else {
+
+ reply_code = "AF";
+ reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name);
+ if (reply_arg == NULL) {
+ reply_code = "BH out of memory";
+ reply_arg = nt_errstr(NT_STATUS_NO_MEMORY);
+ }
+ talloc_free(session_info);
+ }
+ } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) {
+ reply_code = "AF";
+ reply_arg = out_base64;
+ } else {
+ abort();
+ }
+
+ switch (stdio_helper_mode) {
+ case GSS_SPNEGO_SERVER:
+ printf("%s %s %s\n", reply_code,
+ out_base64 ? out_base64 : "*",
+ reply_arg ? reply_arg : "*");
+ break;
+ default:
+ if (out_base64) {
+ printf("%s %s\n", reply_code, out_base64);
+ } else if (reply_arg) {
+ printf("%s %s\n", reply_code, reply_arg);
+ } else {
+ printf("%s\n", reply_code);
+ }
+ }
+
+ talloc_free(mem_ctx);
+ return;
+}
+
+static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
+ return;
+}
+
+static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
+ return;
+}
+
+static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1);
+ return;
+}
+
+static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ char *request, *parameter;
+ static DATA_BLOB challenge;
+ static DATA_BLOB lm_response;
+ static DATA_BLOB nt_response;
+ static char *full_username;
+ static char *username;
+ static char *domain;
+ static char *plaintext_password;
+ static bool ntlm_server_1_user_session_key;
+ static bool ntlm_server_1_lm_session_key;
+
+ if (strequal(buf, ".")) {
+ if (!full_username && !username) {
+ printf("Error: No username supplied!\n");
+ } else if (plaintext_password) {
+ /* handle this request as plaintext */
+ if (!full_username) {
+ if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) {
+ printf("Error: Out of memory in "
+ "asprintf!\n.\n");
+ return;
+ }
+ }
+ if (check_plaintext_auth(full_username, plaintext_password, False)) {
+ printf("Authenticated: Yes\n");
+ } else {
+ printf("Authenticated: No\n");
+ }
+ } else if (!lm_response.data && !nt_response.data) {
+ printf("Error: No password supplied!\n");
+ } else if (!challenge.data) {
+ printf("Error: No lanman-challenge supplied!\n");
+ } else {
+ char *error_string = NULL;
+ uchar lm_key[8];
+ uchar user_session_key[16];
+ uint32_t flags = 0;
+ NTSTATUS nt_status;
+ if (full_username && !username) {
+ fstring fstr_user;
+ fstring fstr_domain;
+
+ if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) {
+ /* username might be 'tainted', don't print into our new-line deleimianted stream */
+ printf("Error: Could not parse into "
+ "domain and username\n");
+ }
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ username = smb_xstrdup(fstr_user);
+ domain = smb_xstrdup(fstr_domain);
+ }
+
+ if (opt_password) {
+ DATA_BLOB nt_session_key, lm_session_key;
+ struct samr_Password lm_pw, nt_pw;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ ZERO_STRUCT(user_session_key);
+ ZERO_STRUCT(lm_key);
+
+ nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash);
+ nt_status = ntlm_password_check(mem_ctx,
+ true,
+ NTLM_AUTH_ON,
+ 0,
+ &challenge,
+ &lm_response,
+ &nt_response,
+ username,
+ username,
+ domain,
+ &lm_pw, &nt_pw,
+ &nt_session_key,
+ &lm_session_key);
+ error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status));
+ if (ntlm_server_1_user_session_key) {
+ if (nt_session_key.length == sizeof(user_session_key)) {
+ memcpy(user_session_key,
+ nt_session_key.data,
+ sizeof(user_session_key));
+ }
+ }
+ if (ntlm_server_1_lm_session_key) {
+ if (lm_session_key.length == sizeof(lm_key)) {
+ memcpy(lm_key,
+ lm_session_key.data,
+ sizeof(lm_key));
+ }
+ }
+ TALLOC_FREE(mem_ctx);
+
+ } else {
+ uint8_t authoritative = 1;
+
+ if (!domain) {
+ domain = smb_xstrdup(get_winbind_domain());
+ }
+
+ if (ntlm_server_1_lm_session_key)
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if (ntlm_server_1_user_session_key)
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ nt_status = contact_winbind_auth_crap(username,
+ domain,
+ lp_netbios_name(),
+ &challenge,
+ &lm_response,
+ &nt_response,
+ flags, 0,
+ lm_key,
+ user_session_key,
+ &authoritative,
+ &error_string,
+ NULL);
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("Authenticated: No\n");
+ printf("Authentication-Error: %s\n.\n",
+ error_string);
+ } else {
+ char *hex_lm_key;
+ char *hex_user_session_key;
+
+ printf("Authenticated: Yes\n");
+
+ if (ntlm_server_1_lm_session_key
+ && (!all_zero(lm_key,
+ sizeof(lm_key)))) {
+ hex_lm_key = hex_encode_talloc(NULL,
+ (const unsigned char *)lm_key,
+ sizeof(lm_key));
+ printf("LANMAN-Session-Key: %s\n",
+ hex_lm_key);
+ TALLOC_FREE(hex_lm_key);
+ }
+
+ if (ntlm_server_1_user_session_key
+ && (!all_zero(user_session_key,
+ sizeof(user_session_key)))) {
+ hex_user_session_key = hex_encode_talloc(NULL,
+ (const unsigned char *)user_session_key,
+ sizeof(user_session_key));
+ printf("User-Session-Key: %s\n",
+ hex_user_session_key);
+ TALLOC_FREE(hex_user_session_key);
+ }
+ }
+ SAFE_FREE(error_string);
+ }
+ /* clear out the state */
+ challenge = data_blob_null;
+ nt_response = data_blob_null;
+ lm_response = data_blob_null;
+ SAFE_FREE(full_username);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(plaintext_password);
+ ntlm_server_1_user_session_key = False;
+ ntlm_server_1_lm_session_key = False;
+ printf(".\n");
+
+ return;
+ }
+
+ request = buf;
+
+ /* Indicates a base64 encoded structure */
+ parameter = strstr_m(request, ":: ");
+ if (!parameter) {
+ parameter = strstr_m(request, ": ");
+
+ if (!parameter) {
+ DEBUG(0, ("Parameter not found!\n"));
+ printf("Error: Parameter not found!\n.\n");
+ return;
+ }
+
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ } else {
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ base64_decode_inplace(parameter);
+ }
+
+ if (strequal(request, "LANMAN-Challenge")) {
+ challenge = strhex_to_data_blob(NULL, parameter);
+ if (challenge.length != 8) {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 8)\n.\n",
+ parameter,
+ (int)challenge.length);
+ challenge = data_blob_null;
+ }
+ } else if (strequal(request, "NT-Response")) {
+ nt_response = strhex_to_data_blob(NULL, parameter);
+ if (nt_response.length < 24) {
+ printf("Error: hex decode of %s failed! "
+ "(only got %d bytes, needed at least 24)\n.\n",
+ parameter,
+ (int)nt_response.length);
+ nt_response = data_blob_null;
+ }
+ } else if (strequal(request, "LANMAN-Response")) {
+ lm_response = strhex_to_data_blob(NULL, parameter);
+ if (lm_response.length != 24) {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 24)\n.\n",
+ parameter,
+ (int)lm_response.length);
+ lm_response = data_blob_null;
+ }
+ } else if (strequal(request, "Password")) {
+ plaintext_password = smb_xstrdup(parameter);
+ } else if (strequal(request, "NT-Domain")) {
+ domain = smb_xstrdup(parameter);
+ } else if (strequal(request, "Username")) {
+ username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Full-Username")) {
+ full_username = smb_xstrdup(parameter);
+ } else if (strequal(request, "Request-User-Session-Key")) {
+ ntlm_server_1_user_session_key = strequal(parameter, "Yes");
+ } else if (strequal(request, "Request-LanMan-Session-Key")) {
+ ntlm_server_1_lm_session_key = strequal(parameter, "Yes");
+ } else {
+ printf("Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ char *buf, int length, void **private2)
+{
+ char *request, *parameter;
+ static DATA_BLOB new_nt_pswd;
+ static DATA_BLOB old_nt_hash_enc;
+ static DATA_BLOB new_lm_pswd;
+ static DATA_BLOB old_lm_hash_enc;
+ static char *full_username = NULL;
+ static char *username = NULL;
+ static char *domain = NULL;
+ static char *newpswd = NULL;
+ static char *oldpswd = NULL;
+
+ if (strequal(buf, ".")) {
+ if(newpswd && oldpswd) {
+ uchar old_nt_hash[16];
+ uchar old_lm_hash[16];
+ uchar new_nt_hash[16];
+ uchar new_lm_hash[16];
+
+ gnutls_cipher_hd_t cipher_hnd = NULL;
+ gnutls_datum_t old_nt_key = {
+ .data = old_nt_hash,
+ .size = sizeof(old_nt_hash),
+ };
+ int rc;
+
+ new_nt_pswd = data_blob(NULL, 516);
+ old_nt_hash_enc = data_blob(NULL, 16);
+
+ /* Calculate the MD4 hash (NT compatible) of the
+ * password */
+ E_md4hash(oldpswd, old_nt_hash);
+ E_md4hash(newpswd, new_nt_hash);
+
+ /* E_deshash returns false for 'long'
+ passwords (> 14 DOS chars).
+
+ Therefore, don't send a buffer
+ encrypted with the truncated hash
+ (it could allow an even easier
+ attack on the password)
+
+ Likewise, obey the admin's restriction
+ */
+
+ rc = gnutls_cipher_init(&cipher_hnd,
+ GNUTLS_CIPHER_ARCFOUR_128,
+ &old_nt_key,
+ NULL);
+ if (rc < 0) {
+ DBG_ERR("gnutls_cipher_init failed: %s\n",
+ gnutls_strerror(rc));
+ if (rc == GNUTLS_E_UNWANTED_ALGORITHM) {
+ DBG_ERR("Running in FIPS mode, NTLM blocked\n");
+ }
+ return;
+ }
+
+ if (lp_client_lanman_auth() &&
+ E_deshash(newpswd, new_lm_hash) &&
+ E_deshash(oldpswd, old_lm_hash)) {
+ new_lm_pswd = data_blob(NULL, 516);
+ old_lm_hash_enc = data_blob(NULL, 16);
+ encode_pw_buffer(new_lm_pswd.data, newpswd,
+ STR_UNICODE);
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ new_lm_pswd.data,
+ 516);
+ if (rc < 0) {
+ gnutls_cipher_deinit(cipher_hnd);
+ return;
+ }
+ rc = E_old_pw_hash(new_nt_hash, old_lm_hash,
+ old_lm_hash_enc.data);
+ if (rc != 0) {
+ DBG_ERR("E_old_pw_hash failed: %s\n",
+ gnutls_strerror(rc));
+ return;
+ }
+ } else {
+ new_lm_pswd.data = NULL;
+ new_lm_pswd.length = 0;
+ old_lm_hash_enc.data = NULL;
+ old_lm_hash_enc.length = 0;
+ }
+
+ encode_pw_buffer(new_nt_pswd.data, newpswd,
+ STR_UNICODE);
+
+ rc = gnutls_cipher_encrypt(cipher_hnd,
+ new_nt_pswd.data,
+ 516);
+ gnutls_cipher_deinit(cipher_hnd);
+ if (rc < 0) {
+ return;
+ }
+ rc = E_old_pw_hash(new_nt_hash, old_nt_hash,
+ old_nt_hash_enc.data);
+ if (rc != 0) {
+ DBG_ERR("E_old_pw_hash failed: %s\n",
+ gnutls_strerror(rc));
+ return;
+ }
+
+ ZERO_ARRAY(old_nt_hash);
+ ZERO_ARRAY(old_lm_hash);
+ ZERO_ARRAY(new_nt_hash);
+ ZERO_ARRAY(new_lm_hash);
+ }
+
+ if (!full_username && !username) {
+ printf("Error: No username supplied!\n");
+ } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) &&
+ (!new_lm_pswd.data || old_lm_hash_enc.data) ) {
+ printf("Error: No NT or LM password "
+ "blobs supplied!\n");
+ } else {
+ char *error_string = NULL;
+
+ if (full_username && !username) {
+ fstring fstr_user;
+ fstring fstr_domain;
+
+ if (!parse_ntlm_auth_domain_user(full_username,
+ fstr_user,
+ fstr_domain)) {
+ /* username might be 'tainted', don't
+ * print into our new-line
+ * deleimianted stream */
+ printf("Error: Could not "
+ "parse into domain and "
+ "username\n");
+ SAFE_FREE(username);
+ username = smb_xstrdup(full_username);
+ } else {
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ username = smb_xstrdup(fstr_user);
+ domain = smb_xstrdup(fstr_domain);
+ }
+
+ }
+
+ if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap(
+ username, domain,
+ new_nt_pswd,
+ old_nt_hash_enc,
+ new_lm_pswd,
+ old_lm_hash_enc,
+ &error_string))) {
+ printf("Password-Change: No\n");
+ printf("Password-Change-Error: %s\n.\n",
+ error_string);
+ } else {
+ printf("Password-Change: Yes\n");
+ }
+
+ SAFE_FREE(error_string);
+ }
+ /* clear out the state */
+ new_nt_pswd = data_blob_null;
+ old_nt_hash_enc = data_blob_null;
+ new_lm_pswd = data_blob_null;
+ old_nt_hash_enc = data_blob_null;
+ SAFE_FREE(full_username);
+ SAFE_FREE(username);
+ SAFE_FREE(domain);
+ SAFE_FREE(newpswd);
+ SAFE_FREE(oldpswd);
+ printf(".\n");
+
+ return;
+ }
+
+ request = buf;
+
+ /* Indicates a base64 encoded structure */
+ parameter = strstr_m(request, ":: ");
+ if (!parameter) {
+ parameter = strstr_m(request, ": ");
+
+ if (!parameter) {
+ DEBUG(0, ("Parameter not found!\n"));
+ printf("Error: Parameter not found!\n.\n");
+ return;
+ }
+
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+ } else {
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+ parameter[0] ='\0';
+ parameter++;
+
+ base64_decode_inplace(parameter);
+ }
+
+ if (strequal(request, "new-nt-password-blob")) {
+ new_nt_pswd = strhex_to_data_blob(NULL, parameter);
+ if (new_nt_pswd.length != 516) {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 516)\n.\n",
+ parameter,
+ (int)new_nt_pswd.length);
+ new_nt_pswd = data_blob_null;
+ }
+ } else if (strequal(request, "old-nt-hash-blob")) {
+ old_nt_hash_enc = strhex_to_data_blob(NULL, parameter);
+ if (old_nt_hash_enc.length != 16) {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 16)\n.\n",
+ parameter,
+ (int)old_nt_hash_enc.length);
+ old_nt_hash_enc = data_blob_null;
+ }
+ } else if (strequal(request, "new-lm-password-blob")) {
+ new_lm_pswd = strhex_to_data_blob(NULL, parameter);
+ if (new_lm_pswd.length != 516) {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 516)\n.\n",
+ parameter,
+ (int)new_lm_pswd.length);
+ new_lm_pswd = data_blob_null;
+ }
+ }
+ else if (strequal(request, "old-lm-hash-blob")) {
+ old_lm_hash_enc = strhex_to_data_blob(NULL, parameter);
+ if (old_lm_hash_enc.length != 16)
+ {
+ printf("Error: hex decode of %s failed! "
+ "(got %d bytes, expected 16)\n.\n",
+ parameter,
+ (int)old_lm_hash_enc.length);
+ old_lm_hash_enc = data_blob_null;
+ }
+ } else if (strequal(request, "nt-domain")) {
+ domain = smb_xstrdup(parameter);
+ } else if(strequal(request, "username")) {
+ username = smb_xstrdup(parameter);
+ } else if(strequal(request, "full-username")) {
+ username = smb_xstrdup(parameter);
+ } else if(strequal(request, "new-password")) {
+ newpswd = smb_xstrdup(parameter);
+ } else if (strequal(request, "old-password")) {
+ oldpswd = smb_xstrdup(parameter);
+ } else {
+ printf("Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode,
+ struct loadparm_context *lp_ctx,
+ struct ntlm_auth_state *state,
+ stdio_helper_function fn, void **private2)
+{
+ char *buf;
+ char tmp[INITIAL_BUFFER_SIZE+1];
+ int length, buf_size = 0;
+ char *c;
+
+ buf = talloc_strdup(state->mem_ctx, "");
+ if (!buf) {
+ DEBUG(0, ("Failed to allocate input buffer.\n"));
+ fprintf(stderr, "ERR\n");
+ exit(1);
+ }
+
+ do {
+
+ /* this is not a typo - x_fgets doesn't work too well under
+ * squid */
+ if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) {
+ if (ferror(stdin)) {
+ DEBUG(1, ("fgets() failed! dying..... errno=%d "
+ "(%s)\n", ferror(stdin),
+ strerror(ferror(stdin))));
+
+ exit(1);
+ }
+ exit(0);
+ }
+
+ buf = talloc_strdup_append_buffer(buf, tmp);
+ buf_size += INITIAL_BUFFER_SIZE;
+
+ if (buf_size > MAX_BUFFER_SIZE) {
+ DEBUG(2, ("Oversized message\n"));
+ fprintf(stderr, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+
+ c = strchr(buf, '\n');
+ } while (c == NULL);
+
+ *c = '\0';
+ length = c-buf;
+
+ DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length));
+
+ if (buf[0] == '\0') {
+ DEBUG(2, ("Invalid Request\n"));
+ fprintf(stderr, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+
+ fn(stdio_helper_mode, lp_ctx, state, buf, length, private2);
+ talloc_free(buf);
+}
+
+
+static void squid_stream(enum stdio_helper_mode stdio_mode,
+ struct loadparm_context *lp_ctx,
+ stdio_helper_function fn) {
+ TALLOC_CTX *mem_ctx;
+ struct ntlm_auth_state *state;
+
+ /* initialize FDescs */
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ mem_ctx = talloc_init("ntlm_auth");
+ if (!mem_ctx) {
+ DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
+ fprintf(stderr, "ERR\n");
+ exit(1);
+ }
+
+ state = talloc_zero(mem_ctx, struct ntlm_auth_state);
+ if (!state) {
+ DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n"));
+ fprintf(stderr, "ERR\n");
+ exit(1);
+ }
+
+ state->mem_ctx = mem_ctx;
+ state->helper_mode = stdio_mode;
+
+ while(1) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL);
+ TALLOC_FREE(frame);
+ }
+}
+
+
+/* Authenticate a user with a challenge/response */
+
+static bool check_auth_crap(void)
+{
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ char lm_key[8];
+ char user_session_key[16];
+ char *hex_lm_key;
+ char *hex_user_session_key;
+ char *error_string;
+ uint8_t authoritative = 1;
+
+ setbuf(stdout, NULL);
+
+ if (request_lm_key)
+ flags |= WBFLAG_PAM_LMKEY;
+
+ if (request_user_session_key)
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ flags |= WBFLAG_PAM_NT_STATUS_SQUASH;
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &opt_challenge,
+ &opt_lm_response,
+ &opt_nt_response,
+ flags, 0,
+ (unsigned char *)lm_key,
+ (unsigned char *)user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ printf("%s (0x%x)\n", error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (request_lm_key
+ && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) {
+ hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key,
+ sizeof(lm_key));
+ printf("LM_KEY: %s\n", hex_lm_key);
+ TALLOC_FREE(hex_lm_key);
+ }
+ if (request_user_session_key
+ && (!all_zero((uint8_t *)user_session_key,
+ sizeof(user_session_key)))) {
+ hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key,
+ sizeof(user_session_key));
+ printf("NT_KEY: %s\n", hex_user_session_key);
+ TALLOC_FREE(hex_user_session_key);
+ }
+
+ return True;
+}
+
+/* Main program */
+
+enum {
+ OPT_USERNAME = 1000,
+ OPT_DOMAIN,
+ OPT_WORKSTATION,
+ OPT_CHALLENGE,
+ OPT_RESPONSE,
+ OPT_LM,
+ OPT_NT,
+ OPT_PASSWORD,
+ OPT_LM_KEY,
+ OPT_USER_SESSION_KEY,
+ OPT_DIAGNOSTICS,
+ OPT_REQUIRE_MEMBERSHIP,
+ OPT_USE_CACHED_CREDS,
+ OPT_ALLOW_MSCHAPV2,
+ OPT_PAM_WINBIND_CONF,
+ OPT_TARGET_SERVICE,
+ OPT_TARGET_HOSTNAME,
+ OPT_OFFLINE_LOGON
+};
+
+ int main(int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ int opt;
+ const char *helper_protocol = NULL;
+ int diagnostics = 0;
+
+ const char *hex_challenge = NULL;
+ const char *hex_lm_response = NULL;
+ const char *hex_nt_response = NULL;
+ struct loadparm_context *lp_ctx;
+ poptContext pc;
+ bool ok;
+
+ /* NOTE: DO NOT change this interface without considering the implications!
+ This is an external interface, which other programs will use to interact
+ with this helper.
+ */
+
+ /* We do not use single-letter command abbreviations, because they harm future
+ interface stability. */
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "helper-protocol",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &helper_protocol,
+ .val = OPT_DOMAIN,
+ .descrip = "operate as a stdio-based helper",
+ .argDescrip = "helper protocol to use"
+ },
+ {
+ .longName = "username",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_username,
+ .val = OPT_USERNAME,
+ .descrip = "username"
+ },
+ {
+ .longName = "domain",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_domain,
+ .val = OPT_DOMAIN,
+ .descrip = "domain name"
+ },
+ {
+ .longName = "workstation",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_workstation,
+ .val = OPT_WORKSTATION,
+ .descrip = "workstation"
+ },
+ {
+ .longName = "challenge",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &hex_challenge,
+ .val = OPT_CHALLENGE,
+ .descrip = "challenge (HEX encoded)"
+ },
+ {
+ .longName = "lm-response",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &hex_lm_response,
+ .val = OPT_LM,
+ .descrip = "LM Response to the challenge (HEX encoded)"
+ },
+ {
+ .longName = "nt-response",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &hex_nt_response,
+ .val = OPT_NT,
+ .descrip = "NT or NTLMv2 Response to the challenge (HEX encoded)"
+ },
+ {
+ .longName = "password",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_password,
+ .val = OPT_PASSWORD,
+ .descrip = "User's plaintext password"
+ },
+ {
+ .longName = "request-lm-key",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &request_lm_key,
+ .val = OPT_LM_KEY,
+ .descrip = "Retrieve LM session key (or, with --diagnostics, expect LM support)"
+ },
+ {
+ .longName = "request-nt-key",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &request_user_session_key,
+ .val = OPT_USER_SESSION_KEY,
+ .descrip = "Retrieve User (NT) session key"
+ },
+ {
+ .longName = "use-cached-creds",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &use_cached_creds,
+ .val = OPT_USE_CACHED_CREDS,
+ .descrip = "Use cached credentials if no password is given"
+ },
+ {
+ .longName = "allow-mschapv2",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt_allow_mschapv2,
+ .val = OPT_ALLOW_MSCHAPV2,
+ .descrip = "Explicitly allow MSCHAPv2",
+ },
+ {
+ .longName = "offline-logon",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &offline_logon,
+ .val = OPT_OFFLINE_LOGON,
+ .descrip = "Use cached passwords when DC is offline"
+ },
+ {
+ .longName = "diagnostics",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &diagnostics,
+ .val = OPT_DIAGNOSTICS,
+ .descrip = "Perform diagnostics on the authentication chain"
+ },
+ {
+ .longName = "require-membership-of",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &require_membership_of,
+ .val = OPT_REQUIRE_MEMBERSHIP,
+ .descrip = "Require that a user be a member of this group (either name or SID) for authentication to succeed",
+ },
+ {
+ .longName = "pam-winbind-conf",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_pam_winbind_conf,
+ .val = OPT_PAM_WINBIND_CONF,
+ .descrip = "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required",
+ },
+ {
+ .longName = "target-service",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_target_service,
+ .val = OPT_TARGET_SERVICE,
+ .descrip = "Target service (eg http)",
+ },
+ {
+ .longName = "target-hostname",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt_target_hostname,
+ .val = OPT_TARGET_HOSTNAME,
+ .descrip = "Target hostname",
+ },
+ POPT_COMMON_DEBUG_ONLY
+ POPT_COMMON_CONFIG_ONLY
+ POPT_COMMON_OPTION_ONLY
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_CHALLENGE:
+ opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
+ if (opt_challenge.length != 8) {
+ fprintf(stderr, "hex decode of %s failed! "
+ "(only got %d bytes)\n",
+ hex_challenge,
+ (int)opt_challenge.length);
+ exit(1);
+ }
+ break;
+ case OPT_LM:
+ opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response);
+ if (opt_lm_response.length != 24) {
+ fprintf(stderr, "hex decode of %s failed! "
+ "(only got %d bytes)\n",
+ hex_lm_response,
+ (int)opt_lm_response.length);
+ exit(1);
+ }
+ break;
+
+ case OPT_NT:
+ opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response);
+ if (opt_nt_response.length < 24) {
+ fprintf(stderr, "hex decode of %s failed! "
+ "(only got %d bytes)\n",
+ hex_nt_response,
+ (int)opt_nt_response.length);
+ exit(1);
+ }
+ break;
+
+ case OPT_REQUIRE_MEMBERSHIP:
+ if (strncasecmp_m("S-", require_membership_of, 2) == 0) {
+ require_membership_of_sid = require_membership_of;
+ }
+ break;
+
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (opt_username) {
+ char *domain = SMB_STRDUP(opt_username);
+ char *p = strchr_m(domain, *lp_winbind_separator());
+ if (p) {
+ opt_username = p+1;
+ *p = '\0';
+ if (opt_domain && !strequal(opt_domain, domain)) {
+ fprintf(stderr, "Domain specified in username (%s) "
+ "doesn't match specified domain (%s)!\n\n",
+ domain, opt_domain);
+ poptPrintHelp(pc, stderr, 0);
+ exit(1);
+ }
+ opt_domain = domain;
+ } else {
+ SAFE_FREE(domain);
+ }
+ }
+
+ /* Note: if opt_domain is "" then send no domain */
+ if (opt_domain == NULL) {
+ opt_domain = get_winbind_domain();
+ }
+
+ if (opt_workstation == NULL) {
+ opt_workstation = "";
+ }
+
+ lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ fprintf(stderr, "loadparm_init_s3() failed!\n");
+ exit(1);
+ }
+
+ if (helper_protocol) {
+ int i;
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) {
+ squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn);
+ exit(0);
+ }
+ }
+ fprintf(stderr, "unknown helper protocol [%s]\n\n"
+ "Valid helper protools:\n\n", helper_protocol);
+
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ fprintf(stderr, "%s\n",
+ stdio_helper_protocols[i].name);
+ }
+
+ exit(1);
+ }
+
+ if (!opt_username || !*opt_username) {
+ fprintf(stderr, "username must be specified!\n\n");
+ poptPrintHelp(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (opt_challenge.length) {
+ if (!check_auth_crap()) {
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if (!opt_password) {
+ char pwd[256] = {0};
+ int rc;
+
+ rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false);
+ if (rc == 0) {
+ opt_password = SMB_STRDUP(pwd);
+ }
+ }
+
+ if (diagnostics) {
+ if (!diagnose_ntlm_auth(request_lm_key)) {
+ poptFreeContext(pc);
+ return 1;
+ }
+ } else {
+ fstring user;
+
+ fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username);
+ if (!check_plaintext_auth(user, opt_password, True)) {
+ poptFreeContext(pc);
+ return 1;
+ }
+ }
+
+ /* Exit code */
+ gfree_all();
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/ntlm_auth.h b/source3/utils/ntlm_auth.h
new file mode 100644
index 0000000..fb1dd62
--- /dev/null
+++ b/source3/utils/ntlm_auth.h
@@ -0,0 +1,26 @@
+/*
+ Samba Unix/Linux NTLM authentication tool
+
+ Copyright (C) 2001 Andrew Bartlett (abartlet@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "utils/ntlm_auth_proto.h"
+
+/* Some of the popt variables are needed in the diagnostics code */
+extern const char *opt_username;
+extern const char *opt_domain;
+extern const char *opt_workstation;
+extern const char *opt_password;
+
diff --git a/source3/utils/ntlm_auth_diagnostics.c b/source3/utils/ntlm_auth_diagnostics.c
new file mode 100644
index 0000000..6a76e73
--- /dev/null
+++ b/source3/utils/ntlm_auth_diagnostics.c
@@ -0,0 +1,724 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Winbind status program.
+
+ Copyright (C) Tim Potter 2000-2003
+ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004
+ Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "utils/ntlm_auth.h"
+#include "../libcli/auth/libcli_auth.h"
+#include "nsswitch/winbind_client.h"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_WINBIND
+
+enum ntlm_break {
+ BREAK_NONE,
+ BREAK_LM,
+ BREAK_NT,
+ NO_LM,
+ NO_NT
+};
+
+/*
+ Authenticate a user with a challenge/response, checking session key
+ and valid authentication types
+*/
+
+/*
+ * Test the normal 'LM and NTLM' combination
+ */
+
+static bool test_lm_ntlm_broken(enum ntlm_break break_which,
+ bool lanman_support_expected)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ DATA_BLOB lm_response = data_blob(NULL, 24);
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+ uint8_t authoritative = 1;
+ uchar lm_key[8];
+ uchar user_session_key[16];
+ uchar lm_hash[16];
+ uchar nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(user_session_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ SMBencrypt(opt_password,chall.data,lm_response.data);
+ E_deshash(opt_password, lm_hash);
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_md4hash(opt_password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, session_key.data);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lm_response);
+ break;
+ case NO_NT:
+ data_blob_free(&nt_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags, 0,
+ lm_key,
+ user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ data_blob_free(&lm_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ /* If we are told the DC is Samba4, expect an LM key of zeros */
+ if (!lanman_support_expected) {
+ if (!all_zero(lm_key,
+ sizeof(lm_key))) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected: all zeros\n"));
+ pass = False;
+ }
+ } else {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ }
+
+ if (break_which == NO_NT) {
+ if (memcmp(lm_hash, user_session_key,
+ 8) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, sizeof(user_session_key));
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, sizeof(lm_hash));
+ pass = False;
+ }
+ } else {
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, session_key.data, session_key.length);
+ pass = False;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test LM authentication, no NT response supplied
+ */
+
+static bool test_lm(bool lanman_support_expected)
+{
+
+ return test_lm_ntlm_broken(NO_NT, lanman_support_expected);
+}
+
+/*
+ * Test the NTLM response only, no LM.
+ */
+
+static bool test_ntlm(bool lanman_support_expected)
+{
+ return test_lm_ntlm_broken(NO_LM, lanman_support_expected);
+}
+
+/*
+ * Test the NTLM response only, but in the LM field.
+ */
+
+static bool test_ntlm_in_lm(bool lanman_support_expected)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ uint8_t authoritative = 1;
+ uchar lm_key[8];
+ uchar lm_hash[16];
+ uchar user_session_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(user_session_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+
+ E_deshash(opt_password, lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ NULL,
+ flags, 0,
+ lm_key,
+ user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ /* If we are told the DC is Samba4, expect an LM key of zeros */
+ if (!lanman_support_expected) {
+ if (!all_zero(lm_key,
+ sizeof(lm_key))) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected: all zeros\n"));
+ pass = False;
+ }
+ if (!all_zero(user_session_key,
+ sizeof(user_session_key))) {
+ DEBUG(1, ("Session Key (normally first 8 lm hash) does not match expectations!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, 16);
+ DEBUG(1, ("expected all zeros:\n"));
+ pass = False;
+ }
+ } else {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ if (memcmp(lm_hash, user_session_key, 8) != 0) {
+ DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLM response only, but in the both the NT and LM fields.
+ */
+
+static bool test_ntlm_in_both(bool lanman_support_expected)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+ uint8_t authoritative = 1;
+ uint8_t lm_key[8];
+ uint8_t lm_hash[16];
+ uint8_t user_session_key[16];
+ uint8_t nt_hash[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(lm_key);
+ ZERO_STRUCT(user_session_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ SMBNTencrypt(opt_password,chall.data,nt_response.data);
+ E_md4hash(opt_password, nt_hash);
+ SMBsesskeygen_ntv1(nt_hash, session_key.data);
+
+ E_deshash(opt_password, lm_hash);
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &nt_response,
+ &nt_response,
+ flags, 0,
+ lm_key,
+ user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ data_blob_free(&nt_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ /* If we are told the DC is Samba4, expect an LM key of zeros */
+ if (!lanman_support_expected) {
+ if (!all_zero(lm_key,
+ sizeof(lm_key))) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected: all zeros\n"));
+ pass = False;
+ }
+ } else {
+ if (memcmp(lm_hash, lm_key,
+ sizeof(lm_key)) != 0) {
+ DEBUG(1, ("LM Key does not match expectations!\n"));
+ DEBUG(1, ("lm_key:\n"));
+ dump_data(1, lm_key, 8);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, lm_hash, 8);
+ pass = False;
+ }
+ }
+ if (memcmp(session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ DEBUG(1, ("NT Session Key does not match expectations!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, session_key.data, session_key.length);
+ pass = False;
+ }
+
+
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static bool test_lmv2_ntlmv2_broken(enum ntlm_break break_which)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ DATA_BLOB ntlmv2_response = data_blob_null;
+ DATA_BLOB lmv2_response = data_blob_null;
+ DATA_BLOB ntlmv2_session_key = data_blob_null;
+ DATA_BLOB names_blob = NTLMv2_generate_names_blob(NULL, get_winbind_netbios_name(), get_winbind_domain());
+ uint8_t authoritative = 1;
+ uchar user_session_key[16];
+ DATA_BLOB chall = get_challenge();
+ char *error_string;
+
+ ZERO_STRUCT(user_session_key);
+
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ if (!SMBNTLMv2encrypt(NULL, opt_username, opt_domain, opt_password, &chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response, NULL,
+ &ntlmv2_session_key)) {
+ data_blob_free(&names_blob);
+ return False;
+ }
+ data_blob_free(&names_blob);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lmv2_response.data[0]++;
+ break;
+ case BREAK_NT:
+ ntlmv2_response.data[0]++;
+ break;
+ case NO_LM:
+ data_blob_free(&lmv2_response);
+ break;
+ case NO_NT:
+ data_blob_free(&ntlmv2_response);
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lmv2_response,
+ &ntlmv2_response,
+ flags, 0,
+ NULL,
+ user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ data_blob_free(&lmv2_response);
+ data_blob_free(&ntlmv2_response);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ if (break_which != NO_NT && break_which != BREAK_NT && memcmp(ntlmv2_session_key.data, user_session_key,
+ sizeof(user_session_key)) != 0) {
+ DEBUG(1, ("USER (NTLMv2) Session Key does not match expectations!\n"));
+ DEBUG(1, ("user_session_key:\n"));
+ dump_data(1, user_session_key, 16);
+ DEBUG(1, ("expected:\n"));
+ dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length);
+ pass = False;
+ }
+ return pass;
+}
+
+/*
+ * Test the NTLMv2 and LMv2 responses
+ */
+
+static bool test_lmv2_ntlmv2(bool lanman_support_expected)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NONE);
+}
+
+/*
+ * Test the LMv2 response only
+ */
+
+static bool test_lmv2(bool lanman_support_expected)
+{
+ return test_lmv2_ntlmv2_broken(NO_NT);
+}
+
+/*
+ * Test the NTLMv2 response only
+ */
+
+static bool test_ntlmv2(bool lanman_support_expected)
+{
+ return test_lmv2_ntlmv2_broken(NO_LM);
+}
+
+static bool test_lm_ntlm(bool lanman_support_expected)
+{
+ return test_lm_ntlm_broken(BREAK_NONE, lanman_support_expected);
+}
+
+static bool test_ntlm_lm_broken(bool lanman_support_expected)
+{
+ return test_lm_ntlm_broken(BREAK_LM, lanman_support_expected);
+}
+
+static bool test_ntlm_ntlm_broken(bool lanman_support_expected)
+{
+ return test_lm_ntlm_broken(BREAK_NT, lanman_support_expected);
+}
+
+static bool test_ntlmv2_lmv2_broken(bool lanman_support_expected)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_LM);
+}
+
+static bool test_ntlmv2_ntlmv2_broken(bool lanman_support_expected)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NT);
+}
+
+static bool test_plaintext(enum ntlm_break break_which)
+{
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+ DATA_BLOB nt_response = data_blob_null;
+ DATA_BLOB lm_response = data_blob_null;
+ char *password;
+ smb_ucs2_t *nt_response_ucs2;
+ size_t converted_size;
+ uint8_t authoritative = 1;
+ uchar user_session_key[16];
+ uchar lm_key[16];
+ static const uchar zeros[8] = { 0, };
+ DATA_BLOB chall = data_blob(zeros, sizeof(zeros));
+ char *error_string;
+
+ ZERO_STRUCT(user_session_key);
+
+ flags |= WBFLAG_PAM_LMKEY;
+ flags |= WBFLAG_PAM_USER_SESSION_KEY;
+
+ if (!push_ucs2_talloc(talloc_tos(), &nt_response_ucs2, opt_password,
+ &converted_size))
+ {
+ DEBUG(0, ("push_ucs2_talloc failed!\n"));
+ exit(1);
+ }
+
+ nt_response.data = (unsigned char *)nt_response_ucs2;
+ nt_response.length = strlen_w(nt_response_ucs2)*sizeof(smb_ucs2_t);
+
+ if ((password = strupper_talloc(talloc_tos(), opt_password)) == NULL) {
+ DEBUG(0, ("strupper_talloc() failed!\n"));
+ exit(1);
+ }
+
+ if (!convert_string_talloc(talloc_tos(), CH_UNIX,
+ CH_DOS, password,
+ strlen(password)+1,
+ &lm_response.data,
+ &lm_response.length)) {
+ DEBUG(0, ("convert_string_talloc failed!\n"));
+ exit(1);
+ }
+
+ TALLOC_FREE(password);
+
+ switch (break_which) {
+ case BREAK_NONE:
+ break;
+ case BREAK_LM:
+ lm_response.data[0]++;
+ break;
+ case BREAK_NT:
+ nt_response.data[0]++;
+ break;
+ case NO_LM:
+ TALLOC_FREE(lm_response.data);
+ lm_response.length = 0;
+ break;
+ case NO_NT:
+ TALLOC_FREE(nt_response.data);
+ nt_response.length = 0;
+ break;
+ }
+
+ nt_status = contact_winbind_auth_crap(opt_username, opt_domain,
+ opt_workstation,
+ &chall,
+ &lm_response,
+ &nt_response,
+ flags, MSV1_0_CLEARTEXT_PASSWORD_ALLOWED,
+ lm_key,
+ user_session_key,
+ &authoritative,
+ &error_string, NULL);
+
+ TALLOC_FREE(nt_response.data);
+ TALLOC_FREE(lm_response.data);
+ data_blob_free(&chall);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return break_which == BREAK_NT;
+ }
+
+ return break_which != BREAK_NT;
+}
+
+static bool test_plaintext_none_broken(bool lanman_support_expected) {
+ return test_plaintext(BREAK_NONE);
+}
+
+static bool test_plaintext_lm_broken(bool lanman_support_expected) {
+ return test_plaintext(BREAK_LM);
+}
+
+static bool test_plaintext_nt_broken(bool lanman_support_expected) {
+ return test_plaintext(BREAK_NT);
+}
+
+static bool test_plaintext_nt_only(bool lanman_support_expected) {
+ return test_plaintext(NO_LM);
+}
+
+static bool test_plaintext_lm_only(bool lanman_support_expected) {
+ return test_plaintext(NO_NT);
+}
+
+/*
+ Tests:
+
+ - LM only
+ - NT and LM
+ - NT
+ - NT in LM field
+ - NT in both fields
+ - NTLMv2
+ - NTLMv2 and LMv2
+ - LMv2
+ - plaintext tests (in challenge-response fields)
+
+ check we get the correct session key in each case
+ check what values we get for the LM session key
+
+*/
+
+static const struct ntlm_tests {
+ bool (*fn)(bool lanman_support_expected);
+ const char *name;
+ bool lanman;
+} test_table[] = {
+ {
+ .fn = test_lm,
+ .name = "LM",
+ .lanman = true
+ },
+ {
+ .fn = test_lm_ntlm,
+ .name = "LM and NTLM"
+ },
+ {
+ .fn = test_ntlm,
+ .name = "NTLM"
+ },
+ {
+ .fn = test_ntlm_in_lm,
+ .name = "NTLM in LM"
+ },
+ {
+ .fn = test_ntlm_in_both,
+ .name = "NTLM in both"
+ },
+ {
+ .fn = test_ntlmv2,
+ .name = "NTLMv2"
+ },
+ {
+ .fn = test_lmv2_ntlmv2,
+ .name = "NTLMv2 and LMv2"
+ },
+ {
+ .fn = test_lmv2,
+ .name = "LMv2"
+ },
+ {
+ .fn = test_ntlmv2_lmv2_broken,
+ .name = "NTLMv2 and LMv2, LMv2 broken"
+ },
+ {
+ .fn = test_ntlmv2_ntlmv2_broken,
+ .name = "NTLMv2 and LMv2, NTLMv2 broken"
+ },
+ {
+ .fn = test_ntlm_lm_broken,
+ .name = "NTLM and LM, LM broken"
+ },
+ {
+ .fn = test_ntlm_ntlm_broken,
+ .name = "NTLM and LM, NTLM broken"
+ },
+ {
+ .fn = test_plaintext_none_broken,
+ .name = "Plaintext"
+ },
+ {
+ .fn = test_plaintext_lm_broken,
+ .name = "Plaintext LM broken"
+ },
+ {
+ .fn = test_plaintext_nt_broken,
+ .name = "Plaintext NT broken"
+ },
+ {
+ .fn = test_plaintext_nt_only,
+ .name = "Plaintext NT only"
+ },
+ {
+ .fn = test_plaintext_lm_only,
+ .name = "Plaintext LM only",
+ .lanman = true
+ },
+ {
+ .fn = NULL
+ }
+};
+
+bool diagnose_ntlm_auth(bool lanman_support_expected)
+{
+ unsigned int i;
+ bool pass = True;
+
+ for (i=0; test_table[i].fn; i++) {
+ bool test_pass = test_table[i].fn(lanman_support_expected);
+ if (!lanman_support_expected
+ && test_table[i].lanman) {
+ if (test_pass) {
+ DBG_ERR("Test %s unexpectedly passed "
+ "(server should have rejected LM)!\n",
+ test_table[i].name);
+ pass = false;
+ }
+ } else if (!test_pass) {
+ DBG_ERR("Test %s failed!\n", test_table[i].name);
+ pass = False;
+ }
+ }
+
+ return pass;
+}
+
diff --git a/source3/utils/ntlm_auth_proto.h b/source3/utils/ntlm_auth_proto.h
new file mode 100644
index 0000000..ed6d5f4
--- /dev/null
+++ b/source3/utils/ntlm_auth_proto.h
@@ -0,0 +1,51 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * collected prototypes header
+ *
+ * frozen from "make proto" in May 2008
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _NTLM_AUTH_PROTO_H_
+#define _NTLM_AUTH_PROTO_H_
+
+
+/* The following definitions come from utils/ntlm_auth.c */
+
+const char *get_winbind_domain(void);
+const char *get_winbind_netbios_name(void);
+DATA_BLOB get_challenge(void) ;
+NTSTATUS contact_winbind_auth_crap(const char *username,
+ const char *domain,
+ const char *workstation,
+ const DATA_BLOB *challenge,
+ const DATA_BLOB *lm_response,
+ const DATA_BLOB *nt_response,
+ uint32_t flags,
+ uint32_t extra_logon_parameters,
+ uint8_t lm_key[8],
+ uint8_t user_session_key[16],
+ uint8_t *pauthoritative,
+ char **error_string,
+ char **unix_name);
+
+/* The following definitions come from utils/ntlm_auth_diagnostics.c */
+
+bool diagnose_ntlm_auth(bool lanman_support_expected);
+int get_pam_winbind_config(void);
+
+#endif /* _NTLM_AUTH_PROTO_H_ */
diff --git a/source3/utils/passwd_proto.h b/source3/utils/passwd_proto.h
new file mode 100644
index 0000000..4099899
--- /dev/null
+++ b/source3/utils/passwd_proto.h
@@ -0,0 +1,31 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * collected prototypes header
+ *
+ * frozen from "make proto" in May 2008
+ *
+ * Copyright (C) Michael Adam 2008
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _PASSWD_PROTO_H_
+#define _PASSWD_PROTO_H_
+
+
+/* The following definitions come from utils/passwd_util.c */
+
+char *get_pass( const char *prompt, bool stdin_get);
+
+#endif /* _PASSWD_PROTO_H_ */
diff --git a/source3/utils/passwd_util.c b/source3/utils/passwd_util.c
new file mode 100644
index 0000000..edd2c52
--- /dev/null
+++ b/source3/utils/passwd_util.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb editing frontend
+
+ Copyright (C) Jeremy Allison 1998
+ Copyright (C) Andrew Tridgell 1998
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Simo Sorce 2000
+ Copyright (C) Martin Pool 2001
+ Copyright (C) Gerald Carter 2002
+ Copyright (C) Andrew Bartlett 2003
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "passwd_proto.h"
+
+/*************************************************************
+ Utility function to prompt for passwords from stdin. Each
+ password entered must end with a newline.
+*************************************************************/
+static char *stdin_new_passwd( void)
+{
+ static fstring new_pw;
+ size_t len;
+
+ ZERO_ARRAY(new_pw);
+
+ /*
+ * if no error is reported from fgets() and string at least contains
+ * the newline that ends the password, then replace the newline with
+ * a null terminator.
+ */
+ if ( fgets(new_pw, sizeof(new_pw), stdin) == NULL) {
+ return NULL;
+ }
+ if ((len = strlen(new_pw)) > 0) {
+ if(new_pw[len-1] == '\n')
+ new_pw[len - 1] = 0;
+ }
+ return(new_pw);
+}
+
+/*************************************************************
+ Utility function to get passwords via tty or stdin
+ Used if the '-s' (smbpasswd) or '-t' (pdbedit) option is set
+ to silently get passwords to enable scripting.
+*************************************************************/
+char *get_pass( const char *prompt, bool stdin_get)
+{
+ char pwd[256] = {0};
+ char *p;
+ int rc;
+
+ if (stdin_get) {
+ p = stdin_new_passwd();
+ if (p == NULL) {
+ return NULL;
+ }
+ } else {
+ rc = samba_getpass(prompt, pwd, sizeof(pwd), false, false);
+ if (rc < 0) {
+ return NULL;
+ }
+ p = pwd;
+ }
+ return smb_xstrdup( p);
+}
diff --git a/source3/utils/pdbedit.c b/source3/utils/pdbedit.c
new file mode 100644
index 0000000..eb6b982
--- /dev/null
+++ b/source3/utils/pdbedit.c
@@ -0,0 +1,1414 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb editing frontend
+
+ Copyright (C) Simo Sorce 2000-2009
+ Copyright (C) Andrew Bartlett 2001
+ Copyright (C) Jelmer Vernooij 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "../librpc/gen_ndr/samr.h"
+#include "../libcli/security/security.h"
+#include "passdb.h"
+#include "cmdline_contexts.h"
+#include "passwd_proto.h"
+#include "lib/util/smb_strtox.h"
+#include "lib/param/param.h"
+
+#define BIT_BACKEND 0x00000004
+#define BIT_VERBOSE 0x00000008
+#define BIT_SPSTYLE 0x00000010
+#define BIT_CAN_CHANGE 0x00000020
+#define BIT_MUST_CHANGE 0x00000040
+#define BIT_USERSIDS 0x00000080
+#define BIT_FULLNAME 0x00000100
+#define BIT_HOMEDIR 0x00000200
+#define BIT_HDIRDRIVE 0x00000400
+#define BIT_LOGSCRIPT 0x00000800
+#define BIT_PROFILE 0x00001000
+#define BIT_MACHINE 0x00002000
+#define BIT_USERDOMAIN 0x00004000
+#define BIT_USER 0x00008000
+#define BIT_LIST 0x00010000
+#define BIT_MODIFY 0x00020000
+#define BIT_CREATE 0x00040000
+#define BIT_DELETE 0x00080000
+#define BIT_ACCPOLICY 0x00100000
+#define BIT_ACCPOLVAL 0x00200000
+#define BIT_ACCTCTRL 0x00400000
+#define BIT_RESERV_7 0x00800000
+#define BIT_IMPORT 0x01000000
+#define BIT_EXPORT 0x02000000
+#define BIT_FIX_INIT 0x04000000
+#define BIT_BADPWRESET 0x08000000
+#define BIT_LOGONHOURS 0x10000000
+#define BIT_KICKOFFTIME 0x20000000
+#define BIT_DESCRIPTION 0x40000000
+#define BIT_PWSETNTHASH 0x80000000
+
+#define MASK_ALWAYS_GOOD 0x0000001F
+#define MASK_USER_GOOD 0xE0405FE0
+
+static int get_sid_from_cli_string(struct dom_sid *sid, const char *str_sid)
+{
+ uint32_t rid;
+
+ if (!string_to_sid(sid, str_sid)) {
+ /* not a complete sid, may be a RID,
+ * try building a SID */
+
+ if (sscanf(str_sid, "%u", &rid) != 1) {
+ fprintf(stderr, "Error passed string is not "
+ "a complete SID or RID!\n");
+ return -1;
+ }
+ sid_compose(sid, get_global_sam_sid(), rid);
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add all currently available users to another db
+ ********************************************************/
+
+static int export_database (struct pdb_methods *in,
+ struct pdb_methods *out,
+ const char *username)
+{
+ NTSTATUS status;
+ struct pdb_search *u_search;
+ struct samr_displayentry userentry;
+
+ DEBUG(3, ("export_database: username=\"%s\"\n", username ? username : "(NULL)"));
+
+ u_search = pdb_search_init(talloc_tos(), PDB_USER_SEARCH);
+ if (u_search == NULL) {
+ DEBUG(0, ("pdb_search_init failed\n"));
+ return 1;
+ }
+
+ if (!in->search_users(in, u_search, 0)) {
+ DEBUG(0, ("Could not start searching users\n"));
+ TALLOC_FREE(u_search);
+ return 1;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+ struct samu *user;
+ struct samu *account;
+ struct dom_sid user_sid;
+
+ DEBUG(4, ("Processing account %s\n", userentry.account_name));
+
+ if ((username != NULL)
+ && (strcmp(username, userentry.account_name) != 0)) {
+ /*
+ * ignore unwanted users
+ */
+ continue;
+ }
+
+ user = samu_new(talloc_tos());
+ if (user == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ break;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), userentry.rid);
+
+ status = in->getsampwsid(in, user, &user_sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("getsampwsid failed: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(user);
+ continue;
+ }
+
+ account = samu_new(NULL);
+ if (account == NULL) {
+ fprintf(stderr, "export_database: Memory allocation "
+ "failure!\n");
+ TALLOC_FREE( user );
+ TALLOC_FREE(u_search);
+ return 1;
+ }
+
+ printf("Importing account for %s...", user->username);
+ status = out->getsampwnam(out, account, user->username);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = out->update_sam_account( out, user );
+ } else {
+ status = out->add_sam_account(out, user);
+ }
+
+ if ( NT_STATUS_IS_OK(status) ) {
+ printf( "ok\n");
+ } else {
+ printf( "failed\n");
+ }
+
+ TALLOC_FREE( account );
+ TALLOC_FREE( user );
+ }
+
+ TALLOC_FREE(u_search);
+
+ return 0;
+}
+
+/*********************************************************
+ Add all currently available group mappings to another db
+ ********************************************************/
+
+static int export_groups (struct pdb_methods *in, struct pdb_methods *out)
+{
+ GROUP_MAP **maps = NULL;
+ size_t i, entries = 0;
+ NTSTATUS status;
+
+ status = in->enum_group_mapping(in, get_global_sam_sid(),
+ SID_NAME_DOM_GRP, &maps, &entries, False);
+
+ if ( NT_STATUS_IS_ERR(status) ) {
+ fprintf(stderr, "Unable to enumerate group map entries.\n");
+ return 1;
+ }
+
+ for (i=0; i<entries; i++) {
+ out->add_group_mapping_entry(out, maps[i]);
+ }
+
+ TALLOC_FREE(maps);
+
+ return 0;
+}
+
+/*********************************************************
+ Reset account policies to their default values and remove marker
+ ********************************************************/
+
+static int reinit_account_policies (void)
+{
+ int i;
+
+ for (i=1; decode_account_policy_name(i) != NULL; i++) {
+ uint32_t policy_value;
+ if (!account_policy_get_default(i, &policy_value)) {
+ fprintf(stderr, "Can't get default account policy\n");
+ return -1;
+ }
+ if (!account_policy_set(i, policy_value)) {
+ fprintf(stderr, "Can't set account policy in tdb\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*********************************************************
+ Add all currently available account policy from tdb to one backend
+ ********************************************************/
+
+static int export_account_policies (struct pdb_methods *in, struct pdb_methods *out)
+{
+ int i;
+
+ for ( i=1; decode_account_policy_name(i) != NULL; i++ ) {
+ uint32_t policy_value;
+ NTSTATUS status;
+
+ status = in->get_account_policy(in, i, &policy_value);
+
+ if ( NT_STATUS_IS_ERR(status) ) {
+ fprintf(stderr, "Unable to get account policy from %s\n", in->name);
+ return -1;
+ }
+
+ status = out->set_account_policy(out, i, policy_value);
+
+ if ( NT_STATUS_IS_ERR(status) ) {
+ fprintf(stderr, "Unable to migrate account policy to %s\n", out->name);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*********************************************************
+ Print info from sam structure
+**********************************************************/
+
+static int print_sam_info (struct samu *sam_pwent, bool verbosity, bool smbpwdstyle)
+{
+ uid_t uid;
+ time_t tmp;
+
+ /* TODO: check if entry is a user or a workstation */
+ if (!sam_pwent) return -1;
+
+ if (verbosity) {
+ char temp[44];
+ const uint8_t *hours;
+ struct dom_sid_buf buf;
+
+ printf ("Unix username: %s\n", pdb_get_username(sam_pwent));
+ printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent));
+ printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN));
+ printf ("User SID: %s\n",
+ dom_sid_str_buf(pdb_get_user_sid(sam_pwent), &buf));
+ printf ("Primary Group SID: %s\n",
+ dom_sid_str_buf(pdb_get_group_sid(sam_pwent), &buf));
+ printf ("Full Name: %s\n", pdb_get_fullname(sam_pwent));
+ printf ("Home Directory: %s\n", pdb_get_homedir(sam_pwent));
+ printf ("HomeDir Drive: %s\n", pdb_get_dir_drive(sam_pwent));
+ printf ("Logon Script: %s\n", pdb_get_logon_script(sam_pwent));
+ printf ("Profile Path: %s\n", pdb_get_profile_path(sam_pwent));
+ printf ("Domain: %s\n", pdb_get_domain(sam_pwent));
+ printf ("Account desc: %s\n", pdb_get_acct_desc(sam_pwent));
+ printf ("Workstations: %s\n", pdb_get_workstations(sam_pwent));
+ printf ("Munged dial: %s\n", pdb_get_munged_dial(sam_pwent));
+
+ tmp = pdb_get_logon_time(sam_pwent);
+ printf ("Logon time: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_logoff_time(sam_pwent);
+ printf ("Logoff time: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_kickoff_time(sam_pwent);
+ printf ("Kickoff time: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_pass_last_set_time(sam_pwent);
+ printf ("Password last set: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_pass_can_change_time(sam_pwent);
+ printf ("Password can change: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_pass_must_change_time(sam_pwent);
+ printf ("Password must change: %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+
+ tmp = pdb_get_bad_password_time(sam_pwent);
+ printf ("Last bad password : %s\n",
+ tmp ? http_timestring(talloc_tos(), tmp) : "0");
+ printf ("Bad password count : %d\n",
+ pdb_get_bad_password_count(sam_pwent));
+
+ hours = pdb_get_hours(sam_pwent);
+ pdb_sethexhours(temp, hours);
+ printf ("Logon hours : %s\n", temp);
+ if (smbpwdstyle){
+ pdb_sethexpwd(temp, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+ printf ("LM hash : %s\n", temp);
+ pdb_sethexpwd(temp, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+ printf ("NT hash : %s\n", temp);
+ }
+
+ } else if (smbpwdstyle) {
+ char lm_passwd[33];
+ char nt_passwd[33];
+
+ uid = nametouid(pdb_get_username(sam_pwent));
+ pdb_sethexpwd(lm_passwd, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+ pdb_sethexpwd(nt_passwd, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent));
+
+ printf("%s:%lu:%s:%s:%s:LCT-%08X:\n",
+ pdb_get_username(sam_pwent),
+ (unsigned long)uid,
+ lm_passwd,
+ nt_passwd,
+ pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN),
+ (uint32_t)convert_time_t_to_uint32_t(pdb_get_pass_last_set_time(sam_pwent)));
+ } else {
+ uid = nametouid(pdb_get_username(sam_pwent));
+ printf ("%s:%lu:%s\n", pdb_get_username(sam_pwent), (unsigned long)uid,
+ pdb_get_fullname(sam_pwent));
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Get an Print User Info
+**********************************************************/
+
+static int print_user_info(const char *username,
+ bool verbosity, bool smbpwdstyle)
+{
+ struct samu *sam_pwent = NULL;
+ bool bret;
+ int ret;
+
+ sam_pwent = samu_new(NULL);
+ if (!sam_pwent) {
+ return -1;
+ }
+
+ bret = pdb_getsampwnam(sam_pwent, username);
+ if (!bret) {
+ fprintf (stderr, "Username not found!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ ret = print_sam_info(sam_pwent, verbosity, smbpwdstyle);
+
+ TALLOC_FREE(sam_pwent);
+ return ret;
+}
+
+/*********************************************************
+ List Users
+**********************************************************/
+static int print_users_list(bool verbosity, bool smbpwdstyle)
+{
+ struct pdb_search *u_search;
+ struct samr_displayentry userentry;
+ struct samu *sam_pwent;
+ TALLOC_CTX *tosctx;
+ struct dom_sid user_sid;
+ bool bret;
+ int ret;
+
+ tosctx = talloc_tos();
+ if (!tosctx) {
+ DEBUG(0, ("talloc failed\n"));
+ return 1;
+ }
+
+ u_search = pdb_search_users(tosctx, 0);
+ if (!u_search) {
+ DEBUG(0, ("User Search failed!\n"));
+ ret = 1;
+ goto done;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+
+ sam_pwent = samu_new(tosctx);
+ if (sam_pwent == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ ret = 1;
+ goto done;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), userentry.rid);
+
+ bret = pdb_getsampwsid(sam_pwent, &user_sid);
+ if (!bret) {
+ DEBUG(2, ("getsampwsid failed\n"));
+ TALLOC_FREE(sam_pwent);
+ continue;
+ }
+
+ if (verbosity) {
+ printf ("---------------\n");
+ }
+ print_sam_info(sam_pwent, verbosity, smbpwdstyle);
+ TALLOC_FREE(sam_pwent);
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(tosctx);
+ return ret;
+}
+
+/*********************************************************
+ Fix a list of Users for uninitialised passwords
+**********************************************************/
+static int fix_users_list(void)
+{
+ struct pdb_search *u_search;
+ struct samr_displayentry userentry;
+ struct samu *sam_pwent;
+ TALLOC_CTX *tosctx;
+ struct dom_sid user_sid;
+ NTSTATUS status;
+ bool bret;
+ int ret;
+
+ tosctx = talloc_tos();
+ if (!tosctx) {
+ fprintf(stderr, "Out of memory!\n");
+ return 1;
+ }
+
+ u_search = pdb_search_users(tosctx, 0);
+ if (!u_search) {
+ fprintf(stderr, "User Search failed!\n");
+ ret = 1;
+ goto done;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+
+ sam_pwent = samu_new(tosctx);
+ if (sam_pwent == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ ret = 1;
+ goto done;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), userentry.rid);
+
+ bret = pdb_getsampwsid(sam_pwent, &user_sid);
+ if (!bret) {
+ DEBUG(2, ("getsampwsid failed\n"));
+ TALLOC_FREE(sam_pwent);
+ continue;
+ }
+
+ status = pdb_update_sam_account(sam_pwent);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Update of user %s failed!\n",
+ pdb_get_username(sam_pwent));
+ }
+ TALLOC_FREE(sam_pwent);
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(tosctx);
+ return ret;
+}
+
+/*********************************************************
+ Set User Info
+**********************************************************/
+
+static int set_user_info(const char *username, const char *fullname,
+ const char *homedir, const char *acct_desc,
+ const char *drive, const char *script,
+ const char *profile, const char *account_control,
+ const char *user_sid, const char *user_domain,
+ const bool badpw, const bool hours,
+ const char *kickoff_time, const char *str_hex_pwd)
+{
+ bool updated_autolock = False, updated_badpw = False;
+ struct samu *sam_pwent;
+ uint8_t hours_array[MAX_HOURS_LEN];
+ uint32_t hours_len;
+ uint32_t acb_flags;
+ uint32_t not_settable;
+ uint32_t new_flags;
+ struct dom_sid u_sid;
+ bool ret;
+
+ sam_pwent = samu_new(NULL);
+ if (!sam_pwent) {
+ return 1;
+ }
+
+ ret = pdb_getsampwnam(sam_pwent, username);
+ if (!ret) {
+ fprintf (stderr, "Username not found!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ if (hours) {
+ hours_len = pdb_get_hours_len(sam_pwent);
+ memset(hours_array, 0xff, hours_len);
+
+ pdb_set_hours(sam_pwent, hours_array, hours_len, PDB_CHANGED);
+ }
+
+ if (!pdb_update_autolock_flag(sam_pwent, &updated_autolock)) {
+ DEBUG(2,("pdb_update_autolock_flag failed.\n"));
+ }
+
+ if (!pdb_update_bad_password_count(sam_pwent, &updated_badpw)) {
+ DEBUG(2,("pdb_update_bad_password_count failed.\n"));
+ }
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (acct_desc)
+ pdb_set_acct_desc(sam_pwent, acct_desc, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED);
+ if (user_domain)
+ pdb_set_domain(sam_pwent, user_domain, PDB_CHANGED);
+
+ if (account_control) {
+ not_settable = ~(ACB_DISABLED | ACB_HOMDIRREQ |
+ ACB_PWNOTREQ | ACB_PWNOEXP | ACB_AUTOLOCK);
+
+ new_flags = pdb_decode_acct_ctrl(account_control);
+
+ if (new_flags & not_settable) {
+ fprintf(stderr, "Can only set [NDHLX] flags\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ acb_flags = pdb_get_acct_ctrl(sam_pwent);
+
+ pdb_set_acct_ctrl(sam_pwent,
+ (acb_flags & not_settable) | new_flags,
+ PDB_CHANGED);
+ }
+ if (user_sid) {
+ if (get_sid_from_cli_string(&u_sid, user_sid)) {
+ fprintf(stderr, "Failed to parse SID\n");
+ return -1;
+ }
+ pdb_set_user_sid(sam_pwent, &u_sid, PDB_CHANGED);
+ }
+
+ if (badpw) {
+ pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED);
+ pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED);
+ }
+
+ if (kickoff_time) {
+ time_t value = get_time_t_max();
+
+ if (strcmp(kickoff_time, "never") != 0) {
+ int error = 0;
+ uint32_t num;
+
+ num = smb_strtoul(kickoff_time,
+ NULL,
+ 10,
+ &error,
+ SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ fprintf(stderr, "Failed to parse kickoff time\n");
+ return -1;
+ }
+
+ value = convert_uint32_t_to_time_t(num);
+ }
+
+ pdb_set_kickoff_time(sam_pwent, value, PDB_CHANGED);
+ }
+ if (str_hex_pwd) {
+ unsigned char new_nt_p16[NT_HASH_LEN];
+ if(strlen(str_hex_pwd) != (NT_HASH_LEN *2)){
+ fprintf(stderr, "Invalid hash\n");
+ return -1;
+ }
+
+ pdb_gethexpwd(str_hex_pwd, new_nt_p16);
+
+ if (!pdb_set_nt_passwd (sam_pwent, new_nt_p16 , PDB_CHANGED)) {
+ fprintf(stderr, "Failed to set password from nt-hash\n");
+ return -1;
+ }
+
+ if (!pdb_set_pass_last_set_time (sam_pwent, time(NULL), PDB_CHANGED)){
+ fprintf(stderr, "Failed to set last password set time\n");
+ return -1;
+ }
+ if (!pdb_update_history(sam_pwent, new_nt_p16)){
+ fprintf(stderr, "Failed to update password history\n");
+ return -1;
+ }
+ }
+
+ if (NT_STATUS_IS_OK(pdb_update_sam_account(sam_pwent))) {
+
+ print_user_info(username, True, (str_hex_pwd != NULL ));
+ } else {
+ fprintf (stderr, "Unable to modify entry!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+ TALLOC_FREE(sam_pwent);
+ return 0;
+}
+
+static int set_machine_info(const char *machinename,
+ const char *account_control,
+ const char *machine_sid)
+{
+ struct samu *sam_pwent = NULL;
+ TALLOC_CTX *tosctx;
+ uint32_t acb_flags;
+ uint32_t not_settable;
+ uint32_t new_flags;
+ struct dom_sid m_sid;
+ char *name;
+ int len;
+ bool ret;
+
+ len = strlen(machinename);
+ if (len == 0) {
+ fprintf(stderr, "No machine name given\n");
+ return -1;
+ }
+
+ tosctx = talloc_tos();
+ if (!tosctx) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ sam_pwent = samu_new(tosctx);
+ if (!sam_pwent) {
+ return 1;
+ }
+
+ if (machinename[len-1] == '$') {
+ name = talloc_strdup(sam_pwent, machinename);
+ } else {
+ name = talloc_asprintf(sam_pwent, "%s$", machinename);
+ }
+ if (!name) {
+ fprintf(stderr, "Out of memory!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ if (!strlower_m(name)) {
+ fprintf(stderr, "strlower_m %s failed\n", name);
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ ret = pdb_getsampwnam(sam_pwent, name);
+ if (!ret) {
+ fprintf (stderr, "Username not found!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ if (account_control) {
+ not_settable = ~(ACB_DISABLED);
+
+ new_flags = pdb_decode_acct_ctrl(account_control);
+
+ if (new_flags & not_settable) {
+ fprintf(stderr, "Can only set [D] flags\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ acb_flags = pdb_get_acct_ctrl(sam_pwent);
+
+ pdb_set_acct_ctrl(sam_pwent,
+ (acb_flags & not_settable) | new_flags,
+ PDB_CHANGED);
+ }
+ if (machine_sid) {
+ if (get_sid_from_cli_string(&m_sid, machine_sid)) {
+ fprintf(stderr, "Failed to parse SID\n");
+ return -1;
+ }
+ pdb_set_user_sid(sam_pwent, &m_sid, PDB_CHANGED);
+ }
+
+ if (NT_STATUS_IS_OK(pdb_update_sam_account(sam_pwent))) {
+ print_user_info(name, True, False);
+ } else {
+ fprintf (stderr, "Unable to modify entry!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+ TALLOC_FREE(sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New User
+**********************************************************/
+static int new_user(const char *username, const char *fullname,
+ const char *homedir, const char *drive,
+ const char *script, const char *profile,
+ char *user_sid, bool stdin_get)
+{
+ char *pwd1 = NULL, *pwd2 = NULL;
+ char *err = NULL, *msg = NULL;
+ struct samu *sam_pwent = NULL;
+ TALLOC_CTX *tosctx;
+ NTSTATUS status;
+ struct dom_sid u_sid;
+ int flags;
+ int ret = -1;
+
+ tosctx = talloc_tos();
+ if (!tosctx) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (user_sid) {
+ if (get_sid_from_cli_string(&u_sid, user_sid)) {
+ fprintf(stderr, "Failed to parse SID\n");
+ return -1;
+ }
+ }
+
+ pwd1 = get_pass( "new password:", stdin_get);
+ if (pwd1 == NULL) {
+ fprintf(stderr, "Failed to read passwords.\n");
+ goto done;
+ }
+ pwd2 = get_pass( "retype new password:", stdin_get);
+ if (pwd2 == NULL) {
+ fprintf(stderr, "Failed to read passwords.\n");
+ goto done;
+ }
+ ret = strcmp(pwd1, pwd2);
+ if (ret != 0) {
+ fprintf (stderr, "Passwords do not match!\n");
+ goto done;
+ }
+
+ flags = LOCAL_ADD_USER | LOCAL_SET_PASSWORD;
+
+ status = local_password_change(username, flags, pwd1, &err, &msg);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (err) fprintf(stderr, "%s", err);
+ ret = -1;
+ goto done;
+ }
+
+ sam_pwent = samu_new(tosctx);
+ if (!sam_pwent) {
+ fprintf(stderr, "Out of memory!\n");
+ ret = -1;
+ goto done;
+ }
+
+ if (!pdb_getsampwnam(sam_pwent, username)) {
+ fprintf(stderr, "User %s not found!\n", username);
+ ret = -1;
+ goto done;
+ }
+
+ if (fullname)
+ pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED);
+ if (homedir)
+ pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED);
+ if (drive)
+ pdb_set_dir_drive(sam_pwent, drive, PDB_CHANGED);
+ if (script)
+ pdb_set_logon_script(sam_pwent, script, PDB_CHANGED);
+ if (profile)
+ pdb_set_profile_path(sam_pwent, profile, PDB_CHANGED);
+ if (user_sid)
+ pdb_set_user_sid(sam_pwent, &u_sid, PDB_CHANGED);
+
+ status = pdb_update_sam_account(sam_pwent);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "Failed to modify entry for user %s.!\n",
+ username);
+ ret = -1;
+ goto done;
+ }
+
+ print_user_info(username, True, False);
+ ret = 0;
+
+done:
+ if (pwd1) memset(pwd1, 0, strlen(pwd1));
+ if (pwd2) memset(pwd2, 0, strlen(pwd2));
+ SAFE_FREE(pwd1);
+ SAFE_FREE(pwd2);
+ SAFE_FREE(err);
+ SAFE_FREE(msg);
+ TALLOC_FREE(sam_pwent);
+ return ret;
+}
+
+/*********************************************************
+ Add New Machine
+**********************************************************/
+
+static int new_machine(const char *machinename, char *machine_sid)
+{
+ char *err = NULL, *msg = NULL;
+ struct samu *sam_pwent = NULL;
+ TALLOC_CTX *tosctx;
+ NTSTATUS status;
+ struct dom_sid m_sid;
+ char *compatpwd;
+ char *name;
+ int flags;
+ int len;
+ int ret;
+
+ len = strlen(machinename);
+ if (len == 0) {
+ fprintf(stderr, "No machine name given\n");
+ return -1;
+ }
+
+ tosctx = talloc_tos();
+ if (!tosctx) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (machine_sid) {
+ if (get_sid_from_cli_string(&m_sid, machine_sid)) {
+ fprintf(stderr, "Failed to parse SID\n");
+ return -1;
+ }
+ }
+
+ compatpwd = talloc_strdup(tosctx, machinename);
+ if (!compatpwd) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (machinename[len-1] == '$') {
+ name = talloc_strdup(tosctx, machinename);
+ compatpwd[len-1] = '\0';
+ } else {
+ name = talloc_asprintf(tosctx, "%s$", machinename);
+ }
+ if (!name) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (!strlower_m(name)) {
+ fprintf(stderr, "strlower_m %s failed\n", name);
+ return -1;
+ }
+
+ flags = LOCAL_ADD_USER | LOCAL_TRUST_ACCOUNT | LOCAL_SET_PASSWORD;
+
+ status = local_password_change(name, flags, compatpwd, &err, &msg);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (err) fprintf(stderr, "%s", err);
+ ret = -1;
+ }
+
+ sam_pwent = samu_new(tosctx);
+ if (!sam_pwent) {
+ fprintf(stderr, "Out of memory!\n");
+ ret = -1;
+ goto done;
+ }
+
+ if (!pdb_getsampwnam(sam_pwent, name)) {
+ fprintf(stderr, "Machine %s not found!\n", name);
+ ret = -1;
+ goto done;
+ }
+
+ if (machine_sid)
+ pdb_set_user_sid(sam_pwent, &m_sid, PDB_CHANGED);
+
+ status = pdb_update_sam_account(sam_pwent);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,
+ "Failed to modify entry for %s.!\n", name);
+ ret = -1;
+ goto done;
+ }
+
+ print_user_info(name, True, False);
+ ret = 0;
+
+done:
+ SAFE_FREE(err);
+ SAFE_FREE(msg);
+ TALLOC_FREE(sam_pwent);
+ return ret;
+}
+
+/*********************************************************
+ Delete user entry
+**********************************************************/
+
+static int delete_user_entry(const char *username)
+{
+ struct samu *samaccount;
+
+ samaccount = samu_new(NULL);
+ if (!samaccount) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (!pdb_getsampwnam(samaccount, username)) {
+ fprintf (stderr,
+ "user %s does not exist in the passdb\n", username);
+ TALLOC_FREE(samaccount);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_delete_sam_account(samaccount))) {
+ fprintf (stderr, "Unable to delete user %s\n", username);
+ TALLOC_FREE(samaccount);
+ return -1;
+ }
+
+ TALLOC_FREE(samaccount);
+ return 0;
+}
+
+/*********************************************************
+ Delete machine entry
+**********************************************************/
+
+static int delete_machine_entry(const char *machinename)
+{
+ struct samu *samaccount = NULL;
+ const char *name;
+
+ if (strlen(machinename) == 0) {
+ fprintf(stderr, "No machine name given\n");
+ return -1;
+ }
+
+ samaccount = samu_new(NULL);
+ if (!samaccount) {
+ fprintf(stderr, "Out of memory!\n");
+ return -1;
+ }
+
+ if (machinename[strlen(machinename)-1] != '$') {
+ name = talloc_asprintf(samaccount, "%s$", machinename);
+ } else {
+ name = machinename;
+ }
+
+ if (!pdb_getsampwnam(samaccount, name)) {
+ fprintf (stderr,
+ "machine %s does not exist in the passdb\n", name);
+ TALLOC_FREE(samaccount);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_delete_sam_account(samaccount))) {
+ fprintf (stderr, "Unable to delete machine %s\n", name);
+ TALLOC_FREE(samaccount);
+ return -1;
+ }
+
+ TALLOC_FREE(samaccount);
+ return 0;
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+
+int main(int argc, const char **argv)
+{
+ static int list_users = False;
+ static int verbose = False;
+ static int spstyle = False;
+ static int machine = False;
+ static int add_user = False;
+ static int delete_user = False;
+ static int modify_user = False;
+ uint32_t setparms, checkparms;
+ int opt;
+ static char *full_name = NULL;
+ static char *acct_desc = NULL;
+ static const char *user_name = NULL;
+ static char *home_dir = NULL;
+ static char *home_drive = NULL;
+ static const char *backend = NULL;
+ static char *backend_in = NULL;
+ static char *backend_out = NULL;
+ static int transfer_groups = False;
+ static int transfer_account_policies = False;
+ static int reset_account_policies = False;
+ static int force_initialised_password = False;
+ static char *logon_script = NULL;
+ static char *profile_path = NULL;
+ static char *user_domain = NULL;
+ static char *account_control = NULL;
+ static char *account_policy = NULL;
+ static char *user_sid = NULL;
+ static char *machine_sid = NULL;
+ static long int account_policy_value = 0;
+ bool account_policy_value_set = False;
+ static int badpw_reset = False;
+ static int hours_reset = False;
+ static char *pwd_time_format = NULL;
+ static int pw_from_stdin = False;
+ struct pdb_methods *bin, *bout;
+ static char *kickoff_time = NULL;
+ static char *str_hex_pwd = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+ NTSTATUS status;
+ poptContext pc;
+ bool ok;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"list", 'L', POPT_ARG_NONE, &list_users, 0, "list all users", NULL},
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL },
+ {"smbpasswd-style", 'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL},
+ {"user", 'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" },
+ {"account-desc", 'N', POPT_ARG_STRING, &acct_desc, 0, "set account description", NULL},
+ {"fullname", 'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL},
+ {"homedir", 'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL},
+ {"drive", 'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL},
+ {"script", 'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL},
+ {"profile", 'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL},
+ {"domain", 'I', POPT_ARG_STRING, &user_domain, 0, "set a users' domain", NULL},
+ {"user SID", 'U', POPT_ARG_STRING, &user_sid, 0, "set user SID or RID", NULL},
+ {"machine SID", 'M', POPT_ARG_STRING, &machine_sid, 0, "set machine SID or RID", NULL},
+ {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL},
+ {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL},
+ {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL},
+ {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL},
+ {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL},
+ {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL},
+ {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL},
+ {"group", 'g', POPT_ARG_NONE, &transfer_groups, 0, "use -i and -e for groups", NULL},
+ {"policies", 'y', POPT_ARG_NONE, &transfer_account_policies, 0, "use -i and -e to move account policies between backends", NULL},
+ {"policies-reset", 0, POPT_ARG_NONE, &reset_account_policies, 0, "restore default policies", NULL},
+ {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL},
+ {"value", 'C', POPT_ARG_LONG, &account_policy_value, 'C',"set the account policy to this value", NULL},
+ {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL},
+ {"force-initialized-passwords", 0, POPT_ARG_NONE, &force_initialised_password, 0, "Force initialization of corrupt password strings in a passdb backend", NULL},
+ {"bad-password-count-reset", 'z', POPT_ARG_NONE, &badpw_reset, 0, "reset bad password count", NULL},
+ {"logon-hours-reset", 'Z', POPT_ARG_NONE, &hours_reset, 0, "reset logon hours", NULL},
+ {"time-format", 0, POPT_ARG_STRING, &pwd_time_format, 0, "The time format for time parameters", NULL },
+ {"password-from-stdin", 't', POPT_ARG_NONE, &pw_from_stdin, 0, "get password from standard in", NULL},
+ {"kickoff-time", 'K', POPT_ARG_STRING, &kickoff_time, 0, "set the kickoff time", NULL},
+ {"set-nt-hash", 0, POPT_ARG_STRING, &str_hex_pwd, 0, "set password from nt-hash", NULL},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ bin = bout = NULL;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'C':
+ account_policy_value_set = True;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ if (user_name == NULL) {
+ if (poptPeekArg(pc)) {
+ user_name = talloc_strdup(frame, poptGetArg(pc));
+ if (user_name == NULL) {
+ fprintf(stderr, "out of memory\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ }
+ }
+
+ setparms = (backend ? BIT_BACKEND : 0) +
+ (verbose ? BIT_VERBOSE : 0) +
+ (spstyle ? BIT_SPSTYLE : 0) +
+ (full_name ? BIT_FULLNAME : 0) +
+ (home_dir ? BIT_HOMEDIR : 0) +
+ (home_drive ? BIT_HDIRDRIVE : 0) +
+ (logon_script ? BIT_LOGSCRIPT : 0) +
+ (profile_path ? BIT_PROFILE : 0) +
+ (user_domain ? BIT_USERDOMAIN : 0) +
+ (machine ? BIT_MACHINE : 0) +
+ (user_name ? BIT_USER : 0) +
+ (list_users ? BIT_LIST : 0) +
+ (force_initialised_password ? BIT_FIX_INIT : 0) +
+ (user_sid ? BIT_USERSIDS : 0) +
+ (machine_sid ? BIT_USERSIDS : 0) +
+ (modify_user ? BIT_MODIFY : 0) +
+ (add_user ? BIT_CREATE : 0) +
+ (delete_user ? BIT_DELETE : 0) +
+ (account_control ? BIT_ACCTCTRL : 0) +
+ (account_policy ? BIT_ACCPOLICY : 0) +
+ (account_policy_value_set ? BIT_ACCPOLVAL : 0) +
+ (backend_in ? BIT_IMPORT : 0) +
+ (backend_out ? BIT_EXPORT : 0) +
+ (badpw_reset ? BIT_BADPWRESET : 0) +
+ (hours_reset ? BIT_LOGONHOURS : 0) +
+ (kickoff_time ? BIT_KICKOFFTIME : 0) +
+ (str_hex_pwd ? BIT_PWSETNTHASH : 0 ) +
+ (acct_desc ? BIT_DESCRIPTION : 0);
+
+
+ if (setparms & BIT_BACKEND) {
+ /* HACK: set the global passdb backend by overwriting globals.
+ * This way we can use regular pdb functions for default
+ * operations that do not involve passdb migrations */
+ lpcfg_set_cmdline(lp_ctx, "passdb backend", backend);
+ } else {
+ backend = lp_passdb_backend();
+ }
+
+ if (!initialize_password_db(False, NULL)) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ exit(1);
+ }
+
+ /* the lowest bit options are always accepted */
+ checkparms = setparms & ~MASK_ALWAYS_GOOD;
+
+ if (checkparms & BIT_FIX_INIT) {
+ poptFreeContext(pc);
+ return fix_users_list();
+ }
+
+ /* account policy operations */
+ if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) {
+ uint32_t value;
+ enum pdb_policy_type field = account_policy_name_to_typenum(account_policy);
+ if (field == 0) {
+ const char **names;
+ int count;
+ int i;
+ account_policy_names_list(talloc_tos(), &names, &count);
+ fprintf(stderr, "No account policy by that name!\n");
+ if (count !=0) {
+ fprintf(stderr, "Account policy names are:\n");
+ for (i = 0; i < count ; i++) {
+ d_fprintf(stderr, "%s\n", names[i]);
+ }
+ }
+ TALLOC_FREE(names);
+ exit(1);
+ }
+ if (!pdb_get_account_policy(field, &value)) {
+ fprintf(stderr, "valid account policy, but unable to fetch value!\n");
+ if (!account_policy_value_set)
+ exit(1);
+ }
+ printf("account policy \"%s\" description: %s\n", account_policy, account_policy_get_desc(field));
+ if (account_policy_value_set) {
+ printf("account policy \"%s\" value was: %u\n", account_policy, value);
+ if (!pdb_set_account_policy(field, account_policy_value)) {
+ fprintf(stderr, "valid account policy, but unable to set value!\n");
+ exit(1);
+ }
+ printf("account policy \"%s\" value is now: %lu\n", account_policy, account_policy_value);
+ exit(0);
+ } else {
+ printf("account policy \"%s\" value is: %u\n", account_policy, value);
+ exit(0);
+ }
+ }
+
+ if (reset_account_policies) {
+ if (reinit_account_policies()) {
+ exit(1);
+ }
+
+ exit(0);
+ }
+
+ /* import and export operations */
+
+ if (((checkparms & BIT_IMPORT) ||
+ (checkparms & BIT_EXPORT)) &&
+ !(checkparms & ~(BIT_IMPORT +BIT_EXPORT +BIT_USER))) {
+
+ poptFreeContext(pc);
+
+ if (backend_in) {
+ status = make_pdb_method_name(&bin, backend_in);
+ } else {
+ status = make_pdb_method_name(&bin, backend);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "Unable to initialize %s.\n",
+ backend_in ? backend_in : backend);
+ return 1;
+ }
+
+ if (backend_out) {
+ status = make_pdb_method_name(&bout, backend_out);
+ } else {
+ status = make_pdb_method_name(&bout, backend);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "Unable to initialize %s.\n",
+ backend_out ? backend_out : backend);
+ return 1;
+ }
+
+ if (transfer_account_policies) {
+
+ if (!(checkparms & BIT_USER)) {
+ return export_account_policies(bin, bout);
+ }
+
+ } else if (transfer_groups) {
+
+ if (!(checkparms & BIT_USER)) {
+ return export_groups(bin, bout);
+ }
+
+ } else {
+ return export_database(bin, bout,
+ (checkparms & BIT_USER) ? user_name : NULL);
+ }
+ }
+
+ /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */
+ /* fake up BIT_LIST if only BIT_USER is defined */
+ if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) {
+ checkparms += BIT_LIST;
+ }
+
+ /* modify flag is optional to maintain backwards compatibility */
+ /* fake up BIT_MODIFY if BIT_USER and at least one of MASK_USER_GOOD is defined */
+ if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) {
+ checkparms += BIT_MODIFY;
+ }
+
+ /* list users operations */
+ if (checkparms & BIT_LIST) {
+ if (!(checkparms & ~BIT_LIST)) {
+ poptFreeContext(pc);
+ return print_users_list(verbose, spstyle);
+ }
+ if (!(checkparms & ~(BIT_USER + BIT_LIST))) {
+ poptFreeContext(pc);
+ return print_user_info(user_name, verbose, spstyle);
+ }
+ }
+
+ /* mask out users options */
+ checkparms &= ~MASK_USER_GOOD;
+
+ /* if bad password count is reset, we must be modifying */
+ if (checkparms & BIT_BADPWRESET) {
+ checkparms |= BIT_MODIFY;
+ checkparms &= ~BIT_BADPWRESET;
+ }
+
+ /* if logon hours is reset, must modify */
+ if (checkparms & BIT_LOGONHOURS) {
+ checkparms |= BIT_MODIFY;
+ checkparms &= ~BIT_LOGONHOURS;
+ }
+
+ /* account operation */
+ if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) {
+ /* check use of -u option */
+ if (!(checkparms & BIT_USER)) {
+ fprintf (stderr, "Username not specified! (use -u option)\n");
+ poptFreeContext(pc);
+ return -1;
+ }
+
+ /* account creation operations */
+ if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE))) {
+ poptFreeContext(pc);
+ if (checkparms & BIT_MACHINE) {
+ return new_machine(user_name, machine_sid);
+ } else {
+ return new_user(user_name, full_name,
+ home_dir, home_drive,
+ logon_script, profile_path,
+ user_sid, pw_from_stdin);
+ }
+ }
+
+ /* account deletion operations */
+ if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) {
+ poptFreeContext(pc);
+ if (checkparms & BIT_MACHINE) {
+ return delete_machine_entry(user_name);
+ } else {
+ return delete_user_entry(user_name);
+ }
+ }
+
+ /* account modification operations */
+ if (!(checkparms & ~(BIT_MODIFY + BIT_USER + BIT_MACHINE))) {
+ poptFreeContext(pc);
+ if (checkparms & BIT_MACHINE) {
+ return set_machine_info(user_name,
+ account_control,
+ machine_sid);
+ } else {
+ return set_user_info(user_name, full_name,
+ home_dir, acct_desc,
+ home_drive, logon_script,
+ profile_path, account_control,
+ user_sid, user_domain,
+ badpw_reset, hours_reset,
+ kickoff_time, str_hex_pwd);
+ }
+ }
+ }
+
+ if (setparms >= 0x20) {
+ fprintf (stderr, "Incompatible or insufficient options on command line!\n");
+ }
+ poptPrintHelp(pc, stderr, 0);
+
+ gfree_all();
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return 1;
+}
diff --git a/source3/utils/profiles.c b/source3/utils/profiles.c
new file mode 100644
index 0000000..ab1eb26
--- /dev/null
+++ b/source3/utils/profiles.c
@@ -0,0 +1,365 @@
+/*
+ Samba Unix/Linux SMB client utility profiles.c
+
+ Copyright (C) Richard Sharpe, <rsharpe@richardsharpe.com> 2002
+ Copyright (C) Jelmer Vernooij (conversion to popt) 2003
+ Copyright (C) Gerald (Jerry) Carter 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "registry/reg_objects.h"
+#include "registry/regfio.h"
+#include "../libcli/security/security.h"
+
+/* GLOBAL VARIABLES */
+
+struct dom_sid old_sid, new_sid;
+int change = 0, new_val = 0;
+int opt_verbose = False;
+
+/********************************************************************
+********************************************************************/
+
+static void verbose_output(const char *format, ...) PRINTF_ATTRIBUTE(1,2);
+static void verbose_output(const char *format, ...)
+{
+ va_list args;
+ char *var = NULL;
+
+ if (!opt_verbose) {
+ return;
+ }
+
+ va_start(args, format);
+ if ((vasprintf(&var, format, args)) == -1) {
+ va_end(args);
+ return;
+ }
+
+ fprintf(stdout, "%s", var);
+ va_end(args);
+ SAFE_FREE(var);
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool swap_sid_in_acl( struct security_descriptor *sd, struct dom_sid *s1, struct dom_sid *s2 )
+{
+ struct security_acl *theacl;
+ int i;
+ bool update = False;
+ struct dom_sid_buf buf;
+
+ verbose_output(" Owner SID: %s\n",
+ dom_sid_str_buf(sd->owner_sid, &buf));
+ if ( dom_sid_equal( sd->owner_sid, s1 ) ) {
+ sid_copy( sd->owner_sid, s2 );
+ update = True;
+ verbose_output(" New Owner SID: %s\n",
+ dom_sid_str_buf(sd->owner_sid, &buf));
+
+ }
+
+ verbose_output(" Group SID: %s\n",
+ dom_sid_str_buf(sd->group_sid, &buf));
+ if ( dom_sid_equal( sd->group_sid, s1 ) ) {
+ sid_copy( sd->group_sid, s2 );
+ update = True;
+ verbose_output(" New Group SID: %s\n",
+ dom_sid_str_buf(sd->group_sid, &buf));
+ }
+
+ theacl = sd->dacl;
+ verbose_output(" DACL: %d entries:\n", theacl->num_aces);
+ for ( i=0; i<theacl->num_aces; i++ ) {
+ verbose_output(" Trustee SID: %s\n",
+ dom_sid_str_buf(&theacl->aces[i].trustee,
+ &buf));
+ if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) {
+ sid_copy( &theacl->aces[i].trustee, s2 );
+ update = True;
+ verbose_output(
+ " New Trustee SID: %s\n",
+ dom_sid_str_buf(&theacl->aces[i].trustee,
+ &buf));
+ }
+ }
+
+#if 0
+ theacl = sd->sacl;
+ verbose_output(" SACL: %d entries: \n", theacl->num_aces);
+ for ( i=0; i<theacl->num_aces; i++ ) {
+ verbose_output(" Trustee SID: %s\n",
+ dom_sid_str_buf(&theacl->aces[i].trustee,
+ &buf));
+ if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) {
+ sid_copy( &theacl->aces[i].trustee, s2 );
+ update = True;
+ verbose_output(
+ " New Trustee SID: %s\n",
+ dom_sid_str_buf(&theacl->aces[i].trustee,
+ &buf));
+ }
+ }
+#endif
+ return update;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool copy_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk,
+ REGF_NK_REC *parent, REGF_FILE *outfile,
+ const char *parentpath )
+{
+ REGF_NK_REC *key, *subkey;
+ struct security_descriptor *new_sd;
+ struct regval_ctr *values;
+ struct regsubkey_ctr *subkeys;
+ int i;
+ char *path;
+ WERROR werr;
+
+ /* swap out the SIDs in the security descriptor */
+
+ if (nk->sec_desc->sec_desc == NULL) {
+ fprintf(stderr, "Invalid (NULL) security descriptor!\n");
+ return false;
+ }
+
+ new_sd = security_descriptor_copy(outfile->mem_ctx,
+ nk->sec_desc->sec_desc);
+ if (new_sd == NULL) {
+ fprintf(stderr, "Failed to copy security descriptor!\n");
+ return False;
+ }
+
+ verbose_output("ACL for %s%s%s\n", parentpath, parent ? "\\" : "", nk->keyname);
+ swap_sid_in_acl( new_sd, &old_sid, &new_sid );
+
+ werr = regsubkey_ctr_init(NULL, &subkeys);
+ if (!W_ERROR_IS_OK(werr)) {
+ DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+ return False;
+ }
+
+ werr = regval_ctr_init(subkeys, &values);
+ if (!W_ERROR_IS_OK(werr)) {
+ TALLOC_FREE( subkeys );
+ DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+ return False;
+ }
+
+ /* copy values into the struct regval_ctr */
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type,
+ nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) );
+ }
+
+ /* copy subkeys into the struct regsubkey_ctr */
+
+ while ( (subkey = regfio_fetch_subkey( infile, nk )) ) {
+ regsubkey_ctr_addkey( subkeys, subkey->keyname );
+ }
+
+ key = regfio_write_key( outfile, nk->keyname, values, subkeys, new_sd, parent );
+
+ /* write each one of the subkeys out */
+
+ path = talloc_asprintf(subkeys, "%s%s%s",
+ parentpath, parent ? "\\" : "",nk->keyname);
+ if (!path) {
+ TALLOC_FREE( subkeys );
+ return false;
+ }
+
+ nk->subkey_index = 0;
+ while ((subkey = regfio_fetch_subkey(infile, nk))) {
+ if (!copy_registry_tree( infile, subkey, key, outfile, path)) {
+ TALLOC_FREE(subkeys);
+ return false;
+ }
+ }
+
+
+ verbose_output("[%s]\n", path);
+
+ /* values is a talloc()'d child of subkeys here so just throw it all away */
+ TALLOC_FREE(subkeys);
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+int main( int argc, const char *argv[] )
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ int opt;
+ REGF_FILE *infile, *outfile;
+ REGF_NK_REC *nk;
+ char *orig_filename, *new_filename;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "change-sid",
+ .shortName = 'c',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'c',
+ .descrip = "Provides SID to change",
+ },
+ {
+ .longName = "new-sid",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'n',
+ .descrip = "Provides SID to change to",
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt_verbose,
+ .val = 'v',
+ .descrip = "Verbose output",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ poptContext pc;
+ bool ok;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "<profilefile>");
+
+ /* Now, process the arguments */
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'c':
+ change = 1;
+ if (!string_to_sid(&old_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(254);
+ }
+ break;
+
+ case 'n':
+ new_val = 1;
+ if (!string_to_sid(&new_sid, poptGetOptArg(pc))) {
+ fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(253);
+ }
+ break;
+
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptGetArg(pc);
+
+ if (!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if ((!change && new_val) || (change && !new_val)) {
+ fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n");
+ poptPrintUsage(pc, stderr, 0);
+ exit(252);
+ }
+
+ orig_filename = talloc_strdup(frame, poptPeekArg(pc));
+ if (!orig_filename) {
+ exit(ENOMEM);
+ }
+ new_filename = talloc_asprintf(frame,
+ "%s.new",
+ orig_filename);
+ if (!new_filename) {
+ exit(ENOMEM);
+ }
+
+ if (!(infile = regfio_open( orig_filename, O_RDONLY, 0))) {
+ fprintf( stderr, "Failed to open %s!\n", orig_filename );
+ fprintf( stderr, "Error was (%s)\n", strerror(errno) );
+ exit (1);
+ }
+
+ if ( !(outfile = regfio_open( new_filename, (O_RDWR|O_CREAT|O_TRUNC),
+ (S_IRUSR|S_IWUSR) )) ) {
+ fprintf( stderr, "Failed to open new file %s!\n", new_filename );
+ fprintf( stderr, "Error was (%s)\n", strerror(errno) );
+ exit (1);
+ }
+
+ /* actually do the update now */
+
+ if ((nk = regfio_rootkey( infile )) == NULL) {
+ fprintf(stderr, "Could not get rootkey\n");
+ exit(3);
+ }
+
+ if (!copy_registry_tree( infile, nk, NULL, outfile, "")) {
+ fprintf(stderr, "Failed to write updated registry file!\n");
+ exit(2);
+ }
+
+ /* cleanup */
+
+ regfio_close(infile);
+ regfio_close(outfile);
+
+ poptFreeContext(pc);
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/py_net.c b/source3/utils/py_net.c
new file mode 100644
index 0000000..5e81b8f
--- /dev/null
+++ b/source3/utils/py_net.c
@@ -0,0 +1,369 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba python bindings to s3 libnet library
+
+ Copyright (C) David Mulder <dmulder@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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "lib/replace/system/python.h"
+#include "includes.h"
+#include <pytalloc.h>
+#include "python/modules.h"
+#include "python/py3compat.h"
+#include "rpc_client/rpc_client.h"
+#include <sys/socket.h>
+#include "net.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/pycredentials.h"
+#include "lib/cmdline_contexts.h"
+#include "param/loadparm.h"
+#include "param/s3_param.h"
+#include "param/pyparam.h"
+#include "py_net.h"
+#include "librpc/gen_ndr/libnet_join.h"
+#include "libnet/libnet_join.h"
+#include "libcli/security/dom_sid.h"
+#include "dynconfig/dynconfig.h"
+
+static WERROR check_ads_config(struct loadparm_context *lp_ctx)
+{
+ if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) {
+ d_printf(_("Host is not configured as a member server.\n"));
+ return WERR_INVALID_DOMAIN_ROLE;
+ }
+
+ if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) {
+ d_printf(_("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n"), lpcfg_netbios_name(lp_ctx),
+ (unsigned int)strlen(lpcfg_netbios_name(lp_ctx)));
+ return WERR_INVALID_COMPUTERNAME;
+ }
+
+ if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) {
+ d_fprintf(stderr, _("realm must be set in %s for ADS "
+ "join to succeed.\n"), get_dyn_CONFIGFILE());
+ return WERR_INVALID_PARAMETER;
+ }
+
+ return WERR_OK;
+}
+
+static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ struct libnet_JoinCtx *r = NULL;
+ struct net_context *c;
+ WERROR werr;
+ PyObject *result;
+ TALLOC_CTX *mem_ctx;
+ int no_dns_updates = false, debug = false;
+ bool modify_config = lp_config_backend_is_registry();
+ const char *kwnames[] = { "dnshostname", "createupn", "createcomputer",
+ "osName", "osVer", "osServicePack",
+ "machinepass", "debug", "noDnsUpdates", NULL };
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+ c = talloc_zero(mem_ctx, struct net_context);
+ c->msg_ctx = mem_ctx;
+
+ werr = libnet_init_JoinCtx(mem_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssszpp:Join",
+ discard_const_p(char *, kwnames),
+ &r->in.dnshostname,
+ &r->in.upn,
+ &r->in.account_ou,
+ &r->in.os_name,
+ &r->in.os_version,
+ &r->in.os_servicepack,
+ &r->in.machine_password,
+ &debug,
+ &no_dns_updates)) {
+ talloc_free(mem_ctx);
+ PyErr_FromString(_("Invalid arguments\n"));
+ return NULL;
+ }
+
+ if (!modify_config) {
+ werr = check_ads_config(self->lp_ctx);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR_and_string(werr,
+ _("Invalid configuration. Exiting....\n"));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ }
+
+ r->in.domain_name = lpcfg_realm(self->lp_ctx);
+ r->in.domain_name_type = JoinDomNameTypeDNS;
+ r->in.create_upn = r->in.upn != NULL ? true : false;
+ r->in.dc_name = self->server_address;
+ r->in.admin_account = cli_credentials_get_username(self->creds);
+ r->in.admin_password = cli_credentials_get_password(self->creds);
+ r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds);
+ r->in.modify_config = modify_config;
+ r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE |
+ WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED;
+ r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
+ r->in.debug = debug;
+ c->opt_user_name = r->in.admin_account;
+ c->opt_password = r->in.admin_password;
+ c->opt_kerberos = r->in.use_kerberos;
+
+ werr = libnet_Join(mem_ctx, r);
+ if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) {
+ r->in.domain_name = lpcfg_workgroup(self->lp_ctx);
+ r->in.domain_name_type = JoinDomNameTypeNBT;
+ werr = libnet_Join(mem_ctx, r);
+ }
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR_and_string(werr,
+ r->out.error_string
+ ? r->out.error_string
+ : get_friendly_werror_msg(werr));
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ /*
+ * Check the short name of the domain
+ */
+
+ if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) {
+ d_printf(_("The workgroup in %s does not match the short\n"
+ "domain name obtained from the server.\n"
+ "Using the name [%s] from the server.\n"
+ "You should set \"workgroup = %s\" in %s.\n"),
+ get_dyn_CONFIGFILE(), r->out.netbios_domain_name,
+ r->out.netbios_domain_name, get_dyn_CONFIGFILE());
+ }
+
+ /*
+ * We try doing the dns update (if it was compiled in
+ * and if it was not disabled on the command line).
+ * If the dns update fails, we still consider the join
+ * operation as succeeded if we came this far.
+ */
+ if (!no_dns_updates) {
+ net_ads_join_dns_updates(c, mem_ctx, r);
+ }
+
+ result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid),
+ r->out.dns_domain_name);
+
+ talloc_free(mem_ctx);
+
+ return result;
+}
+
+static const char py_net_join_member_doc[] = "join_member(dnshostname, createupn, createcomputer, osName, osVer, osServicePack, machinepass) -> (domain_sid, domain_name)\n\n" \
+"Join the domain with the specified name.";
+
+static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs)
+{
+ struct libnet_UnjoinCtx *r = NULL;
+ WERROR werr;
+ TALLOC_CTX *mem_ctx;
+ int keep_account = false, debug = false;
+ const char *kwnames[] = { "keepAccount", "debug", NULL };
+
+ mem_ctx = talloc_new(self->mem_ctx);
+ if (mem_ctx == NULL) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+ if (!*lpcfg_realm(self->lp_ctx)) {
+ PyErr_FromString(_("No realm set, are we joined ?\n"));
+ return NULL;
+ }
+
+ werr = libnet_init_UnjoinCtx(mem_ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR_and_string(werr,
+ _("Could not initialise unjoin context.\n"));
+ return NULL;
+ }
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave",
+ discard_const_p(char *, kwnames),
+ &keep_account, &debug)) {
+ talloc_free(mem_ctx);
+ PyErr_FromString(_("Invalid arguments\n"));
+ return NULL;
+ }
+
+ r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds);
+ r->in.dc_name = self->server_address;
+ r->in.domain_name = lpcfg_realm(self->lp_ctx);
+ r->in.admin_account = cli_credentials_get_username(self->creds);
+ r->in.admin_password = cli_credentials_get_password(self->creds);
+ r->in.modify_config = lp_config_backend_is_registry();
+ r->in.debug = debug;
+
+ /*
+ * Try to delete it, but if that fails, disable it. The
+ * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable"
+ */
+ r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+ if (keep_account) {
+ r->in.delete_machine_account = false;
+ } else {
+ r->in.delete_machine_account = true;
+ }
+
+ r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
+
+ werr = libnet_Unjoin(mem_ctx, r);
+ if (!W_ERROR_IS_OK(werr)) {
+ PyErr_SetWERROR_and_string(werr,
+ r->out.error_string
+ ? r->out.error_string
+ : get_friendly_werror_msg(werr));
+ Py_RETURN_FALSE;
+ }
+
+ if (r->out.deleted_machine_account) {
+ d_printf(_("Deleted account for '%s' in realm '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+ Py_RETURN_TRUE;
+ }
+
+ if (r->out.disabled_machine_account) {
+ d_printf(_("Disabled account for '%s' in realm '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+ werr = WERR_OK;
+ Py_RETURN_TRUE;
+ }
+
+ /*
+ * Based on what we requested, we shouldn't get here, but if
+ * we did, it means the secrets were removed, and therefore
+ * we have left the domain.
+ */
+ d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"),
+ r->in.machine_name, r->out.dns_domain_name);
+ Py_RETURN_TRUE;
+}
+
+static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \
+"Leave the joined domain.";
+
+static PyMethodDef net_obj_methods[] = {
+ {
+ .ml_name = "join_member",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_join_member),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_join_member_doc
+ },
+ {
+ .ml_name = "leave",
+ .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction,
+ py_net_leave),
+ .ml_flags = METH_VARARGS|METH_KEYWORDS,
+ .ml_doc = py_net_leave_doc
+ },
+ { .ml_name = NULL }
+};
+
+static void py_net_dealloc(py_net_Object *self)
+{
+ talloc_free(self->mem_ctx);
+ PyObject_Del(self);
+}
+
+static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ PyObject *py_creds, *py_lp = Py_None;
+ const char *kwnames[] = { "creds", "lp", "server", NULL };
+ py_net_Object *ret;
+ const char *server_address = NULL;
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz",
+ discard_const_p(char *, kwnames), &py_creds, &py_lp,
+ &server_address)) {
+ PyErr_FromString(_("Invalid arguments\n"));
+ return NULL;
+ }
+
+ ret = PyObject_New(py_net_Object, type);
+ if (ret == NULL) {
+ return NULL;
+ }
+
+ ret->ev = samba_tevent_context_init(NULL);
+ ret->mem_ctx = talloc_stackframe();
+
+ ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp);
+ if (ret->lp_ctx == NULL) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ ret->server_address = server_address;
+
+ ret->creds = cli_credentials_from_py_object(py_creds);
+ if (ret->creds == NULL) {
+ PyErr_SetString(PyExc_TypeError, "Expected credentials object");
+ Py_DECREF(ret);
+ return NULL;
+ }
+
+ return (PyObject *)ret;
+}
+
+
+PyTypeObject py_net_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "net_s3.Net",
+ .tp_basicsize = sizeof(py_net_Object),
+ .tp_dealloc = (destructor)py_net_dealloc,
+ .tp_methods = net_obj_methods,
+ .tp_new = net_obj_new,
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "net",
+ .m_size = -1,
+};
+
+MODULE_INIT_FUNC(net_s3)
+{
+ PyObject *m;
+
+ if (PyType_Ready(&py_net_Type) < 0)
+ return NULL;
+
+ m = PyModule_Create(&moduledef);
+ if (m == NULL)
+ return NULL;
+
+ Py_INCREF(&py_net_Type);
+ PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type);
+
+ return m;
+}
diff --git a/source3/utils/py_net.h b/source3/utils/py_net.h
new file mode 100644
index 0000000..9ace71b
--- /dev/null
+++ b/source3/utils/py_net.h
@@ -0,0 +1,26 @@
+/*
+ Unix SMB/CIFS implementation.
+ Samba python bindings to s3 libnet library
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+typedef struct {
+ PyObject_HEAD
+ TALLOC_CTX *mem_ctx;
+ struct cli_credentials *creds;
+ struct loadparm_context *lp_ctx;
+ const char *server_address;
+ struct tevent_context *ev;
+} py_net_Object;
diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c
new file mode 100644
index 0000000..3259076
--- /dev/null
+++ b/source3/utils/regedit.c
@@ -0,0 +1,835 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/param/param.h"
+#include "lib/util/data_blob.h"
+#include "lib/registry/registry.h"
+#include "regedit.h"
+#include "regedit_treeview.h"
+#include "regedit_valuelist.h"
+#include "regedit_dialog.h"
+#include "regedit_list.h"
+#include <ncurses.h>
+#include <menu.h>
+#include <panel.h>
+
+#define KEY_START_X 0
+#define KEY_START_Y 1
+#define KEY_WIDTH (COLS / 4)
+#define KEY_HEIGHT (LINES - KEY_START_Y - 2)
+#define VAL_START_X KEY_WIDTH
+#define VAL_START_Y 1
+#define VAL_WIDTH (COLS - KEY_WIDTH)
+#define VAL_HEIGHT (LINES - VAL_START_Y - 2)
+
+#define HELP1_START_Y (LINES - 2)
+#define HELP1_START_X 0
+#define HELP1_WIDTH (LINES)
+#define HELP2_START_Y (LINES - 1)
+#define HELP2_START_X 0
+#define HELP2_WIDTH (LINES)
+#define PATH_START_Y 0
+#define PATH_START_X 6
+#define PATH_MAX_Y (COLS - 1)
+#define PATH_WIDTH (COLS - 6)
+#define PATH_WIDTH_MAX 1024
+
+struct regedit {
+ struct registry_context *registry_context;
+ WINDOW *main_window;
+ WINDOW *path_label;
+ size_t path_len;
+ struct value_list *vl;
+ struct tree_view *keys;
+ bool tree_input;
+ struct regedit_search_opts active_search;
+};
+
+static struct regedit *regedit_main = NULL;
+
+static void show_path(struct regedit *regedit)
+{
+ int start_pad = 0;
+ int start_win = PATH_START_X;
+
+ if (PATH_START_X + regedit->path_len > COLS) {
+ start_pad = 3 + PATH_START_X + regedit->path_len - COLS;
+ mvprintw(PATH_START_Y, start_win, "...");
+ start_win += 3;
+ }
+ copywin(regedit->path_label, regedit->main_window, 0, start_pad,
+ PATH_START_Y, start_win, PATH_START_Y, PATH_MAX_Y, false);
+
+ mvchgat(0, 0, COLS, A_BOLD, PAIR_YELLOW_CYAN, NULL);
+}
+
+static void print_path(struct regedit *regedit, struct tree_node *node)
+{
+ regedit->path_len = tree_node_print_path(regedit->path_label, node);
+ show_path(regedit);
+}
+
+static void print_help(struct regedit *regedit)
+{
+ const char *khelp = "[n] New Key [s] New Subkey [d] Del Key "
+ "[LEFT] Ascend [RIGHT] Descend";
+ const char *vhelp = "[n] New Value [d] Del Value [ENTER] Edit "
+ "[b] Edit binary";
+ const char *msg = "KEYS";
+ const char *help = khelp;
+ const char *genhelp = "[TAB] Switch sections [q] Quit "
+ "[UP] List up [DOWN] List down "
+ "[/] Search [x] Next";
+ int i, pad;
+
+ if (!regedit->tree_input) {
+ msg = "VALUES";
+ help = vhelp;
+ }
+
+ move(HELP1_START_Y, HELP1_START_X);
+ clrtoeol();
+ attron(COLOR_PAIR(PAIR_BLACK_CYAN));
+ mvaddstr(HELP1_START_Y, HELP1_START_X, help);
+ pad = COLS - strlen(msg) - strlen(help);
+ for (i = 0; i < pad; ++i) {
+ addch(' ');
+ }
+ attroff(COLOR_PAIR(PAIR_BLACK_CYAN));
+ attron(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD);
+ addstr(msg);
+ attroff(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD);
+
+ move(HELP2_START_Y, HELP2_START_X);
+ clrtoeol();
+ mvaddstr(HELP2_START_Y, HELP2_START_X, genhelp);
+}
+
+static void print_heading(struct regedit *regedit)
+{
+ if (regedit->tree_input) {
+ tree_view_set_selected(regedit->keys, true);
+ value_list_set_selected(regedit->vl, false);
+ } else {
+ tree_view_set_selected(regedit->keys, false);
+ value_list_set_selected(regedit->vl, true);
+ }
+
+ print_help(regedit);
+}
+
+static void load_values(struct regedit *regedit)
+{
+ struct tree_node *node;
+
+ node = tree_view_get_current_node(regedit->keys);
+ value_list_load(regedit->vl, node->key);
+}
+
+static void add_reg_key(struct regedit *regedit, struct tree_node *node,
+ bool subkey)
+{
+ const char *name;
+ const char *msg;
+
+ if (!subkey && tree_node_is_top_level(node)) {
+ return;
+ }
+
+ msg = "Enter name of new key";
+ if (subkey) {
+ msg = "Enter name of new subkey";
+ }
+ dialog_input(regedit, &name, "New Key", "%s", msg);
+ if (name) {
+ WERROR rv;
+ struct registry_key *new_key;
+ struct tree_node *new_node = NULL;
+ struct tree_node *list;
+ struct tree_node *parent;
+
+ if (subkey) {
+ parent = node;
+ list = node->child_head;
+ } else {
+ parent = node->parent;
+ list = tree_node_first(node);
+ SMB_ASSERT(list != NULL);
+ }
+ rv = reg_key_add_name(regedit, parent->key, name,
+ NULL, NULL, &new_key);
+ if (W_ERROR_IS_OK(rv)) {
+ /* The list of subkeys may not be present in
+ cache yet, so if not, don't bother allocating
+ a new node for the key. */
+ if (list) {
+ new_node = tree_node_new(parent, parent,
+ name, new_key);
+ SMB_ASSERT(new_node);
+ tree_node_insert_sorted(list, new_node);
+ } else {
+ /* Reopen the parent key to make sure the
+ new subkey will be noticed. */
+ tree_node_reopen_key(regedit->registry_context,
+ parent);
+ }
+
+ list = tree_node_first(node);
+ tree_view_clear(regedit->keys);
+ tree_view_update(regedit->keys, list);
+ if (!subkey) {
+ node = new_node;
+ }
+ tree_view_set_current_node(regedit->keys, node);
+ load_values(regedit);
+ } else {
+ msg = get_friendly_werror_msg(rv);
+ dialog_notice(regedit, DIA_ALERT, "New Key",
+ "Failed to create key: %s", msg);
+ }
+ talloc_free(discard_const(name));
+ }
+}
+
+enum search_flags {
+ SEARCH_NEXT = (1<<0),
+ SEARCH_PREV = (1<<1),
+ SEARCH_REPEAT = (1<<2)
+};
+static WERROR regedit_search(struct regedit *regedit, struct tree_node *node,
+ struct value_item *vitem, unsigned flags)
+{
+ struct regedit_search_opts *opts;
+ struct tree_node *found;
+ struct value_item *found_value;
+ bool search_key, need_sync;
+ char *save_value_name;
+ WERROR rv;
+ bool (*iterate)(struct tree_node **, bool, WERROR *);
+ struct value_item *(*find_item)(struct value_list *,
+ struct value_item *,
+ const char *,
+ regedit_search_match_fn_t);
+
+ opts = &regedit->active_search;
+
+ if (!opts->query || !opts->match) {
+ return WERR_OK;
+ }
+
+ SMB_ASSERT(opts->search_key || opts->search_value);
+
+ rv = WERR_OK;
+ found = NULL;
+ found_value = NULL;
+ save_value_name = NULL;
+ search_key = opts->search_key;
+ need_sync = false;
+ iterate = tree_node_next;
+ find_item = value_list_find_next_item;
+
+ if (flags & SEARCH_PREV) {
+ iterate = tree_node_prev;
+ find_item = value_list_find_prev_item;
+ }
+
+ if (opts->search_value) {
+ struct value_item *it;
+
+ it = value_list_get_current_item(regedit->vl);
+ if (it) {
+ save_value_name = talloc_strdup(regedit,
+ it->value_name);
+ if (save_value_name == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ if (vitem) {
+ search_key = false;
+ }
+ }
+
+ if (!vitem && (flags & SEARCH_REPEAT)) {
+ if (opts->search_value) {
+ search_key = false;
+ } else if (!iterate(&node, opts->search_recursive, &rv)) {
+ beep();
+ return rv;
+ }
+ }
+
+ do {
+ if (search_key) {
+ SMB_ASSERT(opts->search_key == true);
+ if (opts->match(node->name, opts->query)) {
+ found = node;
+ } else if (opts->search_value) {
+ search_key = false;
+ }
+ }
+ if (!search_key) {
+ SMB_ASSERT(opts->search_value == true);
+ if (!vitem) {
+ rv = value_list_load_quick(regedit->vl,
+ node->key);
+ if (!W_ERROR_IS_OK(rv)) {
+ goto out;
+ }
+ need_sync = true;
+ }
+ found_value = find_item(regedit->vl, vitem, opts->query,
+ opts->match);
+ if (found_value) {
+ found = node;
+ } else {
+ vitem = NULL;
+ search_key = opts->search_key;
+ }
+ }
+ } while (!found && iterate(&node, opts->search_recursive, &rv));
+
+ if (!W_ERROR_IS_OK(rv)) {
+ goto out;
+ }
+
+ if (found) {
+ /* Put the cursor on the node that was found */
+ if (!tree_view_is_node_visible(regedit->keys, found)) {
+ tree_view_update(regedit->keys,
+ tree_node_first(found));
+ print_path(regedit, found);
+ }
+ tree_view_set_current_node(regedit->keys, found);
+ if (found_value) {
+ if (need_sync) {
+ value_list_sync(regedit->vl);
+ }
+ value_list_set_current_item(regedit->vl, found_value);
+ regedit->tree_input = false;
+ } else {
+ load_values(regedit);
+ regedit->tree_input = true;
+ }
+ tree_view_show(regedit->keys);
+ value_list_show(regedit->vl);
+ print_heading(regedit);
+ } else {
+ if (need_sync) {
+ load_values(regedit);
+ value_list_set_current_item_by_name(regedit->vl,
+ save_value_name);
+ }
+ beep();
+ }
+
+out:
+ talloc_free(save_value_name);
+
+ return rv;
+}
+
+static void regedit_search_repeat(struct regedit *regedit, unsigned flags)
+{
+ struct tree_node *node;
+ struct value_item *vitem;
+ struct regedit_search_opts *opts;
+
+ opts = &regedit->active_search;
+ if (opts->query == NULL) {
+ return;
+ }
+
+ node = tree_view_get_current_node(regedit->keys);
+ vitem = NULL;
+ if (opts->search_value && !regedit->tree_input) {
+ vitem = value_list_get_current_item(regedit->vl);
+ }
+ regedit_search(regedit, node, vitem, flags | SEARCH_REPEAT);
+}
+
+static void handle_tree_input(struct regedit *regedit, int c)
+{
+ struct tree_node *node;
+
+ switch (c) {
+ case KEY_DOWN:
+ tree_view_driver(regedit->keys, ML_CURSOR_DOWN);
+ load_values(regedit);
+ break;
+ case KEY_UP:
+ tree_view_driver(regedit->keys, ML_CURSOR_UP);
+ load_values(regedit);
+ break;
+ case KEY_NPAGE:
+ tree_view_driver(regedit->keys, ML_CURSOR_PGDN);
+ load_values(regedit);
+ break;
+ case KEY_PPAGE:
+ tree_view_driver(regedit->keys, ML_CURSOR_PGUP);
+ load_values(regedit);
+ break;
+ case KEY_HOME:
+ tree_view_driver(regedit->keys, ML_CURSOR_HOME);
+ load_values(regedit);
+ break;
+ case KEY_END:
+ tree_view_driver(regedit->keys, ML_CURSOR_END);
+ load_values(regedit);
+ break;
+ case '\n':
+ case KEY_ENTER:
+ case KEY_RIGHT:
+ node = tree_view_get_current_node(regedit->keys);
+ if (node && tree_node_has_children(node)) {
+ WERROR rv;
+
+ rv = tree_node_load_children(node);
+ if (W_ERROR_IS_OK(rv)) {
+ print_path(regedit, node->child_head);
+ tree_view_update(regedit->keys, node->child_head);
+ value_list_load(regedit->vl, node->child_head->key);
+ } else {
+ const char *msg = get_friendly_werror_msg(rv);
+ dialog_notice(regedit, DIA_ALERT, "Loading Subkeys",
+ "Failed to load subkeys: %s", msg);
+ }
+ }
+ break;
+ case KEY_LEFT:
+ node = tree_view_get_current_node(regedit->keys);
+ if (node && !tree_node_is_top_level(node)) {
+ print_path(regedit, node->parent);
+ node = node->parent;
+ tree_view_update(regedit->keys, tree_node_first(node));
+ tree_view_set_current_node(regedit->keys, node);
+ value_list_load(regedit->vl, node->key);
+ }
+ break;
+ case 'n':
+ case 'N':
+ node = tree_view_get_current_node(regedit->keys);
+ add_reg_key(regedit, node, false);
+ break;
+ case 's':
+ case 'S':
+ node = tree_view_get_current_node(regedit->keys);
+ add_reg_key(regedit, node, true);
+ break;
+ case 'd':
+ case 'D': {
+ int sel;
+
+ node = tree_view_get_current_node(regedit->keys);
+ if (tree_node_is_top_level(node)) {
+ break;
+ }
+ sel = dialog_notice(regedit, DIA_CONFIRM,
+ "Delete Key",
+ "Really delete key \"%s\"?",
+ node->name);
+ if (sel == DIALOG_OK) {
+ WERROR rv;
+ struct tree_node *pop;
+ struct tree_node *parent = node->parent;
+
+ rv = reg_key_del(node, parent->key, node->name);
+ if (W_ERROR_IS_OK(rv)) {
+ tree_node_reopen_key(regedit->registry_context,
+ parent);
+ tree_view_clear(regedit->keys);
+ pop = tree_node_pop(&node);
+ talloc_free(pop);
+ node = parent->child_head;
+ if (node == NULL) {
+ node = tree_node_first(parent);
+ print_path(regedit, node);
+ }
+ tree_view_update(regedit->keys, node);
+ value_list_load(regedit->vl, node->key);
+ } else {
+ const char *msg = get_friendly_werror_msg(rv);
+ dialog_notice(regedit, DIA_ALERT, "Delete Key",
+ "Failed to delete key: %s", msg);
+ }
+ }
+ break;
+ }
+ }
+
+ tree_view_show(regedit->keys);
+ value_list_show(regedit->vl);
+}
+
+static void handle_value_input(struct regedit *regedit, int c)
+{
+ struct value_item *vitem;
+ bool binmode = false;
+ WERROR err;
+ int sel;
+
+ switch (c) {
+ case KEY_DOWN:
+ value_list_driver(regedit->vl, ML_CURSOR_DOWN);
+ break;
+ case KEY_UP:
+ value_list_driver(regedit->vl, ML_CURSOR_UP);
+ break;
+ case KEY_NPAGE:
+ value_list_driver(regedit->vl, ML_CURSOR_PGDN);
+ break;
+ case KEY_PPAGE:
+ value_list_driver(regedit->vl, ML_CURSOR_PGUP);
+ break;
+ case KEY_HOME:
+ value_list_driver(regedit->vl, ML_CURSOR_HOME);
+ break;
+ case KEY_END:
+ value_list_driver(regedit->vl, ML_CURSOR_END);
+ break;
+ case 'b':
+ case 'B':
+ binmode = true;
+
+ FALL_THROUGH;
+ case '\n':
+ case KEY_ENTER:
+ vitem = value_list_get_current_item(regedit->vl);
+ if (vitem) {
+ struct tree_node *node;
+ const char *name = NULL;
+ node = tree_view_get_current_node(regedit->keys);
+ sel = dialog_edit_value(regedit, node->key, vitem->type,
+ vitem, binmode, &err, &name);
+ if (!W_ERROR_IS_OK(err)) {
+ const char *msg = get_friendly_werror_msg(err);
+ dialog_notice(regedit, DIA_ALERT, "Error",
+ "Error editing value:\n%s", msg);
+ } else if (sel == DIALOG_OK) {
+ tree_node_reopen_key(regedit->registry_context,
+ node);
+ value_list_load(regedit->vl, node->key);
+ value_list_set_current_item_by_name(regedit->vl,
+ name);
+ talloc_free(discard_const(name));
+ }
+ }
+ break;
+ case 'n':
+ case 'N': {
+ int new_type;
+
+ sel = dialog_select_type(regedit, &new_type);
+ if (sel == DIALOG_OK) {
+ struct tree_node *node;
+ const char *name = NULL;
+ node = tree_view_get_current_node(regedit->keys);
+ sel = dialog_edit_value(regedit, node->key, new_type,
+ NULL, false, &err, &name);
+ if (!W_ERROR_IS_OK(err)) {
+ const char *msg = get_friendly_werror_msg(err);
+ dialog_notice(regedit, DIA_ALERT, "Error",
+ "Error creating value:\n%s", msg);
+ } else if (sel == DIALOG_OK) {
+ tree_node_reopen_key(regedit->registry_context,
+ node);
+ value_list_load(regedit->vl, node->key);
+ value_list_set_current_item_by_name(regedit->vl,
+ name);
+ talloc_free(discard_const(name));
+ }
+ }
+ break;
+ }
+ case 'd':
+ case 'D':
+ vitem = value_list_get_current_item(regedit->vl);
+ if (vitem) {
+ sel = dialog_notice(regedit, DIA_CONFIRM,
+ "Delete Value",
+ "Really delete value \"%s\"?",
+ vitem->value_name);
+ if (sel == DIALOG_OK) {
+ struct tree_node *node;
+ node = tree_view_get_current_node(regedit->keys);
+ reg_del_value(regedit, node->key,
+ vitem->value_name);
+ tree_node_reopen_key(regedit->registry_context,
+ node);
+ value_list_load(regedit->vl, node->key);
+ }
+ }
+ break;
+ }
+
+ value_list_show(regedit->vl);
+}
+
+static bool find_substring(const char *haystack, const char *needle)
+{
+ return strstr(haystack, needle) != NULL;
+}
+
+static bool find_substring_nocase(const char *haystack, const char *needle)
+{
+ return strcasestr(haystack, needle) != NULL;
+}
+
+static void handle_main_input(struct regedit *regedit, int c)
+{
+ switch (c) {
+ case 18: { /* CTRL-R */
+ struct tree_node *root, *node;
+ const char **path;
+
+ node = tree_view_get_current_node(regedit->keys);
+ path = tree_node_get_path(regedit, node);
+ SMB_ASSERT(path != NULL);
+
+ root = tree_node_new_root(regedit, regedit->registry_context);
+ SMB_ASSERT(root != NULL);
+
+ tree_view_set_root(regedit->keys, root);
+ tree_view_set_path(regedit->keys, path);
+ node = tree_view_get_current_node(regedit->keys);
+ value_list_load(regedit->vl, node->key);
+ tree_view_show(regedit->keys);
+ value_list_show(regedit->vl);
+ print_path(regedit, node);
+ talloc_free(discard_const(path));
+ break;
+ }
+ case 'f':
+ case 'F':
+ case '/': {
+ int rv;
+ struct regedit_search_opts *opts;
+ struct tree_node *node;
+
+ opts = &regedit->active_search;
+ rv = dialog_search_input(regedit, opts);
+ if (rv == DIALOG_OK) {
+ SMB_ASSERT(opts->query != NULL);
+ opts->match = find_substring_nocase;
+ node = regedit->keys->root->child_head;
+ if (opts->search_case) {
+ opts->match = find_substring;
+ }
+ if (!opts->search_recursive) {
+ node = tree_view_get_current_node(regedit->keys);
+ node = tree_node_first(node);
+ }
+ regedit_search(regedit, node, NULL, SEARCH_NEXT);
+ }
+ break;
+ }
+ case 'x':
+ regedit_search_repeat(regedit, SEARCH_NEXT);
+ break;
+ case 'X':
+ regedit_search_repeat(regedit, SEARCH_PREV);
+ break;
+ case '\t':
+ regedit->tree_input = !regedit->tree_input;
+ print_heading(regedit);
+ break;
+ default:
+ if (regedit->tree_input) {
+ handle_tree_input(regedit, c);
+ } else {
+ handle_value_input(regedit, c);
+ }
+ }
+}
+
+int regedit_getch(void)
+{
+ int c;
+
+ SMB_ASSERT(regedit_main);
+
+ c = getch();
+ if (c == KEY_RESIZE) {
+ tree_view_resize(regedit_main->keys, KEY_HEIGHT, KEY_WIDTH,
+ KEY_START_Y, KEY_START_X);
+ value_list_resize(regedit_main->vl, VAL_HEIGHT, VAL_WIDTH,
+ VAL_START_Y, VAL_START_X);
+ print_heading(regedit_main);
+ show_path(regedit_main);
+ }
+
+ return c;
+}
+
+static void regedit_panic_handler(const char *msg)
+{
+ endwin();
+ smb_panic_log(msg);
+ smb_panic_s3(msg);
+}
+
+static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx)
+{
+ struct regedit *regedit;
+ struct tree_node *root;
+ bool colors;
+ int key;
+
+ initscr();
+
+ cbreak();
+ noecho();
+
+ fault_configure(regedit_panic_handler);
+
+ colors = has_colors();
+ if (colors) {
+ start_color();
+ use_default_colors();
+ assume_default_colors(COLOR_WHITE, COLOR_BLUE);
+ init_pair(PAIR_YELLOW_CYAN, COLOR_YELLOW, COLOR_CYAN);
+ init_pair(PAIR_BLACK_CYAN, COLOR_BLACK, COLOR_CYAN);
+ init_pair(PAIR_YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE);
+ }
+
+ regedit = talloc_zero(mem_ctx, struct regedit);
+ SMB_ASSERT(regedit != NULL);
+ regedit_main = regedit;
+
+ regedit->registry_context = ctx;
+ regedit->main_window = stdscr;
+ keypad(regedit->main_window, TRUE);
+
+ mvwprintw(regedit->main_window, 0, 0, "Path: ");
+ regedit->path_label = newpad(1, PATH_WIDTH_MAX);
+ SMB_ASSERT(regedit->path_label);
+ wprintw(regedit->path_label, "/");
+ show_path(regedit_main);
+
+ root = tree_node_new_root(regedit, ctx);
+ SMB_ASSERT(root != NULL);
+
+ regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH,
+ KEY_START_Y, KEY_START_X);
+ SMB_ASSERT(regedit->keys != NULL);
+
+ regedit->vl = value_list_new(regedit, VAL_HEIGHT, VAL_WIDTH,
+ VAL_START_Y, VAL_START_X);
+ SMB_ASSERT(regedit->vl != NULL);
+
+ regedit->tree_input = true;
+ print_heading(regedit);
+
+ tree_view_show(regedit->keys);
+ load_values(regedit);
+ value_list_show(regedit->vl);
+
+ update_panels();
+ doupdate();
+
+ do {
+ key = regedit_getch();
+
+ handle_main_input(regedit, key);
+ update_panels();
+ doupdate();
+ } while (key != 'q' && key != 'Q');
+
+ endwin();
+}
+
+int main(int argc, char **argv)
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ /* ... */
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ int opt;
+ poptContext pc;
+ TALLOC_CTX *frame;
+ struct registry_context *ctx;
+ WERROR rv;
+ bool ok;
+ struct loadparm_context *lp_ctx = NULL;
+
+ frame = talloc_stackframe();
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+ /* process options */
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ rv = reg_open_samba3(frame, &ctx);
+ if (!W_ERROR_IS_OK(rv)) {
+ fprintf(stderr, "Unable to open registry: %s\n",
+ win_errstr(rv));
+ TALLOC_FREE(frame);
+
+ return 1;
+ }
+
+ display_window(frame, ctx);
+
+ gfree_all();
+
+ TALLOC_FREE(frame);
+
+ return 0;
+}
diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h
new file mode 100644
index 0000000..55a25c9
--- /dev/null
+++ b/source3/utils/regedit.h
@@ -0,0 +1,77 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REGEDIT_H_
+#define _REGEDIT_H_
+
+struct registry_context;
+struct security_token;
+struct registry_key;
+
+struct samba3_registry_key {
+ struct registry_key *key;
+};
+
+WERROR reg_openhive_wrap(TALLOC_CTX *ctx, const char *hive,
+ struct samba3_registry_key *key);
+WERROR reg_openkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent,
+ const char *name, struct samba3_registry_key *key);
+WERROR reg_enumvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ uint32_t idx, char **name, uint32_t *type,
+ DATA_BLOB *data);
+WERROR reg_queryvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ const char *name, uint32_t *type, DATA_BLOB *data);
+WERROR reg_enumkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ uint32_t idx, char **name, NTTIME *last_write_time);
+WERROR reg_createkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent,
+ const char *subkeypath,
+ struct samba3_registry_key *pkey);
+WERROR reg_deletekey_wrap(struct samba3_registry_key *parent,
+ const char *path);
+WERROR reg_deletevalue_wrap(struct samba3_registry_key *key, const char *name);
+WERROR reg_queryinfokey_wrap(struct samba3_registry_key *key,
+ uint32_t *num_subkeys, uint32_t *max_subkeylen,
+ uint32_t *max_subkeysize, uint32_t *num_values,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize, uint32_t *secdescsize,
+ NTTIME *last_changed_time);
+WERROR reg_setvalue_wrap(struct samba3_registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data);
+WERROR reg_init_wrap(void);
+
+WERROR reg_open_samba3(TALLOC_CTX *mem_ctx, struct registry_context **ctx);
+
+int regedit_getch(void);
+
+typedef bool (*regedit_search_match_fn_t)(const char *, const char *);
+
+struct regedit_search_opts {
+ const char *query;
+ regedit_search_match_fn_t match;
+ bool search_key;
+ bool search_value;
+ bool search_recursive;
+ bool search_case;
+};
+
+#define PAIR_YELLOW_CYAN 1
+#define PAIR_BLACK_CYAN 2
+#define PAIR_YELLOW_BLUE 3
+
+#endif
diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c
new file mode 100644
index 0000000..d1cb45f
--- /dev/null
+++ b/source3/utils/regedit_dialog.c
@@ -0,0 +1,2328 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "regedit.h"
+#include "regedit_dialog.h"
+#include "regedit_valuelist.h"
+#include "regedit_hexedit.h"
+#include "util_reg.h"
+#include "lib/registry/registry.h"
+#include "lib/util/smb_strtox.h"
+#include <stdarg.h>
+#include <form.h>
+
+static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n)
+{
+ char *str;
+
+ str = talloc_strndup(ctx, buf, n);
+
+ if (str) {
+ trim_string(str, " ", " ");
+ }
+
+ return str;
+}
+
+static char *string_trim(TALLOC_CTX *ctx, const char *buf)
+{
+ char *str;
+
+ str = talloc_strdup(ctx, buf);
+
+ if (str) {
+ trim_string(str, " ", " ");
+ }
+
+ return str;
+}
+
+static int dialog_free(struct dialog *dia)
+{
+ dialog_destroy(dia);
+
+ return 0;
+}
+
+static bool default_validator(struct dialog *dia, struct dialog_section *sect,
+ void *arg)
+{
+ return true;
+}
+
+struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title,
+ int y, int x)
+{
+ struct dialog *dia;
+
+ dia = talloc_zero(ctx, struct dialog);
+ if (dia == NULL) {
+ return NULL;
+ }
+
+ talloc_set_destructor(dia, dialog_free);
+
+ dia->title = talloc_strdup(dia, title);
+ if (dia->title == NULL) {
+ goto fail;
+ }
+ dia->x = x;
+ dia->y = y;
+ dia->color = color;
+ dia->submit = default_validator;
+
+ return dia;
+
+fail:
+ talloc_free(dia);
+
+ return NULL;
+
+}
+
+void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg)
+{
+ dia->submit = cb;
+ dia->submit_arg = arg;
+}
+
+static void center_above_window(int *nlines, int *ncols, int *y, int *x)
+{
+ int centery, centerx;
+
+ centery = LINES / 2;
+ centerx = COLS / 2;
+ *y = 0;
+ *x = 0;
+
+ if (*nlines > LINES) {
+ *nlines = LINES;
+ }
+ if (*ncols > COLS) {
+ *ncols = COLS;
+ }
+
+ if (*nlines/2 < centery) {
+ *y = centery - *nlines / 2;
+ }
+ if (*ncols/2 < centerx) {
+ *x = centerx - *ncols / 2;
+ }
+}
+
+void dialog_section_destroy(struct dialog_section *section)
+{
+ if (section->ops->destroy) {
+ section->ops->destroy(section);
+ }
+ if (section->window) {
+ delwin(section->window);
+ section->window = NULL;
+ }
+}
+
+void dialog_section_init(struct dialog_section *section,
+ const struct dialog_section_ops *ops,
+ int nlines, int ncols)
+{
+ section->ops = ops;
+ section->nlines = nlines;
+ section->ncols = ncols;
+}
+
+const char *dialog_section_get_name(struct dialog_section *section)
+{
+ return section->name;
+}
+
+void dialog_section_set_name(struct dialog_section *section, const char *name)
+{
+ TALLOC_FREE(section->name);
+ section->name = talloc_strdup(section, name);
+}
+
+void dialog_section_set_justify(struct dialog_section *section,
+ enum section_justify justify)
+{
+ section->justify = justify;
+}
+
+/* append a section to the dialog's circular list */
+void dialog_append_section(struct dialog *dia,
+ struct dialog_section *section)
+{
+ SMB_ASSERT(section != NULL);
+
+ if (!dia->head_section) {
+ dia->head_section = section;
+ }
+ if (dia->tail_section) {
+ dia->tail_section->next = section;
+ }
+ section->prev = dia->tail_section;
+ section->next = dia->head_section;
+ dia->head_section->prev = section;
+ dia->tail_section = section;
+}
+
+struct dialog_section *dialog_find_section(struct dialog *dia, const char *name)
+{
+ struct dialog_section *section = dia->head_section;
+
+ do {
+ if (section->name && strequal(section->name, name)) {
+ return section;
+ }
+ section = section->next;
+ } while (section != dia->head_section);
+
+ return NULL;
+}
+
+static void section_on_input(struct dialog *dia, int c)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section->ops->on_input) {
+ return;
+ }
+ section->ops->on_input(dia, section, c);
+}
+
+static bool section_on_tab(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_tab) {
+ return false;
+ }
+ return section->ops->on_tab(dia, section);
+}
+
+static bool section_on_btab(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_btab) {
+ return false;
+ }
+ return section->ops->on_btab(dia, section);
+}
+
+static bool section_on_up(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_up) {
+ return false;
+ }
+ return section->ops->on_up(dia, section);
+}
+
+static bool section_on_down(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_down) {
+ return false;
+ }
+ return section->ops->on_down(dia, section);
+}
+
+static bool section_on_left(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_left) {
+ return false;
+ }
+ return section->ops->on_left(dia, section);
+}
+
+static bool section_on_right(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_right) {
+ return false;
+ }
+ return section->ops->on_right(dia, section);
+}
+
+static enum dialog_action section_on_enter(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section || !section->ops->on_enter) {
+ return DIALOG_OK;
+ }
+ return section->ops->on_enter(dia, section);
+}
+
+static bool section_on_focus(struct dialog *dia, bool forward)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (!section->ops->on_focus) {
+ return false;
+ }
+ return section->ops->on_focus(dia, section, forward);
+}
+
+static void section_on_leave_focus(struct dialog *dia)
+{
+ struct dialog_section *section = dia->current_section;
+
+ if (section->ops->on_leave_focus) {
+ section->ops->on_leave_focus(dia, section);
+ }
+}
+
+static void section_set_next_focus(struct dialog *dia)
+{
+ section_on_leave_focus(dia);
+
+ do {
+ dia->current_section = dia->current_section->next;
+ } while (!section_on_focus(dia, true));
+}
+
+static void section_set_previous_focus(struct dialog *dia)
+{
+ section_on_leave_focus(dia);
+
+ do {
+ dia->current_section = dia->current_section->prev;
+ } while (!section_on_focus(dia, false));
+}
+
+WERROR dialog_create(struct dialog *dia)
+{
+ WERROR rv = WERR_OK;
+ int row, col;
+ int nlines, ncols;
+ struct dialog_section *section;
+
+ nlines = 0;
+ ncols = 0;
+ SMB_ASSERT(dia->head_section != NULL);
+
+ /* calculate total size based on sections */
+ section = dia->head_section;
+ do {
+ nlines += section->nlines;
+ ncols = MAX(ncols, section->ncols);
+ section = section->next;
+ } while (section != dia->head_section);
+
+ /* fill in widths for sections that expand */
+ section = dia->head_section;
+ do {
+ if (section->ncols < 0) {
+ section->ncols = ncols;
+ }
+ section = section->next;
+ } while (section != dia->head_section);
+
+ /* create window for dialog */
+ nlines += 4;
+ ncols += 6;
+ dia->pad = newpad(nlines, ncols);
+ if (dia->pad == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ dia->centered = false;
+ if (dia->y < 0 || dia->x < 0) {
+ dia->centered = true;
+ center_above_window(&nlines, &ncols, &dia->y, &dia->x);
+ }
+ dia->window = newwin(nlines, ncols, dia->y, dia->x);
+ if (dia->window == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ dia->panel = new_panel(dia->window);
+ if (dia->panel == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+
+ /* setup color and border */
+ getmaxyx(dia->pad, nlines, ncols);
+ wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color));
+ wclear(dia->pad);
+ mvwhline(dia->pad, 1, 2, 0, ncols - 4);
+ mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4);
+ mvwvline(dia->pad, 2, 1, 0, nlines - 4);
+ mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4);
+ mvwaddch(dia->pad, 1, 1, ACS_ULCORNER);
+ mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER);
+ mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER);
+ mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER);
+ col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2;
+ mvwprintw(dia->pad, 1, col, " %s ", dia->title);
+
+ /* create subwindows for each section */
+ row = 2;
+ section = dia->head_section;
+ do {
+ col = 3;
+
+ switch (section->justify) {
+ case SECTION_JUSTIFY_LEFT:
+ break;
+ case SECTION_JUSTIFY_CENTER:
+ col += (ncols - 6)/ 2 - section->ncols / 2;
+ break;
+ case SECTION_JUSTIFY_RIGHT:
+ break;
+ }
+
+ section->window = subpad(dia->pad, section->nlines,
+ section->ncols, row, col);
+ if (section->window == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto fail;
+ }
+ SMB_ASSERT(section->ops->create != NULL);
+ rv = section->ops->create(dia, section);
+ row += section->nlines;
+ section = section->next;
+ } while (section != dia->head_section && W_ERROR_IS_OK(rv));
+
+ dia->current_section = dia->head_section;
+ section_set_next_focus(dia);
+
+fail:
+ return rv;
+}
+
+void dialog_show(struct dialog *dia)
+{
+ int nlines, ncols;
+ int pad_y, pad_x;
+ int y, x;
+ int rv;
+
+ touchwin(dia->pad);
+ getmaxyx(dia->window, nlines, ncols);
+ getmaxyx(dia->pad, pad_y, pad_x);
+ y = 0;
+ if (pad_y > nlines) {
+ y = (pad_y - nlines) / 2;
+ }
+ x = 0;
+ if (pad_x > ncols) {
+ x = (pad_x - ncols) / 2;
+ }
+ rv = copywin(dia->pad, dia->window, y, x, 0, 0,
+ nlines - 1, ncols - 1, false);
+ SMB_ASSERT(rv == OK);
+
+ getyx(dia->pad, pad_y, pad_x);
+ wmove(dia->window, pad_y - y, pad_x - x);
+ touchwin(dia->window);
+ wnoutrefresh(dia->window);
+}
+
+void dialog_destroy(struct dialog *dia)
+{
+ struct dialog_section *section;
+
+ section = dia->head_section;
+ do {
+ dialog_section_destroy(section);
+ section = section->next;
+ } while (section != dia->head_section);
+
+ if (dia->panel) {
+ del_panel(dia->panel);
+ dia->panel = NULL;
+ }
+ if (dia->window) {
+ delwin(dia->window);
+ dia->window = NULL;
+ }
+}
+
+static int dialog_getch(struct dialog *dia)
+{
+ int c;
+
+ c = regedit_getch();
+ if (c == KEY_RESIZE) {
+ int nlines, ncols, y, x;
+ int pad_nlines, pad_ncols;
+ int win_nlines, win_ncols;
+
+ getmaxyx(dia->window, win_nlines, win_ncols);
+ getmaxyx(dia->pad, pad_nlines, pad_ncols);
+ getbegyx(dia->window, y, x);
+
+ nlines = pad_nlines;
+ ncols = pad_ncols;
+
+ if (dia->centered) {
+ center_above_window(&nlines, &ncols, &y, &x);
+ } else {
+ if (nlines + y > LINES) {
+ if (nlines > LINES) {
+ y = 0;
+ } else {
+ y = LINES - nlines;
+ }
+ }
+ if (ncols + x > COLS) {
+ if (ncols > COLS) {
+ x = 0;
+ } else {
+ x = COLS - ncols;
+ }
+ }
+ }
+ if (nlines != win_nlines || ncols != win_ncols) {
+ wresize(dia->window, nlines, ncols);
+ replace_panel(dia->panel, dia->window);
+ }
+ move_panel(dia->panel, y, x);
+ }
+
+ return c;
+}
+
+bool dialog_handle_input(struct dialog *dia, WERROR *err,
+ enum dialog_action *action)
+{
+ int c;
+
+ *err = WERR_OK;
+
+ c = dialog_getch(dia);
+
+ switch (c) {
+ case '\t':
+ if (!section_on_tab(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case KEY_BTAB:
+ if (!section_on_btab(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_UP:
+ if (!section_on_up(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_DOWN:
+ if (!section_on_down(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case KEY_LEFT:
+ if (!section_on_left(dia)) {
+ section_set_previous_focus(dia);
+ }
+ break;
+ case KEY_RIGHT:
+ if (!section_on_right(dia)) {
+ section_set_next_focus(dia);
+ }
+ break;
+ case '\n':
+ case KEY_ENTER:
+ *action = section_on_enter(dia);
+ switch (*action) {
+ case DIALOG_IGNORE:
+ break;
+ case DIALOG_CANCEL:
+ return false;
+ case DIALOG_OK:
+ return !dia->submit(dia, dia->current_section,
+ dia->submit_arg);
+ }
+ break;
+ case 27: /* ESC */
+ return false;
+ default:
+ section_on_input(dia, c);
+ break;
+ }
+
+ return true;
+}
+
+void dialog_modal_loop(struct dialog *dia, WERROR *err,
+ enum dialog_action *action)
+{
+ do {
+ dialog_show(dia);
+ update_panels();
+ doupdate();
+ } while (dialog_handle_input(dia, err, action));
+}
+
+/* text label */
+struct dialog_section_label {
+ struct dialog_section section;
+ char **text;
+};
+
+static WERROR label_create(struct dialog *dia, struct dialog_section *section)
+{
+ int row;
+ struct dialog_section_label *label =
+ talloc_get_type_abort(section, struct dialog_section_label);
+
+ for (row = 0; row < section->nlines; ++row) {
+ mvwaddstr(section->window, row, 0, label->text[row]);
+ }
+
+ return WERR_OK;
+}
+
+struct dialog_section_ops label_ops = {
+ .create = label_create,
+};
+
+static int label_free(struct dialog_section_label *label)
+{
+ dialog_section_destroy(&label->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx,
+ const char *msg, va_list ap)
+{
+ struct dialog_section_label *label;
+ char *tmp, *ptmp, *line, *saveptr;
+ int nlines, ncols;
+
+ label = talloc_zero(ctx, struct dialog_section_label);
+ if (label == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(label, label_free);
+ tmp = talloc_vasprintf(label, msg, ap);
+ if (tmp == NULL) {
+ goto fail;
+ }
+
+ for (nlines = 0, ncols = 0, ptmp = tmp;
+ (line = strtok_r(ptmp, "\n", &saveptr)) != NULL;
+ ++nlines) {
+ ptmp = NULL;
+ label->text = talloc_realloc(label, label->text,
+ char *, nlines + 1);
+ if (label->text == NULL) {
+ goto fail;
+ }
+ ncols = MAX(ncols, strlen(line));
+ label->text[nlines] = talloc_strdup(label->text, line);
+ if (label->text[nlines] == NULL) {
+ goto fail;
+ }
+ }
+ talloc_free(tmp);
+ dialog_section_init(&label->section, &label_ops, nlines, ncols);
+
+ return &label->section;
+
+fail:
+ talloc_free(label);
+ return NULL;
+}
+
+struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx,
+ const char *msg, ...)
+{
+ va_list ap;
+ struct dialog_section *rv;
+
+ va_start(ap, msg);
+ rv = dialog_section_label_new_va(ctx, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+/* horizontal separator */
+struct dialog_section_hsep {
+ struct dialog_section section;
+ int sep;
+};
+
+static WERROR hsep_create(struct dialog *dia, struct dialog_section *section)
+{
+ int y, x;
+ struct dialog_section_hsep *hsep =
+ talloc_get_type_abort(section, struct dialog_section_hsep);
+
+ whline(section->window, hsep->sep, section->ncols);
+
+ if (hsep->sep == 0 || hsep->sep == ACS_HLINE) {
+ /* change the border characters around this section to
+ tee chars */
+ getparyx(section->window, y, x);
+ mvwaddch(dia->pad, y, x - 1, ACS_HLINE);
+ mvwaddch(dia->pad, y, x - 2, ACS_LTEE);
+ mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE);
+ mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE);
+ }
+
+ return WERR_OK;
+}
+
+struct dialog_section_ops hsep_ops = {
+ .create = hsep_create
+};
+
+static int hsep_free(struct dialog_section_hsep *hsep)
+{
+ dialog_section_destroy(&hsep->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep)
+{
+ struct dialog_section_hsep *hsep;
+
+ hsep = talloc_zero(ctx, struct dialog_section_hsep);
+ if (hsep) {
+ talloc_set_destructor(hsep, hsep_free);
+ dialog_section_init(&hsep->section, &hsep_ops, 1, -1);
+ hsep->sep = sep;
+ }
+
+ return &hsep->section;
+}
+
+/* text input field */
+struct dialog_section_text_field {
+ struct dialog_section section;
+ unsigned opts;
+ FIELD *field[2];
+ FORM *form;
+ int length;
+};
+
+static int get_cursor_col(struct dialog_section_text_field *field)
+{
+ int col;
+
+ col = field->form->curcol + field->form->begincol;
+
+ return col;
+}
+
+static WERROR text_field_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ text_field->field[0] = new_field(section->nlines, section->ncols,
+ 0, 0, 0, 0);
+ if (text_field->field[0] == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ set_field_back(text_field->field[0], A_REVERSE);
+ set_field_opts(text_field->field[0], text_field->opts);
+
+ text_field->form = new_form(text_field->field);
+ if (text_field->form == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ set_form_win(text_field->form, dia->window);
+ set_form_sub(text_field->form, section->window);
+ set_current_field(text_field->form, text_field->field[0]);
+ post_form(text_field->form);
+
+ return WERR_OK;
+}
+
+static void text_field_destroy(struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (text_field->form) {
+ unpost_form(text_field->form);
+ free_form(text_field->form);
+ text_field->form = NULL;
+ }
+ if (text_field->field[0]) {
+ free_field(text_field->field[0]);
+ text_field->field[0] = NULL;
+ }
+}
+
+static void text_field_on_input(struct dialog *dia,
+ struct dialog_section *section,
+ int c)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ switch (c) {
+ case KEY_BACKSPACE:
+ if (text_field->length) {
+ text_field->length--;
+ }
+ form_driver(text_field->form, REQ_DEL_PREV);
+ break;
+ case '\x7f':
+ case KEY_DC:
+ if (text_field->length) {
+ text_field->length--;
+ }
+ form_driver(text_field->form, REQ_DEL_CHAR);
+ break;
+ default:
+ text_field->length++;
+ form_driver(text_field->form, c);
+ break;
+ }
+}
+
+static bool text_field_on_up(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ form_driver(text_field->form, REQ_UP_CHAR);
+ return true;
+ }
+ return false;
+}
+
+static bool text_field_on_down(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ form_driver(text_field->form, REQ_DOWN_CHAR);
+ return true;
+ }
+ return false;
+}
+
+static bool text_field_on_left(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_LEFT_CHAR);
+
+ return true;
+}
+
+static bool text_field_on_right(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1 ||
+ get_cursor_col(text_field) < text_field->length) {
+ form_driver(text_field->form, REQ_RIGHT_CHAR);
+ }
+
+ return true;
+}
+
+static enum dialog_action text_field_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ if (section->nlines > 1) {
+ text_field->length += text_field->form->cols;
+ form_driver(text_field->form, REQ_NEW_LINE);
+ return DIALOG_IGNORE;
+ }
+
+ return DIALOG_OK;
+}
+
+static bool text_field_on_focus(struct dialog *dia,
+ struct dialog_section *section, bool forward)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ pos_form_cursor(text_field->form);
+
+ return true;
+}
+
+struct dialog_section_ops text_field_ops = {
+ .create = text_field_create,
+ .destroy = text_field_destroy,
+ .on_input = text_field_on_input,
+ .on_up = text_field_on_up,
+ .on_down = text_field_on_down,
+ .on_left = text_field_on_left,
+ .on_right = text_field_on_right,
+ .on_enter = text_field_on_enter,
+ .on_focus = text_field_on_focus
+};
+
+static int text_field_free(struct dialog_section_text_field *text_field)
+{
+ dialog_section_destroy(&text_field->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx,
+ int height, int width)
+{
+ struct dialog_section_text_field *text_field;
+
+ text_field = talloc_zero(ctx, struct dialog_section_text_field);
+ if (text_field == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(text_field, text_field_free);
+ dialog_section_init(&text_field->section, &text_field_ops,
+ height, width);
+ text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK;
+
+ return &text_field->section;
+}
+
+const char *dialog_section_text_field_get(TALLOC_CTX *ctx,
+ struct dialog_section *section)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ return string_trim(ctx, field_buffer(text_field->field[0], 0));
+}
+
+void dialog_section_text_field_set(struct dialog_section *section,
+ const char *s)
+{
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ text_field->length = strlen(s);
+ set_field_buffer(text_field->field[0], 0, s);
+}
+
+const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section)
+{
+ int rows, cols, max;
+ const char **arr;
+ size_t i;
+ const char *buf;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+ buf = field_buffer(text_field->field[0], 0);
+
+ dynamic_field_info(text_field->field[0], &rows, &cols, &max);
+
+ arr = talloc_zero_array(ctx, const char *, rows + 1);
+ if (arr == NULL) {
+ return NULL;
+ }
+ for (i = 0; *buf; ++i, buf += cols) {
+ SMB_ASSERT(i < rows);
+ arr[i] = string_trim_n(arr, buf, cols);
+ }
+
+ return arr;
+}
+
+WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section,
+ const char **array)
+{
+ int rows, cols, max;
+ size_t padding, length, idx;
+ const char **arrayp;
+ char *buf = NULL;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ dynamic_field_info(text_field->field[0], &rows, &cols, &max);
+ /* try to fit each string on it's own line. each line
+ needs to be padded with whitespace manually, since
+ ncurses fields do not have newlines. */
+ for (idx = 0, arrayp = array; *arrayp != NULL; ++arrayp) {
+ length = MIN(strlen(*arrayp), cols);
+ padding = cols - length;
+ buf = talloc_realloc(ctx, buf, char,
+ talloc_array_length(buf) +
+ length + padding + 1);
+ if (buf == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ memcpy(&buf[idx], *arrayp, length);
+ idx += length;
+ memset(&buf[idx], ' ', padding);
+ idx += padding;
+ buf[idx] = '\0';
+ }
+
+ set_field_buffer(text_field->field[0], 0, buf);
+ talloc_free(buf);
+
+ return WERR_OK;
+}
+
+bool dialog_section_text_field_get_int(struct dialog_section *section,
+ long long *out)
+{
+ bool rv;
+ const char *buf;
+ char *endp;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ buf = string_trim(section, field_buffer(text_field->field[0], 0));
+ if (buf == NULL) {
+ return false;
+ }
+ *out = strtoll(buf, &endp, 0);
+ rv = true;
+ if (endp == buf || endp == NULL || endp[0] != '\0') {
+ rv = false;
+ }
+
+ return rv;
+}
+
+
+bool dialog_section_text_field_get_uint(struct dialog_section *section,
+ unsigned long long *out)
+{
+ const char *buf;
+ int error = 0;
+ struct dialog_section_text_field *text_field =
+ talloc_get_type_abort(section, struct dialog_section_text_field);
+
+ form_driver(text_field->form, REQ_VALIDATION);
+
+ buf = string_trim(section, field_buffer(text_field->field[0], 0));
+ if (buf == NULL) {
+ return false;
+ }
+ *out = smb_strtoull(buf, NULL, 0, &error, SMB_STR_FULL_STR_CONV);
+ if (error != 0) {
+ return false;
+ }
+
+ return true;
+}
+
+/* hex editor field */
+struct dialog_section_hexedit {
+ struct dialog_section section;
+ struct hexedit *buf;
+};
+
+#define HEXEDIT_MIN_SIZE 1
+static WERROR hexedit_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit->buf = hexedit_new(dia, section->window, NULL,
+ HEXEDIT_MIN_SIZE);
+ if (hexedit->buf == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ hexedit_refresh(hexedit->buf);
+
+ return WERR_OK;
+}
+
+static void hexedit_destroy(struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ if (hexedit->buf) {
+ TALLOC_FREE(hexedit->buf);
+ }
+}
+
+static void hexedit_on_input(struct dialog *dia,
+ struct dialog_section *section,
+ int c)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ switch (c) {
+ case KEY_BACKSPACE:
+ hexedit_driver(hexedit->buf, HE_BACKSPACE);
+ break;
+ case '\x7f':
+ case KEY_DC:
+ hexedit_driver(hexedit->buf, HE_DELETE);
+ break;
+ default:
+ hexedit_driver(hexedit->buf, c);
+ break;
+ }
+}
+
+static bool hexedit_on_up(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_UP);
+
+ return true;
+}
+
+static bool hexedit_on_down(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_DOWN);
+
+ return true;
+}
+
+static bool hexedit_on_left(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_LEFT);
+
+ return true;
+}
+
+static bool hexedit_on_right(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT);
+
+ return true;
+}
+
+static enum dialog_action hexedit_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ return DIALOG_IGNORE;
+}
+
+static bool hexedit_on_focus(struct dialog *dia,
+ struct dialog_section *section, bool forward)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ hexedit_set_cursor(hexedit->buf);
+
+ return true;
+}
+
+struct dialog_section_ops hexedit_ops = {
+ .create = hexedit_create,
+ .destroy = hexedit_destroy,
+ .on_input = hexedit_on_input,
+ .on_up = hexedit_on_up,
+ .on_down = hexedit_on_down,
+ .on_left = hexedit_on_left,
+ .on_right = hexedit_on_right,
+ .on_enter = hexedit_on_enter,
+ .on_focus = hexedit_on_focus
+};
+
+static int hexedit_free(struct dialog_section_hexedit *hexedit)
+{
+ dialog_section_destroy(&hexedit->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height)
+{
+ struct dialog_section_hexedit *hexedit;
+
+ hexedit = talloc_zero(ctx, struct dialog_section_hexedit);
+ if (hexedit == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(hexedit, hexedit_free);
+ dialog_section_init(&hexedit->section, &hexedit_ops,
+ height, LINE_WIDTH);
+
+ return &hexedit->section;
+}
+
+WERROR dialog_section_hexedit_set_buf(struct dialog_section *section,
+ const void *data, size_t size)
+{
+ WERROR rv;
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+
+ rv = hexedit_set_buf(hexedit->buf, data, size);
+ if (W_ERROR_IS_OK(rv)) {
+ hexedit_refresh(hexedit->buf);
+ hexedit_set_cursor(hexedit->buf);
+ }
+
+ return rv;
+}
+
+void dialog_section_hexedit_get_buf(struct dialog_section *section,
+ const void **data, size_t *size)
+{
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+ *data = hexedit_get_buf(hexedit->buf);
+ *size = hexedit_get_buf_len(hexedit->buf);
+}
+
+WERROR dialog_section_hexedit_resize(struct dialog_section *section,
+ size_t size)
+{
+ WERROR rv;
+ struct dialog_section_hexedit *hexedit =
+ talloc_get_type_abort(section, struct dialog_section_hexedit);
+
+ SMB_ASSERT(hexedit->buf != NULL);
+ rv = hexedit_resize_buffer(hexedit->buf, size);
+ if (W_ERROR_IS_OK(rv)) {
+ hexedit_refresh(hexedit->buf);
+ }
+
+ return rv;
+}
+
+
+/* button box */
+struct dialog_section_buttons {
+ struct dialog_section section;
+ struct button_spec *spec;
+ int current_button;
+};
+
+static void buttons_unhighlight(struct dialog_section_buttons *buttons)
+{
+ short pair;
+ attr_t attr;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(buttons->section.window, &attr, &pair, NULL);
+ mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
+ wnoutrefresh(buttons->section.window);
+}
+
+static void buttons_highlight(struct dialog_section_buttons *buttons)
+{
+ struct button_spec *spec = &buttons->spec[buttons->current_button];
+ short pair;
+ attr_t attr;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(buttons->section.window, &attr, &pair, NULL);
+ mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL);
+ mvwchgat(buttons->section.window, 0, spec->col,
+ strlen(spec->label), A_REVERSE, pair, NULL);
+ wmove(buttons->section.window, 0, spec->col + 2);
+ wcursyncup(buttons->section.window);
+ wnoutrefresh(buttons->section.window);
+}
+
+static bool buttons_highlight_next(struct dialog_section_buttons *buttons)
+{
+ if (buttons->current_button < talloc_array_length(buttons->spec) - 1) {
+ buttons->current_button++;
+ buttons_highlight(buttons);
+ return true;
+ }
+ return false;
+}
+
+static bool buttons_highlight_previous(struct dialog_section_buttons *buttons)
+{
+ if (buttons->current_button > 0) {
+ buttons->current_button--;
+ buttons_highlight(buttons);
+ return true;
+ }
+ return false;
+}
+
+static WERROR buttons_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ size_t i, nbuttons;
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ nbuttons = talloc_array_length(buttons->spec);
+ for (i = 0; i < nbuttons; ++i) {
+ struct button_spec *spec = &buttons->spec[i];
+ mvwaddstr(section->window, 0, spec->col, spec->label);
+ }
+
+ buttons->current_button = 0;
+
+ return WERR_OK;
+}
+
+static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ return buttons_highlight_previous(buttons);
+}
+
+static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ return buttons_highlight_next(buttons);
+}
+
+static enum dialog_action buttons_on_enter(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+ struct button_spec *spec = &buttons->spec[buttons->current_button];
+
+ if (spec->on_enter) {
+ return spec->on_enter(dia, section);
+ }
+
+ return spec->action;
+}
+
+static bool buttons_on_focus(struct dialog *dia,
+ struct dialog_section *section,
+ bool forward)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+
+ if (forward) {
+ buttons->current_button = 0;
+ } else {
+ buttons->current_button = talloc_array_length(buttons->spec) - 1;
+ }
+ buttons_highlight(buttons);
+
+ return true;
+}
+
+static void buttons_on_leave_focus(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_buttons *buttons =
+ talloc_get_type_abort(section, struct dialog_section_buttons);
+ buttons_unhighlight(buttons);
+}
+
+struct dialog_section_ops buttons_ops = {
+ .create = buttons_create,
+ .on_tab = buttons_on_tab,
+ .on_btab = buttons_on_btab,
+ .on_up = buttons_on_btab,
+ .on_down = buttons_on_tab,
+ .on_left = buttons_on_btab,
+ .on_right = buttons_on_tab,
+ .on_enter = buttons_on_enter,
+ .on_focus = buttons_on_focus,
+ .on_leave_focus = buttons_on_leave_focus
+};
+
+static int buttons_free(struct dialog_section_buttons *buttons)
+{
+ dialog_section_destroy(&buttons->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx,
+ const struct button_spec *spec)
+{
+ struct dialog_section_buttons *buttons;
+ size_t i, nbuttons;
+ int width;
+
+ buttons = talloc_zero(ctx, struct dialog_section_buttons);
+ if (buttons == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(buttons, buttons_free);
+
+ for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) {
+ }
+ buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons);
+ if (buttons->spec == NULL) {
+ goto fail;
+ }
+
+ for (width = 0, i = 0; i < nbuttons; ++i) {
+ buttons->spec[i] = spec[i];
+ buttons->spec[i].label = talloc_asprintf(buttons->spec,
+ "[ %s ]",
+ spec[i].label);
+ if (!buttons->spec[i].label) {
+ goto fail;
+ }
+
+ buttons->spec[i].col = width;
+ width += strlen(buttons->spec[i].label);
+ if (i != nbuttons - 1) {
+ ++width;
+ }
+ }
+
+ dialog_section_init(&buttons->section, &buttons_ops, 1, width);
+
+ return &buttons->section;
+
+fail:
+ talloc_free(buttons);
+ return NULL;
+}
+
+/* options */
+struct dialog_section_options {
+ struct dialog_section section;
+ struct option_spec *spec;
+ int current_option;
+ bool single_select;
+};
+
+static void options_unhighlight(struct dialog_section_options *options)
+{
+ short pair;
+ attr_t attr;
+ size_t row;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(options->section.window, &attr, &pair, NULL);
+ for (row = 0; row < options->section.nlines; ++row) {
+ mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
+ }
+ wnoutrefresh(options->section.window);
+}
+
+static void options_highlight(struct dialog_section_options *options)
+{
+ struct option_spec *spec = &options->spec[options->current_option];
+ short pair;
+ attr_t attr;
+ size_t row;
+
+ /*
+ * Some GCC versions will complain if the macro version of
+ * wattr_get is used. So we should enforce the use of the
+ * function instead. See:
+ * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html
+ */
+ (wattr_get)(options->section.window, &attr, &pair, NULL);
+ for (row = 0; row < options->section.nlines; ++row) {
+ mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL);
+ }
+ mvwchgat(options->section.window, spec->row, spec->col,
+ strlen(spec->label), A_REVERSE, pair, NULL);
+ wmove(options->section.window, spec->row, spec->col + 4);
+ wcursyncup(options->section.window);
+ wnoutrefresh(options->section.window);
+}
+
+static void options_render_state(struct dialog_section_options *options)
+{
+ size_t i, noptions;
+
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ struct option_spec *spec = &options->spec[i];
+ char c = ' ';
+ if (*spec->state)
+ c = 'x';
+ mvwaddch(options->section.window,
+ spec->row, spec->col + 1, c);
+ wnoutrefresh(options->section.window);
+ }
+}
+
+static bool options_highlight_next(struct dialog_section_options *options)
+{
+ if (options->current_option < talloc_array_length(options->spec) - 1) {
+ options->current_option++;
+ options_highlight(options);
+ return true;
+ }
+ return false;
+}
+
+static bool options_highlight_previous(struct dialog_section_options *options)
+{
+ if (options->current_option > 0) {
+ options->current_option--;
+ options_highlight(options);
+ return true;
+ }
+ return false;
+}
+
+static WERROR options_create(struct dialog *dia,
+ struct dialog_section *section)
+{
+ size_t i, noptions;
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ struct option_spec *spec = &options->spec[i];
+ mvwaddstr(section->window, spec->row, spec->col,
+ spec->label);
+ }
+
+ options->current_option = 0;
+ options_render_state(options);
+
+ return WERR_OK;
+}
+
+static bool options_on_btab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ return options_highlight_previous(options);
+}
+
+static bool options_on_tab(struct dialog *dia, struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ return options_highlight_next(options);
+}
+
+static void options_on_input(struct dialog *dia, struct dialog_section *section, int c)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ if (c == ' ') {
+ struct option_spec *spec = &options->spec[options->current_option];
+ if (options->single_select) {
+ size_t i, noptions;
+ noptions = talloc_array_length(options->spec);
+ for (i = 0; i < noptions; ++i) {
+ *(options->spec[i].state) = false;
+ }
+ }
+ *spec->state = !*spec->state;
+ options_unhighlight(options);
+ options_render_state(options);
+ options_highlight(options);
+ }
+}
+
+static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section)
+{
+ options_on_input(dia, section, ' ');
+ return DIALOG_OK;
+}
+
+static bool options_on_focus(struct dialog *dia,
+ struct dialog_section *section,
+ bool forward)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+
+ if (forward) {
+ options->current_option = 0;
+ } else {
+ options->current_option = talloc_array_length(options->spec) - 1;
+ }
+ options_highlight(options);
+
+ return true;
+}
+
+static void options_on_leave_focus(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section_options *options =
+ talloc_get_type_abort(section, struct dialog_section_options);
+ options_unhighlight(options);
+}
+
+struct dialog_section_ops options_ops = {
+ .create = options_create,
+ .on_tab = options_on_tab,
+ .on_btab = options_on_btab,
+ .on_up = options_on_btab,
+ .on_down = options_on_tab,
+ .on_left = options_on_btab,
+ .on_right = options_on_tab,
+ .on_input = options_on_input,
+ .on_enter = options_on_enter,
+ .on_focus = options_on_focus,
+ .on_leave_focus = options_on_leave_focus
+};
+
+static int options_free(struct dialog_section_options *options)
+{
+ dialog_section_destroy(&options->section);
+ return 0;
+}
+
+struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx,
+ const struct option_spec *spec,
+ int maxcol, bool single_select)
+{
+ struct dialog_section_options *options;
+ size_t i, noptions;
+ int width, maxwidth, maxrows;
+
+ options = talloc_zero(ctx, struct dialog_section_options);
+ if (options == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(options, options_free);
+
+ for (noptions = 0; spec[noptions].label; ++noptions) {
+ }
+ options->spec = talloc_zero_array(options, struct option_spec, noptions);
+ if (options->spec == NULL) {
+ goto fail;
+ }
+
+ maxrows = noptions / maxcol;
+ if (noptions % maxcol) {
+ ++maxrows;
+ }
+
+ for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) {
+ options->spec[i] = spec[i];
+ options->spec[i].label = talloc_asprintf(options->spec,
+ "[ ] %s",
+ spec[i].label);
+ if (!options->spec[i].label) {
+ goto fail;
+ }
+
+ options->spec[i].col = maxwidth;
+ options->spec[i].row = i % maxrows;
+ width = MAX(strlen(options->spec[i].label), width);
+ if (options->spec[i].row == maxrows - 1 || i == noptions - 1) {
+ maxwidth += width + 1;
+ width = 0;
+ }
+ }
+
+ dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1);
+ options->single_select = single_select;
+
+ return &options->section;
+
+fail:
+ talloc_free(options);
+ return NULL;
+}
+
+
+enum input_type {
+ DLG_IN_LONG,
+ DLG_IN_ULONG,
+ DLG_IN_STR,
+};
+
+struct input_req {
+ TALLOC_CTX *ctx;
+ enum input_type type;
+ union {
+ void *out;
+ unsigned long *out_ulong;
+ long *out_long;
+ const char **out_str;
+ } out;
+};
+
+static bool input_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct input_req *req = arg;
+ struct dialog_section *data;
+ unsigned long long out_ulong;
+ long long out_long;
+
+ data = dialog_find_section(dia, "input");
+
+ switch (req->type) {
+ case DLG_IN_LONG:
+ if (!dialog_section_text_field_get_int(data, &out_long)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Input must be a number.");
+ return false;
+ }
+ if (out_long < LONG_MIN || out_long > LONG_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Number is out of range.");
+ return false;
+ }
+ *req->out.out_long = out_long;
+ break;
+ case DLG_IN_ULONG:
+ if (!dialog_section_text_field_get_uint(data, &out_ulong)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Input must be a number greater than zero.");
+ return false;
+ }
+ if (out_ulong > ULONG_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Number is out of range.");
+ return false;
+ }
+ *req->out.out_ulong = out_ulong;
+ break;
+ case DLG_IN_STR:
+ *req->out.out_str = dialog_section_text_field_get(req->ctx, data);
+ break;
+ }
+
+ return true;
+}
+
+static int dialog_input_internal(TALLOC_CTX *ctx, void *output,
+ enum input_type type,
+ const char *title,
+ const char *msg, va_list ap)
+ PRINTF_ATTRIBUTE(5,0);
+
+static int dialog_input_internal(TALLOC_CTX *ctx, void *output,
+ enum input_type type,
+ const char *title,
+ const char *msg, va_list ap)
+{
+ WERROR err;
+ struct input_req req;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct button_spec spec[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+
+ req.ctx = ctx;
+ req.type = type;
+ req.out.out = output;
+ *req.out.out_str = NULL;
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
+ dialog_set_submit_cb(dia, input_on_submit, &req);
+ section = dialog_section_label_new_va(dia, msg, ap);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+ section = dialog_section_text_field_new(dia, 1, -1);
+ dialog_section_set_name(section, "input");
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}
+
+int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title,
+ const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_STR, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_ULONG, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_input_long(TALLOC_CTX *ctx, long *output,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ int rv;
+
+ va_start(ap, msg);
+ rv = dialog_input_internal(ctx, output, DLG_IN_LONG, title, msg, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
+ const char *title, const char *msg, ...)
+{
+ va_list ap;
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct button_spec spec[3];
+
+ memset(&spec, '\0', sizeof(spec));
+ spec[0].label = "OK";
+ spec[0].action = DIALOG_OK;
+ if (type == DIA_CONFIRM) {
+ spec[1].label = "Cancel";
+ spec[1].action = DIALOG_CANCEL;
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1);
+ va_start(ap, msg);
+ section = dialog_section_label_new_va(dia, msg, ap);
+ va_end(ap);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}
+
+
+struct edit_req {
+ uint32_t type;
+ uint32_t mode;
+ struct registry_key *key;
+ const struct value_item *vitem;
+};
+
+static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit)
+{
+ char *tmp;
+ struct dialog_section *data;
+
+ if (edit->vitem == NULL) {
+ return WERR_OK;
+ }
+
+ data = dialog_find_section(dia, "data");
+ SMB_ASSERT(data != NULL);
+
+ switch (edit->mode) {
+ case REG_DWORD: {
+ uint32_t v = 0;
+ if (edit->vitem->data.length >= 4) {
+ v = IVAL(edit->vitem->data.data, 0);
+ }
+ tmp = talloc_asprintf(dia, "%u", (unsigned)v);
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dialog_section_text_field_set(data, tmp);
+ talloc_free(tmp);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *s;
+
+ if (!pull_reg_sz(dia, &edit->vitem->data, &s)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ dialog_section_text_field_set(data, s);
+ break;
+ }
+ case REG_MULTI_SZ: {
+ const char **array;
+
+ if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ return dialog_section_text_field_set_lines(dia, data, array);
+ }
+ case REG_BINARY:
+ default:
+ return dialog_section_hexedit_set_buf(data,
+ edit->vitem->data.data,
+ edit->vitem->data.length);
+ }
+
+ return WERR_OK;
+}
+
+static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key,
+ const char *name)
+{
+ uint32_t type;
+ DATA_BLOB blob;
+ WERROR rv;
+
+ rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob);
+
+ return W_ERROR_IS_OK(rv);
+}
+
+static bool edit_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct edit_req *edit = arg;
+ WERROR rv;
+ DATA_BLOB blob;
+ const char *name;
+ struct dialog_section *name_section, *data;
+
+ name_section = dialog_find_section(dia, "name");
+ if (name_section) {
+ name = dialog_section_text_field_get(dia, name_section);
+ if (*name == '\0') {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Value name must not be blank.");
+ return false;
+ }
+ if (value_exists(dia, edit->key, name)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Value named \"%s\" already exists.",
+ name);
+ return false;
+ }
+ } else {
+ SMB_ASSERT(edit->vitem);
+ name = edit->vitem->value_name;
+ }
+ SMB_ASSERT(name);
+
+ data = dialog_find_section(dia, "data");
+ SMB_ASSERT(data != NULL);
+
+ rv = WERR_OK;
+ switch (edit->mode) {
+ case REG_DWORD: {
+ unsigned long long v;
+ uint32_t val;
+
+ if (!dialog_section_text_field_get_uint(data, &v)) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "REG_DWORD value must be an integer.");
+ return false;
+ }
+ if (v > UINT32_MAX) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "REG_DWORD value must less than %lu.",
+ (unsigned long)UINT32_MAX);
+ return false;
+ }
+ val = (uint32_t)v;
+ blob = data_blob_talloc(dia, NULL, sizeof(val));
+ SIVAL(blob.data, 0, val);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *buf;
+
+ buf = dialog_section_text_field_get(dia, data);
+ if (!buf || !push_reg_sz(dia, &blob, buf)) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ }
+ case REG_MULTI_SZ: {
+ const char **lines;
+
+ lines = dialog_section_text_field_get_lines(dia, data);
+ if (!lines || !push_reg_multi_sz(dia, &blob, lines)) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ }
+ break;
+ }
+ case REG_BINARY: {
+ const void *buf;
+ size_t len;
+
+ dialog_section_hexedit_get_buf(data, &buf, &len);
+ blob = data_blob_talloc(dia, buf, len);
+ break;
+ }
+ }
+
+ if (W_ERROR_IS_OK(rv)) {
+ rv = reg_val_set(edit->key, name, edit->type, blob);
+ }
+
+ if (!W_ERROR_IS_OK(rv)) {
+ const char *msg = get_friendly_werror_msg(rv);
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Error saving value:\n%s", msg);
+
+ return false;
+ }
+
+ return true;
+
+}
+
+static enum dialog_action edit_on_resize(struct dialog *dia,
+ struct dialog_section *section)
+{
+ struct dialog_section *data;
+ unsigned long size;
+ int rv;
+
+ data = dialog_find_section(dia, "data");
+ rv = dialog_input_ulong(dia, &size, "Resize", "Enter size of buffer");
+ if (rv == DIALOG_OK) {
+ dialog_section_hexedit_resize(data, size);
+ }
+
+ return DIALOG_IGNORE;
+}
+
+int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key,
+ uint32_t type, const struct value_item *vitem,
+ bool force_binary, WERROR *err,
+ const char **name)
+{
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ struct edit_req edit;
+ struct button_spec buttons[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ struct button_spec buttons_hexedit[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Resize Buffer", .on_enter = edit_on_resize},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+
+
+ edit.key = key;
+ edit.vitem = vitem;
+ edit.type = type;
+ edit.mode = type;
+ if (force_binary || (vitem && vitem->unprintable)) {
+ edit.mode = REG_BINARY;
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1);
+ dialog_set_submit_cb(dia, edit_on_submit, &edit);
+
+ section = dialog_section_label_new(dia, "Type");
+ dialog_append_section(dia, section);
+ section = dialog_section_label_new(dia, "%s",
+ str_regtype(type));
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+
+ section = dialog_section_label_new(dia, "Name");
+ dialog_append_section(dia, section);
+ if (vitem) {
+ section = dialog_section_label_new(dia, "%s",
+ vitem->value_name);
+ } else {
+ section = dialog_section_text_field_new(dia, 1, 50);
+ dialog_section_set_name(section, "name");
+ }
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+
+ section = dialog_section_label_new(dia, "Data");
+ dialog_append_section(dia, section);
+
+ switch (edit.mode) {
+ case REG_DWORD:
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ section = dialog_section_text_field_new(dia, 1, 50);
+ break;
+ case REG_MULTI_SZ:
+ section = dialog_section_text_field_new(dia, 10, 50);
+ break;
+ case REG_BINARY:
+ default:
+ section = dialog_section_hexedit_new(dia, 10);
+ break;
+ }
+
+ dialog_section_set_name(section, "data");
+ dialog_append_section(dia, section);
+
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ if (edit.mode == REG_BINARY) {
+ section = dialog_section_buttons_new(dia, buttons_hexedit);
+ } else {
+ section = dialog_section_buttons_new(dia, buttons);
+ }
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+
+ *err = fill_value_buffer(dia, &edit);
+ if (!W_ERROR_IS_OK(*err)) {
+ return DIALOG_CANCEL;
+ }
+
+ dialog_show(dia);
+ dialog_modal_loop(dia, err, &action);
+
+ if (action == DIALOG_OK && name) {
+ if (vitem) {
+ *name = talloc_strdup(ctx, vitem->value_name);
+ } else if ((section = dialog_find_section(dia, "name"))) {
+ *name = dialog_section_text_field_get(ctx, section);
+ }
+ }
+
+ talloc_free(dia);
+
+ return action;
+}
+
+int dialog_select_type(TALLOC_CTX *ctx, int *type)
+{
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section;
+ const char *reg_types[] = {
+ "REG_BINARY",
+ "REG_DWORD",
+ "REG_EXPAND_SZ",
+ "REG_MULTI_SZ",
+ "REG_SZ"
+ };
+ #define NTYPES ARRAY_SIZE(reg_types)
+ struct button_spec spec[] = {
+ {.label = "OK", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ bool flags[NTYPES] = { true };
+ struct option_spec opsec[NTYPES + 1];
+ unsigned i;
+
+ memset(&opsec, '\0', sizeof(opsec));
+ for (i = 0; i < NTYPES; ++i) {
+ opsec[i].label = reg_types[i];
+ opsec[i].state = &flags[i];
+ }
+
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1);
+
+ section = dialog_section_label_new(dia, "Select type for new value:");
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, ' ');
+ dialog_append_section(dia, section);
+ section = dialog_section_options_new(dia, opsec, 2, true);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ dialog_show(dia);
+
+ dialog_modal_loop(dia, &err, &action);
+ if (action == DIALOG_OK) {
+ for (i = 0; i < NTYPES; ++i) {
+ if (flags[i]) {
+ *type = regtype_by_string(reg_types[i]);
+ break;
+ }
+ }
+ }
+
+ talloc_free(dia);
+
+ return action;
+}
+
+struct search_req {
+ TALLOC_CTX *ctx;
+ struct regedit_search_opts *opts;
+};
+
+static bool search_on_submit(struct dialog *dia, struct dialog_section *section,
+ void *arg)
+{
+ struct search_req *search = arg;
+ struct dialog_section *query;
+
+ query = dialog_find_section(dia, "query");
+ SMB_ASSERT(query != NULL);
+
+ if (!search->opts->search_key && !search->opts->search_value) {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Must search a key and/or a value");
+ return false;
+ }
+
+ talloc_free(discard_const(search->opts->query));
+ search->opts->query = dialog_section_text_field_get(search->ctx, query);
+ SMB_ASSERT(search->opts->query != NULL);
+ if (search->opts->query[0] == '\0') {
+ dialog_notice(dia, DIA_ALERT, "Error",
+ "Query must not be blank.");
+ return false;
+ }
+
+ return true;
+}
+
+int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts)
+{
+ WERROR err;
+ enum dialog_action action;
+ struct dialog *dia;
+ struct dialog_section *section, *query;
+ struct search_req search;
+ struct button_spec spec[] = {
+ {.label = "Search", .action = DIALOG_OK},
+ {.label = "Cancel", .action = DIALOG_CANCEL},
+ { 0 }
+ };
+ struct option_spec search_opts[] = {
+ {.label = "Search Keys", .state = &opts->search_key},
+ {.label = "Search Values", .state = &opts->search_value},
+ {.label = "Recursive", .state = &opts->search_recursive},
+ {.label = "Case Sensitive", .state = &opts->search_case},
+ { 0 }
+ };
+
+ if (!opts->search_key && !opts->search_value) {
+ opts->search_key = true;
+ }
+
+ search.ctx = ctx;
+ search.opts = opts;
+ dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1);
+ dialog_set_submit_cb(dia, search_on_submit, &search);
+ section = dialog_section_label_new(dia, "Query");
+ dialog_append_section(dia, section);
+ query = dialog_section_text_field_new(dia, 1, -1);
+ dialog_section_set_name(query, "query");
+ dialog_append_section(dia, query);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_options_new(dia, search_opts, 2, false);
+ dialog_append_section(dia, section);
+ section = dialog_section_hsep_new(dia, 0);
+ dialog_append_section(dia, section);
+ section = dialog_section_buttons_new(dia, spec);
+ dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER);
+ dialog_append_section(dia, section);
+
+ dialog_create(dia);
+ if (opts->query) {
+ dialog_section_text_field_set(query, opts->query);
+ }
+
+ dialog_modal_loop(dia, &err, &action);
+ talloc_free(dia);
+
+ return action;
+}
diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h
new file mode 100644
index 0000000..b8bc3bf
--- /dev/null
+++ b/source3/utils/regedit_dialog.h
@@ -0,0 +1,240 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REGEDIT_DIALOG_H_
+#define _REGEDIT_DIALOG_H_
+
+#include <ncurses.h>
+#include <panel.h>
+#include <menu.h>
+
+struct dialog;
+struct dialog_section;
+
+/* dialog submit cb. return true to close dialog, false to keep
+ it open */
+typedef bool (*dialog_submit_cb)(struct dialog *, struct dialog_section *,
+ void *);
+
+struct dialog {
+ char *title;
+ WINDOW *window;
+ WINDOW *pad;
+ PANEL *panel;
+ int x;
+ int y;
+ short color;
+ bool centered;
+ dialog_submit_cb submit;
+ void *submit_arg;
+ struct dialog_section *head_section;
+ struct dialog_section *tail_section;
+ struct dialog_section *current_section;
+};
+
+enum dialog_action {
+ DIALOG_IGNORE,
+ DIALOG_OK,
+ DIALOG_CANCEL
+};
+
+struct dialog_section_ops {
+ /* create section */
+ WERROR (*create)(struct dialog *, struct dialog_section *);
+
+ /* (optional) cleanup the section */
+ void (*destroy)(struct dialog_section *);
+
+ /* (optional) handle character input */
+ void (*on_input)(struct dialog *, struct dialog_section *, int c);
+
+ /* (optional) handle a tab character. return true if section dealt
+ with the tab internally, or false to advance focus to
+ the next dialog section. */
+ bool (*on_tab)(struct dialog *, struct dialog_section *);
+
+ /* (optional) handle a btab character. return true if section dealt
+ with the tab internally, or false to move focus to
+ the previous dialog section. */
+ bool (*on_btab)(struct dialog *, struct dialog_section *);
+
+ /* */
+ bool (*on_up)(struct dialog *, struct dialog_section *);
+
+ /* */
+ bool (*on_down)(struct dialog *, struct dialog_section *);
+
+ /* */
+ bool (*on_left)(struct dialog *, struct dialog_section *);
+
+ /* */
+ bool (*on_right)(struct dialog *, struct dialog_section *);
+
+ /* (optional) handle enter key. return DIALOG_OK to submit
+ dialog, DIALOG_CANCEL to close dialog, or DIALOG_IGNORE to
+ handle the enter internally. */
+ enum dialog_action (*on_enter)(struct dialog *,
+ struct dialog_section *);
+
+ /* (optional) called when this section is about to take focus. forward
+ is set to true when focus has landed here from forward traversal,
+ such as from a tab. return true to accept focus, false to pass to an
+ adjacent section. */
+ bool (*on_focus)(struct dialog *, struct dialog_section *, bool forward);
+
+ /* (optional) called when focus is leaving this section */
+ void (*on_leave_focus)(struct dialog *, struct dialog_section *);
+};
+
+enum section_justify {
+ SECTION_JUSTIFY_LEFT,
+ SECTION_JUSTIFY_CENTER,
+ SECTION_JUSTIFY_RIGHT,
+};
+
+struct dialog_section {
+ char *name;
+ int nlines;
+ int ncols;
+ WINDOW *window;
+ enum section_justify justify;
+ const struct dialog_section_ops *ops;
+ struct dialog_section *next;
+ struct dialog_section *prev;
+};
+
+struct dialog *dialog_new(TALLOC_CTX *ctx, short color,
+ const char *title, int y, int x);
+
+void dialog_section_destroy(struct dialog_section *section);
+void dialog_section_init(struct dialog_section *section,
+ const struct dialog_section_ops *ops,
+ int nlines, int ncols);
+
+void dialog_section_set_name(struct dialog_section *section, const char *name);
+const char *dialog_section_get_name(struct dialog_section *section);
+void dialog_section_set_justify(struct dialog_section *section,
+ enum section_justify justify);
+
+void dialog_append_section(struct dialog *dia,
+ struct dialog_section *section);
+struct dialog_section *dialog_find_section(struct dialog *dia,
+ const char *name);
+
+WERROR dialog_create(struct dialog *dia);
+void dialog_show(struct dialog *dia);
+void dialog_destroy(struct dialog *dia);
+void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg);
+bool dialog_handle_input(struct dialog *dia, WERROR *err,
+ enum dialog_action *action);
+void dialog_modal_loop(struct dialog *dia, WERROR *err,
+ enum dialog_action *action);
+
+struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx,
+ const char *msg,
+ va_list ap)
+ PRINTF_ATTRIBUTE(2,0);
+struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx,
+ const char *msg, ...)
+ PRINTF_ATTRIBUTE(2,3);
+
+struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep);
+
+
+struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx,
+ int height, int width);
+const char *dialog_section_text_field_get(TALLOC_CTX *ctx,
+ struct dialog_section *section);
+const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section);
+bool dialog_section_text_field_get_int(struct dialog_section *section,
+ long long *out);
+bool dialog_section_text_field_get_uint(struct dialog_section *section,
+ unsigned long long *out);
+void dialog_section_text_field_set(struct dialog_section *section,
+ const char *s);
+WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx,
+ struct dialog_section *section,
+ const char **array);
+
+struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height);
+WERROR dialog_section_hexedit_set_buf(struct dialog_section *section,
+ const void *data, size_t size);
+void dialog_section_hexedit_get_buf(struct dialog_section *section,
+ const void **data, size_t *size);
+WERROR dialog_section_hexedit_resize(struct dialog_section *section,
+ size_t size);
+
+struct button_spec {
+ const char *label;
+ enum dialog_action (*on_enter)(struct dialog *,
+ struct dialog_section *);
+ enum dialog_action action;
+
+ /* internal */
+ int col;
+};
+struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx,
+ const struct button_spec *spec);
+
+struct option_spec {
+ const char *label;
+ bool *state;
+
+ /* internal */
+ int col;
+ int row;
+};
+struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx,
+ const struct option_spec *spec,
+ int maxcol, bool single_select);
+
+enum dialog_type {
+ DIA_ALERT,
+ DIA_CONFIRM
+};
+
+int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type,
+ const char *title, const char *msg, ...)
+ PRINTF_ATTRIBUTE(4,5);
+
+int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title,
+ const char *msg, ...) PRINTF_ATTRIBUTE(4,5);
+int dialog_input_long(TALLOC_CTX *ctx, long *output,
+ const char *title, const char *msg, ...)
+ PRINTF_ATTRIBUTE(4,5);
+int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output,
+ const char *title, const char *msg, ...)
+ PRINTF_ATTRIBUTE(4,5);
+
+struct registry_key;
+struct value_item;
+
+int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key,
+ uint32_t type, const struct value_item *vitem,
+ bool force_binary, WERROR *err,
+ const char **name);
+
+int dialog_select_type(TALLOC_CTX *ctx, int *type);
+
+struct regedit_search_opts;
+
+int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts);
+
+#endif
diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c
new file mode 100644
index 0000000..413e563
--- /dev/null
+++ b/source3/utils/regedit_hexedit.c
@@ -0,0 +1,563 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "regedit_hexedit.h"
+
+/*
+ offset hex1 hex2 ascii
+ 00000000 FF FF FF FF FF FF FF FF ........
+*/
+
+#define HEX_COL1 10
+#define HEX_COL1_END 21
+#define HEX_COL2 23
+#define HEX_COL2_END 34
+#define ASCII_COL 36
+#define ASCII_COL_END LINE_WIDTH
+#define BYTES_PER_LINE 8
+
+struct hexedit {
+ size_t offset;
+ size_t len;
+ size_t alloc_size;
+ int cursor_y;
+ int cursor_x;
+ size_t cursor_offset;
+ size_t cursor_line_offset;
+ int nibble;
+ uint8_t *data;
+ WINDOW *win;
+};
+
+static int max_rows(WINDOW *win)
+{
+ int maxy;
+
+ maxy = getmaxy(win);
+
+ return maxy - 1;
+}
+
+struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data,
+ size_t sz)
+{
+ WERROR rv;
+ struct hexedit *buf;
+
+ buf = talloc_zero(ctx, struct hexedit);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ buf->win = parent;
+
+ rv = hexedit_set_buf(buf, data, sz);
+ if (!W_ERROR_IS_OK(rv)) {
+ goto fail;
+ }
+
+ return buf;
+
+fail:
+ talloc_free(buf);
+
+ return NULL;
+}
+
+WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz)
+{
+ TALLOC_FREE(buf->data);
+
+ buf->data = talloc_zero_array(buf, uint8_t, sz);
+ if (buf->data == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (data != NULL) {
+ memcpy(buf->data, data, sz);
+ }
+
+ buf->len = sz;
+ buf->alloc_size = sz;
+ buf->cursor_x = HEX_COL1;
+ buf->cursor_y = 0;
+ buf->cursor_offset = 0;
+ buf->cursor_line_offset = 0;
+ buf->nibble = 0;
+
+ return WERR_OK;
+}
+
+const void *hexedit_get_buf(struct hexedit *buf)
+{
+ return buf->data;
+}
+
+size_t hexedit_get_buf_len(struct hexedit *buf)
+{
+ return buf->len;
+}
+
+static size_t bytes_per_screen(WINDOW *win)
+{
+ return max_rows(win) * BYTES_PER_LINE;
+}
+
+void hexedit_set_cursor(struct hexedit *buf)
+{
+ wmove(buf->win, max_rows(buf->win), 0);
+ wattron(buf->win, A_REVERSE | A_STANDOUT);
+ wclrtoeol(buf->win);
+ if (buf->cursor_offset < buf->len) {
+ wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len,
+ buf->cursor_offset, buf->data[buf->cursor_offset]);
+ } else {
+ wprintw(buf->win, "Len:%lu Off:%lu", buf->len,
+ buf->cursor_offset);
+ }
+ wattroff(buf->win, A_REVERSE | A_STANDOUT);
+ wmove(buf->win, buf->cursor_y, buf->cursor_x);
+ wcursyncup(buf->win);
+ wsyncup(buf->win);
+ untouchwin(buf->win);
+}
+
+void hexedit_refresh(struct hexedit *buf)
+{
+ size_t end;
+ size_t lineno;
+ size_t off;
+
+ werase(buf->win);
+ if (buf->len == 0) {
+ mvwprintw(buf->win, 0, 0, "%08X", 0);
+ return;
+ }
+
+ end = buf->offset + bytes_per_screen(buf->win);
+ if (end > buf->len) {
+ end = buf->len;
+ }
+
+ for (off = buf->offset, lineno = 0;
+ off < end;
+ off += BYTES_PER_LINE, ++lineno) {
+ uint8_t *line = buf->data + off;
+ size_t i, endline;
+
+ wmove(buf->win, lineno, 0);
+ wprintw(buf->win, "%08zX ", off);
+
+ endline = BYTES_PER_LINE;
+
+ if (off + BYTES_PER_LINE > buf->len) {
+ endline = buf->len - off;
+ }
+
+ for (i = 0; i < endline; ++i) {
+ wprintw(buf->win, "%02X", line[i]);
+ if (i + 1 < endline) {
+ if (i == 3) {
+ wprintw(buf->win, " ");
+ } else {
+ wprintw(buf->win, " ");
+ }
+ }
+ }
+
+ wmove(buf->win, lineno, ASCII_COL);
+ for (i = 0; i < endline; ++i) {
+ if (isprint(line[i])) {
+ waddch(buf->win, line[i]);
+ } else {
+ waddch(buf->win, '.');
+ }
+ }
+ }
+}
+
+static void calc_cursor_offset(struct hexedit *buf)
+{
+ buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE +
+ buf->cursor_line_offset;
+}
+
+static int offset_to_hex_col(size_t pos)
+{
+ switch (pos) {
+ case 0:
+ return HEX_COL1;
+ case 1:
+ return HEX_COL1 + 3;
+ case 2:
+ return HEX_COL1 + 6;
+ case 3:
+ return HEX_COL1 + 9;
+
+ case 4:
+ return HEX_COL2;
+ case 5:
+ return HEX_COL2 + 3;
+ case 6:
+ return HEX_COL2 + 6;
+ case 7:
+ return HEX_COL2 + 9;
+ }
+
+ return -1;
+}
+
+static bool scroll_up(struct hexedit *buf)
+{
+ if (buf->offset == 0) {
+ return false;
+ }
+
+ buf->offset -= BYTES_PER_LINE;
+
+ return true;
+}
+
+static void cursor_down(struct hexedit *buf)
+{
+ size_t space;
+ bool need_refresh = false;
+
+ space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE;
+ if (space > buf->len) {
+ return;
+ }
+
+ if (buf->cursor_y + 1 == max_rows(buf->win)) {
+ buf->offset += BYTES_PER_LINE;
+ need_refresh = true;
+ } else {
+ buf->cursor_y++;
+ }
+
+ if (buf->cursor_offset + BYTES_PER_LINE > buf->len) {
+ buf->nibble = 0;
+ buf->cursor_offset = buf->len;
+ buf->cursor_line_offset = buf->len - space;
+ if (buf->cursor_x >= ASCII_COL) {
+ buf->cursor_x = ASCII_COL + buf->cursor_line_offset;
+ } else {
+ buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset);
+ }
+ }
+ if (need_refresh) {
+ hexedit_refresh(buf);
+ }
+ calc_cursor_offset(buf);
+}
+
+static void cursor_up(struct hexedit *buf)
+{
+ if (buf->cursor_y == 0) {
+ if (scroll_up(buf)) {
+ hexedit_refresh(buf);
+ }
+ } else {
+ buf->cursor_y--;
+ }
+
+ calc_cursor_offset(buf);
+}
+
+static bool is_over_gap(struct hexedit *buf)
+{
+ int col;
+
+ if (buf->cursor_x < ASCII_COL) {
+ if (buf->cursor_x >= HEX_COL2) {
+ col = buf->cursor_x - HEX_COL2;
+ } else {
+ col = buf->cursor_x - HEX_COL1;
+ }
+
+ switch (col) {
+ case 2:
+ case 5:
+ case 8:
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void cursor_left(struct hexedit *buf)
+{
+ if (buf->cursor_x == HEX_COL1) {
+ return;
+ }
+ if (buf->cursor_x == HEX_COL2) {
+ buf->cursor_x = HEX_COL1_END - 1;
+ buf->cursor_line_offset = 3;
+ buf->nibble = 1;
+ } else if (buf->cursor_x == ASCII_COL) {
+ size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE;
+ if (off + 7 >= buf->len) {
+ size_t lastpos = buf->len - off;
+ buf->cursor_x = offset_to_hex_col(lastpos) + 1;
+ buf->cursor_line_offset = lastpos;
+ } else {
+ buf->cursor_x = HEX_COL2_END - 1;
+ buf->cursor_line_offset = 7;
+ }
+ buf->nibble = 1;
+ } else {
+ if (buf->cursor_x > ASCII_COL || buf->nibble == 0) {
+ buf->cursor_line_offset--;
+ }
+ buf->cursor_x--;
+ buf->nibble = !buf->nibble;
+ }
+
+ if (is_over_gap(buf)) {
+ buf->cursor_x--;
+ }
+
+ calc_cursor_offset(buf);
+}
+
+static void cursor_right(struct hexedit *buf)
+{
+ int new_x = buf->cursor_x + 1;
+
+ if (new_x == ASCII_COL_END) {
+ return;
+ }
+ if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) &&
+ buf->cursor_offset == buf->len) {
+ if (buf->cursor_x < ASCII_COL) {
+ new_x = ASCII_COL;
+ buf->cursor_line_offset = 0;
+ buf->nibble = 0;
+ } else {
+ return;
+ }
+ }
+ if (new_x == HEX_COL1_END) {
+ new_x = HEX_COL2;
+ buf->cursor_line_offset = 4;
+ buf->nibble = 0;
+ } else if (new_x == HEX_COL2_END) {
+ new_x = ASCII_COL;
+ buf->cursor_line_offset = 0;
+ buf->nibble = 0;
+ } else {
+ if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) {
+ buf->cursor_line_offset++;
+ }
+ buf->nibble = !buf->nibble;
+ }
+
+ buf->cursor_x = new_x;
+
+ if (is_over_gap(buf)) {
+ buf->cursor_x++;
+ }
+
+ calc_cursor_offset(buf);
+}
+
+static void do_edit(struct hexedit *buf, int c)
+{
+ uint8_t *byte;
+
+ if (buf->cursor_offset == buf->len) {
+ hexedit_resize_buffer(buf, buf->len + 1);
+ }
+
+ byte = buf->data + buf->cursor_offset;
+
+ if (buf->cursor_x >= ASCII_COL) {
+ *byte = (uint8_t)c;
+
+ mvwprintw(buf->win, buf->cursor_y,
+ offset_to_hex_col(buf->cursor_line_offset), "%X", c);
+ if (!isprint(c)) {
+ c = '.';
+ }
+ mvwaddch(buf->win, buf->cursor_y,
+ ASCII_COL + buf->cursor_line_offset, c);
+ if (buf->cursor_x + 1 != ASCII_COL_END) {
+ cursor_right(buf);
+ } else {
+ cursor_down(buf);
+ }
+ } else {
+ if (!isxdigit(c)) {
+ return;
+ }
+ c = toupper(c);
+ waddch(buf->win, c);
+
+ if (isdigit(c)) {
+ c = c - '0';
+ } else {
+ c = c - 'A' + 10;
+ }
+ if (buf->nibble == 0) {
+ *byte = (*byte & 0x0f) | c << 4;
+ } else {
+ *byte = (*byte & 0xf0) | c;
+ }
+
+ c = *byte;
+ if (!isprint(c)) {
+ c = '.';
+ }
+ mvwaddch(buf->win, buf->cursor_y,
+ ASCII_COL + buf->cursor_line_offset, c);
+
+ if (buf->cursor_x + 1 != HEX_COL2_END) {
+ cursor_right(buf);
+ } else {
+ cursor_down(buf);
+ }
+ }
+
+ hexedit_refresh(buf);
+}
+
+static void erase_at(struct hexedit *buf, size_t pos)
+{
+ if (pos >= buf->len) {
+ return;
+ }
+
+ if (pos < buf->len - 1) {
+ /* squeeze the character out of the buffer */
+ uint8_t *p = buf->data + pos;
+ uint8_t *end = buf->data + buf->len;
+ memmove(p, p + 1, end - p - 1);
+ }
+
+ buf->len--;
+ hexedit_refresh(buf);
+}
+
+static void do_backspace(struct hexedit *buf)
+{
+ size_t off;
+ bool do_erase = true;
+
+ if (buf->cursor_offset == 0) {
+ return;
+ }
+
+ off = buf->cursor_offset;
+ if (buf->cursor_x == ASCII_COL) {
+ cursor_up(buf);
+ buf->cursor_line_offset = 7;
+ buf->cursor_x = ASCII_COL_END - 1;
+ calc_cursor_offset(buf);
+ } else if (buf->cursor_x == HEX_COL1) {
+ cursor_up(buf);
+ buf->cursor_line_offset = 7;
+ buf->cursor_x = HEX_COL2_END - 1;
+ buf->nibble = 1;
+ calc_cursor_offset(buf);
+ } else {
+ if (buf->cursor_x < ASCII_COL && buf->nibble) {
+ do_erase = false;
+ }
+ cursor_left(buf);
+ }
+ if (do_erase) {
+ erase_at(buf, off - 1);
+ }
+}
+
+static void do_delete(struct hexedit *buf)
+{
+ erase_at(buf, buf->cursor_offset);
+}
+
+void hexedit_driver(struct hexedit *buf, int c)
+{
+ switch (c) {
+ case HE_CURSOR_UP:
+ cursor_up(buf);
+ break;
+ case HE_CURSOR_DOWN:
+ cursor_down(buf);
+ break;
+ case HE_CURSOR_LEFT:
+ cursor_left(buf);
+ break;
+ case HE_CURSOR_RIGHT:
+ cursor_right(buf);
+ break;
+ case HE_CURSOR_PGUP:
+ break;
+ case HE_CURSOR_PGDN:
+ break;
+ case HE_BACKSPACE:
+ do_backspace(buf);
+ break;
+ case HE_DELETE:
+ do_delete(buf);
+ break;
+ default:
+ do_edit(buf, c & 0xff);
+ break;
+ }
+
+ hexedit_set_cursor(buf);
+}
+
+WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz)
+{
+ /* reset the cursor if it'll be out of bounds
+ after the resize */
+ if (buf->cursor_offset > newsz) {
+ buf->cursor_y = 0;
+ buf->cursor_x = HEX_COL1;
+ buf->offset = 0;
+ buf->cursor_offset = 0;
+ buf->cursor_line_offset = 0;
+ buf->nibble = 0;
+ }
+
+ if (newsz > buf->len) {
+ if (newsz > buf->alloc_size) {
+ uint8_t *d;
+ buf->alloc_size *= 2;
+ if (newsz > buf->alloc_size) {
+ buf->alloc_size = newsz;
+ }
+ d = talloc_realloc(buf, buf->data, uint8_t,
+ buf->alloc_size);
+ if (d == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ buf->data = d;
+ }
+ memset(buf->data + buf->len, '\0', newsz - buf->len);
+ buf->len = newsz;
+ } else {
+ buf->len = newsz;
+ }
+
+ return WERR_OK;
+}
diff --git a/source3/utils/regedit_hexedit.h b/source3/utils/regedit_hexedit.h
new file mode 100644
index 0000000..dfbe27a
--- /dev/null
+++ b/source3/utils/regedit_hexedit.h
@@ -0,0 +1,49 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _HEXEDIT_H_
+#define _HEXEDIT_H_
+
+#include <ncurses.h>
+
+enum {
+ HE_CURSOR_UP = 0x1000,
+ HE_CURSOR_DOWN = 0x1100,
+ HE_CURSOR_LEFT = 0x1200,
+ HE_CURSOR_RIGHT = 0x1300,
+ HE_CURSOR_PGUP = 0x1400,
+ HE_CURSOR_PGDN = 0x1500,
+ HE_BACKSPACE = 0x1600,
+ HE_DELETE = 0x1700,
+};
+
+#define LINE_WIDTH 44
+struct hexedit;
+
+struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data,
+ size_t sz);
+WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz);
+const void *hexedit_get_buf(struct hexedit *buf);
+size_t hexedit_get_buf_len(struct hexedit *buf);
+void hexedit_set_cursor(struct hexedit *buf);
+void hexedit_refresh(struct hexedit *buf);
+void hexedit_driver(struct hexedit *buf, int c);
+WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz);
+
+#endif
diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c
new file mode 100644
index 0000000..b5405f2
--- /dev/null
+++ b/source3/utils/regedit_list.c
@@ -0,0 +1,591 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "regedit_list.h"
+#include "regedit.h"
+
+struct multilist {
+ WINDOW *window;
+ WINDOW *pad;
+
+ unsigned window_height;
+ unsigned window_width;
+ unsigned start_row;
+ unsigned cursor_row;
+
+ unsigned ncols;
+ struct multilist_column *columns;
+
+ const void *data;
+ unsigned nrows;
+ const void *current_row;
+ const struct multilist_accessors *cb;
+};
+
+/* data getters */
+static const void *data_get_first_row(struct multilist *list)
+{
+ SMB_ASSERT(list->cb->get_first_row);
+ return list->cb->get_first_row(list->data);
+}
+
+static const void *data_get_next_row(struct multilist *list, const void *row)
+{
+ SMB_ASSERT(list->cb->get_next_row);
+ return list->cb->get_next_row(list->data, row);
+}
+
+static const void *data_get_prev_row(struct multilist *list, const void *row)
+{
+ const void *tmp, *next;
+
+ if (list->cb->get_prev_row) {
+ return list->cb->get_prev_row(list->data, row);
+ }
+
+ tmp = data_get_first_row(list);
+ if (tmp == row) {
+ return NULL;
+ }
+
+ for (; tmp && (next = data_get_next_row(list, tmp)) != row;
+ tmp = next) {
+ }
+
+ SMB_ASSERT(tmp != NULL);
+
+ return tmp;
+}
+
+static unsigned data_get_row_count(struct multilist *list)
+{
+ unsigned i;
+ const void *row;
+
+ if (list->cb->get_row_count)
+ return list->cb->get_row_count(list->data);
+
+ for (i = 0, row = data_get_first_row(list);
+ row != NULL;
+ ++i, row = data_get_next_row(list, row)) {
+ }
+
+ return i;
+}
+
+static const void *data_get_row_n(struct multilist *list, size_t n)
+{
+ unsigned i;
+ const void *row;
+
+ if (list->cb->get_row_n)
+ return list->cb->get_row_n(list->data, n);
+
+ for (i = 0, row = data_get_first_row(list);
+ i < n && row != NULL;
+ ++i, row = data_get_next_row(list, row)) {
+ }
+
+ return row;
+}
+
+static const char *data_get_column_header(struct multilist *list, unsigned col)
+{
+ SMB_ASSERT(list->cb->get_column_header);
+ return list->cb->get_column_header(list->data, col);
+}
+
+static const char *data_get_item_label(struct multilist *list, const void *row,
+ unsigned col)
+{
+ SMB_ASSERT(list->cb->get_item_label);
+ return list->cb->get_item_label(row, col);
+}
+
+static const char *data_get_item_prefix(struct multilist *list, const void *row,
+ unsigned col)
+{
+ if (list->cb->get_item_prefix)
+ return list->cb->get_item_prefix(row, col);
+ return "";
+}
+
+static int multilist_free(struct multilist *list)
+{
+ if (list->pad) {
+ delwin(list->pad);
+ }
+
+ return 0;
+}
+
+struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window,
+ const struct multilist_accessors *cb,
+ unsigned ncol)
+{
+ struct multilist *list;
+
+ SMB_ASSERT(ncol > 0);
+
+ list = talloc_zero(ctx, struct multilist);
+ if (list == NULL) {
+ return NULL;
+ }
+ talloc_set_destructor(list, multilist_free);
+
+ list->cb = cb;
+ list->ncols = ncol;
+ list->columns = talloc_zero_array(list, struct multilist_column, ncol);
+ if (list->columns == NULL) {
+ talloc_free(list);
+ return NULL;
+ }
+ multilist_set_window(list, window);
+
+ return list;
+}
+
+struct multilist_column *multilist_column_config(struct multilist *list,
+ unsigned col)
+{
+ SMB_ASSERT(col < list->ncols);
+ return &list->columns[col];
+}
+
+static void put_padding(WINDOW *win, size_t col_width, size_t item_len)
+{
+ size_t amt;
+
+ SMB_ASSERT(item_len <= col_width);
+
+ amt = col_width - item_len;
+ while (amt--) {
+ waddch(win, ' ');
+ }
+}
+
+static void put_item(struct multilist *list, WINDOW *win, unsigned col,
+ const char *item, int attr)
+{
+ bool append_sep = true;
+ unsigned i;
+ size_t len;
+ struct multilist_column *col_info;
+ bool trim = false;
+
+ SMB_ASSERT(col < list->ncols);
+ SMB_ASSERT(item != NULL);
+
+ if (col == list->ncols - 1) {
+ append_sep = false;
+ }
+ col_info = &list->columns[col];
+
+ len = strlen(item);
+ if (len > col_info->width) {
+ len = col_info->width;
+ trim = true;
+ }
+
+ if (col_info->align_right) {
+ put_padding(win, col_info->width, len);
+ }
+ for (i = 0; i < len; ++i) {
+ if (i == len - 1 && trim) {
+ waddch(win, '~' | attr);
+ } else {
+ waddch(win, item[i] | attr);
+ }
+ }
+ if (!col_info->align_right) {
+ put_padding(win, col_info->width, len);
+ }
+
+ if (append_sep) {
+ waddch(win, ' ');
+ waddch(win, '|');
+ waddch(win, ' ');
+ }
+}
+
+static void put_header(struct multilist *list)
+{
+ unsigned col;
+ const char *header;
+
+ if (!list->cb->get_column_header) {
+ return;
+ }
+
+ wmove(list->window, 0, 0);
+ for (col = 0; col < list->ncols; ++col) {
+ header = data_get_column_header(list, col);
+ SMB_ASSERT(header != NULL);
+ put_item(list, list->window, col, header,
+ A_BOLD | COLOR_PAIR(PAIR_YELLOW_BLUE));
+ }
+}
+
+static WERROR put_data(struct multilist *list)
+{
+ const void *row;
+ int ypos;
+ unsigned col;
+ const char *prefix, *item;
+ char *tmp;
+
+ for (ypos = 0, row = data_get_first_row(list);
+ row != NULL;
+ row = data_get_next_row(list, row), ++ypos) {
+ wmove(list->pad, ypos, 0);
+ for (col = 0; col < list->ncols; ++col) {
+ prefix = data_get_item_prefix(list, row, col);
+ SMB_ASSERT(prefix != NULL);
+ item = data_get_item_label(list, row, col);
+ SMB_ASSERT(item != NULL);
+ tmp = talloc_asprintf(list, "%s%s", prefix, item);
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ put_item(list, list->pad, col, tmp, 0);
+ talloc_free(tmp);
+ }
+ }
+
+ return WERR_OK;
+}
+
+#define MIN_WIDTH 3
+static struct multilist_column *find_widest_column(struct multilist *list)
+{
+ unsigned col;
+ struct multilist_column *colp;
+
+ SMB_ASSERT(list->ncols > 0);
+ colp = &list->columns[0];
+
+ for (col = 1; col < list->ncols; ++col) {
+ if (list->columns[col].width > colp->width) {
+ colp = &list->columns[col];
+ }
+ }
+
+ if (colp->width < MIN_WIDTH) {
+ return NULL;
+ }
+
+ return colp;
+}
+
+static WERROR calc_column_widths(struct multilist *list)
+{
+ const void *row;
+ unsigned col;
+ size_t len;
+ const char *item;
+ size_t width, total_width, overflow;
+ struct multilist_column *colp;
+
+ /* calculate the maximum widths for each column */
+ for (col = 0; col < list->ncols; ++col) {
+ len = 0;
+ if (list->cb->get_column_header) {
+ item = data_get_column_header(list, col);
+ len = strlen(item);
+ }
+ list->columns[col].width = len;
+ }
+
+ for (row = data_get_first_row(list);
+ row != NULL;
+ row = data_get_next_row(list, row)) {
+ for (col = 0; col < list->ncols; ++col) {
+ item = data_get_item_prefix(list, row, col);
+ SMB_ASSERT(item != NULL);
+ len = strlen(item);
+
+ item = data_get_item_label(list, row, col);
+ SMB_ASSERT(item != NULL);
+ len += strlen(item);
+ if (len > list->columns[col].width) {
+ list->columns[col].width = len;
+ }
+ }
+ }
+
+ /* calculate row width */
+ for (width = 0, col = 0; col < list->ncols; ++col) {
+ width += list->columns[col].width;
+ }
+ /* width including column spacing and separations */
+ total_width = width + (list->ncols - 1) * 3;
+ /* if everything fits, we're done */
+ if (total_width <= list->window_width) {
+ return WERR_OK;
+ }
+
+ overflow = total_width - list->window_width;
+
+ /* attempt to trim as much as possible to fit all the columns to
+ the window */
+ while (overflow && (colp = find_widest_column(list))) {
+ colp->width--;
+ overflow--;
+ }
+
+ return WERR_OK;
+}
+
+static void highlight_current_row(struct multilist *list)
+{
+ mvwchgat(list->pad, list->cursor_row, 0, -1, A_REVERSE, 0, NULL);
+}
+
+static void unhighlight_current_row(struct multilist *list)
+{
+ mvwchgat(list->pad, list->cursor_row, 0, -1, A_NORMAL, 0, NULL);
+}
+
+const void *multilist_get_data(struct multilist *list)
+{
+ return list->data;
+}
+
+WERROR multilist_set_data(struct multilist *list, const void *data)
+{
+ WERROR rv;
+
+ SMB_ASSERT(list->window != NULL);
+ list->data = data;
+
+ calc_column_widths(list);
+
+ if (list->pad) {
+ delwin(list->pad);
+ }
+ /* construct a pad that is exactly the width of the window, and
+ as tall as required to fit all data rows. */
+ list->nrows = data_get_row_count(list);
+ list->pad = newpad(MAX(list->nrows, 1), list->window_width);
+ if (list->pad == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ /* add the column headers to the window and render all rows to
+ the pad. */
+ werase(list->window);
+ put_header(list);
+ rv = put_data(list);
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+
+ /* initialize the cursor */
+ list->start_row = 0;
+ list->cursor_row = 0;
+ list->current_row = data_get_first_row(list);
+ highlight_current_row(list);
+
+ return WERR_OK;
+}
+
+static int get_window_height(struct multilist *list)
+{
+ int height;
+
+ height = list->window_height;
+ if (list->cb->get_column_header) {
+ height--;
+ }
+
+ return height;
+}
+
+static void fix_start_row(struct multilist *list)
+{
+ int height;
+
+ /* adjust start_row so that the cursor appears on the screen */
+
+ height = get_window_height(list);
+ if (list->cursor_row < list->start_row) {
+ list->start_row = list->cursor_row;
+ } else if (list->cursor_row >= list->start_row + height) {
+ list->start_row = list->cursor_row - height + 1;
+ }
+ if (list->nrows > height && list->nrows - list->start_row < height) {
+ list->start_row = list->nrows - height;
+ }
+}
+
+WERROR multilist_set_window(struct multilist *list, WINDOW *window)
+{
+ int maxy, maxx;
+ bool rerender = false;
+
+ getmaxyx(window, maxy, maxx);
+
+ /* rerender pad if window width is different. */
+ if (list->data && maxx != list->window_width) {
+ rerender = true;
+ }
+
+ list->window = window;
+ list->window_width = maxx;
+ list->window_height = maxy;
+ list->start_row = 0;
+ if (rerender) {
+ const void *row = multilist_get_current_row(list);
+ WERROR rv = multilist_set_data(list, list->data);
+ if (W_ERROR_IS_OK(rv) && row) {
+ multilist_set_current_row(list, row);
+ }
+ return rv;
+ } else {
+ put_header(list);
+ fix_start_row(list);
+ }
+
+ return WERR_OK;
+}
+
+void multilist_refresh(struct multilist *list)
+{
+ int window_start_row, height;
+
+ if (list->nrows == 0) {
+ return;
+ }
+
+ /* copy from pad, starting at start_row, to the window, accounting
+ for the column header (if present). */
+ height = MIN(list->window_height, list->nrows);
+ window_start_row = 0;
+ if (list->cb->get_column_header) {
+ window_start_row = 1;
+ if (height < list->window_height) {
+ height++;
+ }
+ }
+ copywin(list->pad, list->window, list->start_row, 0,
+ window_start_row, 0, height - 1, list->window_width - 1,
+ false);
+}
+
+void multilist_driver(struct multilist *list, int c)
+{
+ unsigned page;
+ const void *tmp = NULL;
+
+ if (list->nrows == 0) {
+ return;
+ }
+
+ switch (c) {
+ case ML_CURSOR_UP:
+ if (list->cursor_row == 0) {
+ return;
+ }
+ unhighlight_current_row(list);
+ list->cursor_row--;
+ tmp = data_get_prev_row(list, list->current_row);
+ break;
+ case ML_CURSOR_DOWN:
+ if (list->cursor_row == list->nrows - 1) {
+ return;
+ }
+ unhighlight_current_row(list);
+ list->cursor_row++;
+ tmp = data_get_next_row(list, list->current_row);
+ break;
+ case ML_CURSOR_PGUP:
+ if (list->cursor_row == 0) {
+ return;
+ }
+ unhighlight_current_row(list);
+ page = get_window_height(list);
+ if (page > list->cursor_row) {
+ list->cursor_row = 0;
+ } else {
+ list->cursor_row -= page;
+ list->start_row -= page;
+ }
+ tmp = data_get_row_n(list, list->cursor_row);
+ break;
+ case ML_CURSOR_PGDN:
+ if (list->cursor_row == list->nrows - 1) {
+ return;
+ }
+ unhighlight_current_row(list);
+ page = get_window_height(list);
+ if (page > list->nrows - list->cursor_row - 1) {
+ list->cursor_row = list->nrows - 1;
+ } else {
+ list->cursor_row += page;
+ list->start_row += page;
+ }
+ tmp = data_get_row_n(list, list->cursor_row);
+ break;
+ case ML_CURSOR_HOME:
+ if (list->cursor_row == 0) {
+ return;
+ }
+ unhighlight_current_row(list);
+ list->cursor_row = 0;
+ tmp = data_get_row_n(list, list->cursor_row);
+ break;
+ case ML_CURSOR_END:
+ if (list->cursor_row == list->nrows - 1) {
+ return;
+ }
+ unhighlight_current_row(list);
+ list->cursor_row = list->nrows - 1;
+ tmp = data_get_row_n(list, list->cursor_row);
+ break;
+ }
+
+ SMB_ASSERT(tmp);
+ list->current_row = tmp;
+ highlight_current_row(list);
+ fix_start_row(list);
+}
+
+const void *multilist_get_current_row(struct multilist *list)
+{
+ return list->current_row;
+}
+
+void multilist_set_current_row(struct multilist *list, const void *row)
+{
+ unsigned i;
+ const void *tmp;
+
+ for (i = 0, tmp = data_get_first_row(list);
+ tmp != NULL;
+ ++i, tmp = data_get_next_row(list, tmp)) {
+ if (tmp == row) {
+ unhighlight_current_row(list);
+ list->cursor_row = i;
+ list->current_row = row;
+ highlight_current_row(list);
+ fix_start_row(list);
+ return;
+ }
+ }
+}
diff --git a/source3/utils/regedit_list.h b/source3/utils/regedit_list.h
new file mode 100644
index 0000000..abd6ffd
--- /dev/null
+++ b/source3/utils/regedit_list.h
@@ -0,0 +1,82 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REGEDIT_LIST_H_
+#define _REGEDIT_LIST_H_
+
+#include "includes.h"
+#include <ncurses.h>
+
+struct multilist_accessors {
+ /* (optional) return the column header for col */
+ const char *(*get_column_header)(const void *data, unsigned col);
+
+ /* return a pointer to the first row of data */
+ const void *(*get_first_row)(const void *data);
+
+ /* (optional) return a count of all data rows */
+ size_t (*get_row_count)(const void *data);
+
+ /* return the next row or NULL if there aren't any more */
+ const void *(*get_next_row)(const void *data, const void *row);
+
+ /* (optional) return the previous row or NULL if row is on top. */
+ const void *(*get_prev_row)(const void *data, const void *row);
+
+ /* (optional) return row n of data */
+ const void *(*get_row_n)(const void *data, size_t n);
+
+ /* return the label for row and col */
+ const char *(*get_item_label)(const void *row, unsigned col);
+
+ /* (optional) return a prefix string to be prepended to an item's
+ label. */
+ const char *(*get_item_prefix)(const void *row, unsigned col);
+};
+
+struct multilist_column {
+ size_t width;
+ unsigned int align_right:1;
+};
+
+struct multilist;
+
+struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window,
+ const struct multilist_accessors *cb,
+ unsigned ncol);
+struct multilist_column *multilist_column_config(struct multilist *list,
+ unsigned col);
+WERROR multilist_set_window(struct multilist *list, WINDOW *window);
+const void *multilist_get_data(struct multilist *list);
+WERROR multilist_set_data(struct multilist *list, const void *data);
+void multilist_refresh(struct multilist *list);
+
+enum {
+ ML_CURSOR_UP,
+ ML_CURSOR_DOWN,
+ ML_CURSOR_PGUP,
+ ML_CURSOR_PGDN,
+ ML_CURSOR_HOME,
+ ML_CURSOR_END
+};
+void multilist_driver(struct multilist *list, int c);
+const void *multilist_get_current_row(struct multilist *list);
+void multilist_set_current_row(struct multilist *list, const void *row);
+
+#endif
diff --git a/source3/utils/regedit_samba3.c b/source3/utils/regedit_samba3.c
new file mode 100644
index 0000000..a1f8b39
--- /dev/null
+++ b/source3/utils/regedit_samba3.c
@@ -0,0 +1,244 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* s3 registry backend, adapted from rpc backend */
+
+#include "includes.h"
+#include "lib/registry/registry.h"
+#include "regedit.h"
+
+struct samba3_key {
+ struct registry_key key;
+ struct samba3_registry_key s3key;
+};
+
+struct samba3_registry_context {
+ struct registry_context context;
+};
+
+static struct registry_operations reg_backend_s3;
+
+static struct {
+ uint32_t hkey;
+ const char *name;
+} known_hives[] = {
+ { HKEY_LOCAL_MACHINE, "HKLM" },
+ { HKEY_CURRENT_USER, "HKCU" },
+ { HKEY_CLASSES_ROOT, "HKCR" },
+ { HKEY_PERFORMANCE_DATA, "HKPD" },
+ { HKEY_USERS, "HKU" },
+ { HKEY_DYN_DATA, "HKDD" },
+ { HKEY_CURRENT_CONFIG, "HKCC" },
+ { 0, NULL }
+};
+
+static WERROR samba3_get_predefined_key(struct registry_context *ctx,
+ uint32_t hkey_type,
+ struct registry_key **k)
+{
+ int n;
+ const char *name;
+ struct samba3_key *mykeydata;
+
+ *k = NULL;
+ name = NULL;
+
+ for(n = 0; known_hives[n].hkey; n++) {
+ if(known_hives[n].hkey == hkey_type) {
+ name = known_hives[n].name;
+ break;
+ }
+ }
+
+ if (name == NULL) {
+ DEBUG(1, ("No such hive %d\n", hkey_type));
+ return WERR_NO_MORE_ITEMS;
+ }
+
+ mykeydata = talloc_zero(ctx, struct samba3_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = ctx;
+ *k = (struct registry_key *)mykeydata;
+
+ return reg_openhive_wrap(ctx, name, &mykeydata->s3key);
+}
+
+static WERROR samba3_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h,
+ const char *name, struct registry_key **key)
+{
+ struct samba3_key *parentkeydata, *mykeydata;
+
+ parentkeydata = talloc_get_type(h, struct samba3_key);
+
+ mykeydata = talloc_zero(mem_ctx, struct samba3_key);
+ W_ERROR_HAVE_NO_MEMORY(mykeydata);
+ mykeydata->key.context = h->context;
+ *key = (struct registry_key *)mykeydata;
+
+ return reg_openkey_wrap(mem_ctx, &parentkeydata->s3key,
+ name, &mykeydata->s3key);
+}
+
+static WERROR samba3_get_value_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct samba3_key *mykeydata;
+
+ mykeydata = talloc_get_type(parent, struct samba3_key);
+
+ return reg_enumvalue_wrap(mem_ctx, &mykeydata->s3key, n,
+ discard_const(value_name), type, data);
+}
+
+static WERROR samba3_get_value_by_name(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ const char *value_name,
+ uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct samba3_key *mykeydata;
+
+ mykeydata = talloc_get_type(parent, struct samba3_key);
+
+ return reg_queryvalue_wrap(mem_ctx, &mykeydata->s3key,
+ value_name, type, data);
+}
+
+static WERROR samba3_get_subkey_by_index(TALLOC_CTX *mem_ctx,
+ const struct registry_key *parent,
+ uint32_t n,
+ const char **name,
+ const char **keyclass,
+ NTTIME *last_changed_time)
+{
+ struct samba3_key *mykeydata;
+
+ mykeydata = talloc_get_type(parent, struct samba3_key);
+
+ *keyclass = NULL;
+
+ return reg_enumkey_wrap(mem_ctx, &mykeydata->s3key, n,
+ discard_const(name), last_changed_time);
+}
+
+static WERROR samba3_add_key(TALLOC_CTX *mem_ctx,
+ struct registry_key *parent, const char *path,
+ const char *key_class,
+ struct security_descriptor *sec,
+ struct registry_key **key)
+{
+ struct samba3_key *parentkd;
+ struct samba3_key *newkd;
+
+ parentkd = talloc_get_type(parent, struct samba3_key);
+ newkd = talloc_zero(mem_ctx, struct samba3_key);
+
+ W_ERROR_HAVE_NO_MEMORY(newkd);
+ newkd->key.context = parent->context;
+ *key = (struct registry_key *)newkd;
+
+ return reg_createkey_wrap(mem_ctx, &parentkd->s3key, path,
+ &newkd->s3key);
+}
+
+static WERROR samba3_del_key(TALLOC_CTX *mem_ctx, struct registry_key *parent,
+ const char *name)
+{
+ struct samba3_key *mykeydata;
+
+ mykeydata = talloc_get_type(parent, struct samba3_key);
+
+ return reg_deletekey_wrap(&mykeydata->s3key, name);
+}
+
+static WERROR samba3_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key,
+ const char *name)
+{
+ struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key);
+
+ return reg_deletevalue_wrap(&mykeydata->s3key, name);
+}
+
+static WERROR samba3_set_value(struct registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key);
+
+ return reg_setvalue_wrap(&mykeydata->s3key, name, type, data);
+}
+
+static WERROR samba3_get_info(TALLOC_CTX *mem_ctx,
+ const struct registry_key *key,
+ const char **classname,
+ uint32_t *num_subkeys,
+ uint32_t *num_values,
+ NTTIME *last_changed_time,
+ uint32_t *max_subkeylen,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize)
+{
+ struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key);
+ uint32_t max_subkeysize, secdescsize;
+
+ return reg_queryinfokey_wrap(&mykeydata->s3key, num_subkeys,
+ max_subkeylen, &max_subkeysize,
+ num_values, max_valnamelen,
+ max_valbufsize, &secdescsize,
+ last_changed_time);
+}
+
+static struct registry_operations reg_backend_s3 = {
+ .name = "samba3",
+ .open_key = samba3_open_key,
+ .get_predefined_key = samba3_get_predefined_key,
+ .enum_key = samba3_get_subkey_by_index,
+ .enum_value = samba3_get_value_by_index,
+ .get_value = samba3_get_value_by_name,
+ .set_value = samba3_set_value,
+ .delete_value = samba3_del_value,
+ .create_key = samba3_add_key,
+ .delete_key = samba3_del_key,
+ .get_key_info = samba3_get_info,
+};
+
+WERROR reg_open_samba3(TALLOC_CTX *mem_ctx, struct registry_context **ctx)
+{
+ WERROR rv;
+ struct samba3_registry_context *rctx;
+
+ /* initialize s3 registry */
+ rv = reg_init_wrap();
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+
+ rctx = talloc_zero(mem_ctx, struct samba3_registry_context);
+ if (rctx == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ *ctx = (struct registry_context *)rctx;
+ (*ctx)->ops = &reg_backend_s3;
+
+ return WERR_OK;
+}
diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c
new file mode 100644
index 0000000..a3df94d
--- /dev/null
+++ b/source3/utils/regedit_treeview.c
@@ -0,0 +1,705 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "regedit_treeview.h"
+#include "regedit_list.h"
+#include "lib/registry/registry.h"
+
+#define HEADING_X 3
+
+static int tree_node_free(struct tree_node *node)
+{
+ DEBUG(9, ("tree_node_free('%s', %p)\n", node->name, node));
+ return 0;
+}
+
+struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent,
+ const char *name, struct registry_key *key)
+{
+ struct tree_node *node;
+
+ node = talloc_zero(ctx, struct tree_node);
+ if (!node) {
+ return NULL;
+ }
+ talloc_set_destructor(node, tree_node_free);
+ DEBUG(9, ("tree_node_new('%s', %p)\n", name, node));
+
+ node->name = talloc_strdup(node, name);
+ if (!node->name) {
+ talloc_free(node);
+ return NULL;
+ }
+
+ if (key) {
+ node->key = talloc_steal(node, key);
+ }
+
+ if (parent) {
+ /* Check if this node is the first descendant of parent. */
+ if (!parent->child_head) {
+ parent->child_head = node;
+ }
+ node->parent = parent;
+ }
+
+ return node;
+}
+
+/* prepare a root node with all available hives as children */
+struct tree_node *tree_node_new_root(TALLOC_CTX *ctx,
+ struct registry_context *regctx)
+{
+ const char *hives[] = {
+ "HKEY_CLASSES_ROOT",
+ "HKEY_CURRENT_USER",
+ "HKEY_LOCAL_MACHINE",
+ "HKEY_PERFORMANCE_DATA",
+ "HKEY_USERS",
+ "HKEY_CURRENT_CONFIG",
+ "HKEY_DYN_DATA",
+ "HKEY_PERFORMANCE_TEXT",
+ "HKEY_PERFORMANCE_NLSTEXT",
+ NULL
+ };
+ struct tree_node *root, *prev, *node;
+ struct registry_key *key;
+ WERROR rv;
+ size_t i;
+
+ root = tree_node_new(ctx, NULL, "ROOT", NULL);
+ if (root == NULL) {
+ return NULL;
+ }
+ prev = NULL;
+
+ for (i = 0; hives[i] != NULL; ++i) {
+ rv = reg_get_predefined_key_by_name(regctx, hives[i], &key);
+ if (!W_ERROR_IS_OK(rv)) {
+ continue;
+ }
+
+ node = tree_node_new(root, root, hives[i], key);
+ if (node == NULL) {
+ return NULL;
+ }
+ if (prev) {
+ tree_node_append(prev, node);
+ }
+ prev = node;
+ }
+
+ return root;
+}
+
+void tree_node_append(struct tree_node *left, struct tree_node *right)
+{
+ if (left->next) {
+ right->next = left->next;
+ left->next->previous = right;
+ }
+ left->next = right;
+ right->previous = left;
+}
+
+void tree_node_append_last(struct tree_node *list, struct tree_node *node)
+{
+ tree_node_append(tree_node_last(list), node);
+}
+
+struct tree_node *tree_node_pop(struct tree_node **plist)
+{
+ struct tree_node *node;
+
+ node = *plist;
+
+ if (node == NULL)
+ return NULL;
+
+ *plist = node->previous;
+ if (*plist == NULL) {
+ *plist = node->next;
+ }
+ if (node->previous) {
+ node->previous->next = node->next;
+ }
+ if (node->next) {
+ node->next->previous = node->previous;
+ }
+ if (node->parent && node->parent->child_head == node) {
+ node->parent->child_head = node->next;
+ }
+ node->next = NULL;
+ node->previous = NULL;
+
+ return node;
+}
+
+struct tree_node *tree_node_first(struct tree_node *list)
+{
+ /* Grab the first node in this list from the parent if available. */
+ if (list->parent) {
+ return list->parent->child_head;
+ }
+
+ while (list && list->previous) {
+ list = list->previous;
+ }
+
+ return list;
+}
+
+struct tree_node *tree_node_last(struct tree_node *list)
+{
+ while (list && list->next) {
+ list = list->next;
+ }
+
+ return list;
+}
+
+static uint32_t get_num_subkeys(struct tree_node *node)
+{
+ const char *classname;
+ uint32_t num_subkeys;
+ uint32_t num_values;
+ NTTIME last_change_time;
+ uint32_t max_subkeynamelen;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ WERROR rv;
+
+ rv = reg_key_get_info(node, node->key, &classname, &num_subkeys,
+ &num_values, &last_change_time,
+ &max_subkeynamelen, &max_valnamelen,
+ &max_valbufsize);
+
+ if (W_ERROR_IS_OK(rv)) {
+ return num_subkeys;
+ }
+
+ return 0;
+}
+
+WERROR tree_node_reopen_key(struct registry_context *ctx,
+ struct tree_node *node)
+{
+ SMB_ASSERT(node->parent != NULL);
+ SMB_ASSERT(node->name != NULL);
+ TALLOC_FREE(node->key);
+
+ if (tree_node_is_top_level(node)) {
+ WERROR rv;
+ struct registry_key *key;
+ rv = reg_get_predefined_key_by_name(ctx, node->name, &key);
+ if (W_ERROR_IS_OK(rv)) {
+ node->key = talloc_steal(node, key);
+ }
+ return rv;
+ }
+
+ return reg_open_key(node, node->parent->key, node->name, &node->key);
+}
+
+bool tree_node_has_children(struct tree_node *node)
+{
+ if (node->child_head) {
+ return true;
+ }
+
+ return get_num_subkeys(node) > 0;
+}
+
+static int node_cmp(struct tree_node **a, struct tree_node **b)
+{
+ return strcmp((*a)->name, (*b)->name);
+}
+
+void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node)
+{
+ list = tree_node_first(list);
+
+ if (node_cmp(&list, &node) >= 0) {
+ tree_node_append(node, list);
+ if (list->parent) {
+ list->parent->child_head = node;
+ }
+ return;
+ }
+
+ while (list->next && node_cmp(&list->next, &node) < 0) {
+ list = list->next;
+ }
+
+ tree_node_append(list, node);
+}
+
+WERROR tree_node_load_children(struct tree_node *node)
+{
+ struct registry_key *key;
+ const char *reg_key_name, *klass;
+ NTTIME modified;
+ uint32_t i, nsubkeys, count;
+ WERROR rv;
+ struct tree_node *prev, **array;
+
+ /* does this node already have it's children loaded? */
+ if (node->child_head)
+ return WERR_OK;
+
+ nsubkeys = get_num_subkeys(node);
+ if (nsubkeys == 0)
+ return WERR_OK;
+
+ array = talloc_zero_array(node, struct tree_node *, nsubkeys);
+ if (array == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (count = 0, i = 0; i < nsubkeys; ++i) {
+ rv = reg_key_get_subkey_by_index(node, node->key, i,
+ &reg_key_name, &klass,
+ &modified);
+ if (!W_ERROR_IS_OK(rv)) {
+ goto finish;
+ }
+
+ rv = reg_open_key(node, node->key, reg_key_name, &key);
+ if (!W_ERROR_IS_OK(rv)) {
+ continue;
+ }
+
+ array[count] = tree_node_new(array, node, reg_key_name, key);
+ if (array[count] == NULL) {
+ rv = WERR_NOT_ENOUGH_MEMORY;
+ goto finish;
+ }
+ ++count;
+ }
+
+ if (count) {
+ TYPESAFE_QSORT(array, count, node_cmp);
+
+ for (i = 1, prev = array[0]; i < count; ++i) {
+ talloc_steal(node, array[i]);
+ tree_node_append(prev, array[i]);
+ prev = array[i];
+ }
+ node->child_head = talloc_steal(node, array[0]);
+
+ rv = WERR_OK;
+ }
+
+finish:
+ talloc_free(array);
+
+ return rv;
+}
+
+static WERROR next_depth_first(struct tree_node **node)
+{
+ WERROR rv = WERR_OK;
+
+ SMB_ASSERT(node != NULL && *node != NULL);
+
+ if (tree_node_has_children(*node)) {
+ /* 1. If the node has children, go to the first one. */
+ rv = tree_node_load_children(*node);
+ if (W_ERROR_IS_OK(rv)) {
+ SMB_ASSERT((*node)->child_head != NULL);
+ *node = (*node)->child_head;
+ }
+ } else if ((*node)->next) {
+ /* 2. If there's a node directly after this one, go there */
+ *node = (*node)->next;
+ } else {
+ /* 3. Otherwise, go up the hierarchy to find the next one */
+ do {
+ *node = (*node)->parent;
+ if (*node && (*node)->next) {
+ *node = (*node)->next;
+ break;
+ }
+ } while (*node);
+ }
+
+ return rv;
+}
+
+static WERROR prev_depth_first(struct tree_node **node)
+{
+ WERROR rv = WERR_OK;
+
+ SMB_ASSERT(node != NULL && *node != NULL);
+
+ if ((*node)->previous) {
+ *node = (*node)->previous;
+ while (tree_node_has_children(*node)) {
+ rv = tree_node_load_children(*node);
+ if (W_ERROR_IS_OK(rv)) {
+ SMB_ASSERT((*node)->child_head != NULL);
+ *node = tree_node_last((*node)->child_head);
+ }
+ }
+ } else if (!tree_node_is_top_level(*node)) {
+ *node = (*node)->parent;
+ } else {
+ *node = NULL;
+ }
+
+ return rv;
+}
+
+bool tree_node_next(struct tree_node **node, bool depth, WERROR *err)
+{
+ *err = WERR_OK;
+
+ if (*node == NULL) {
+ return false;
+ }
+
+ if (depth) {
+ *err = next_depth_first(node);
+ } else {
+ *node = (*node)->next;
+ }
+
+ return *node != NULL && W_ERROR_IS_OK(*err);
+}
+
+bool tree_node_prev(struct tree_node **node, bool depth, WERROR *err)
+{
+ *err = WERR_OK;
+
+ if (*node == NULL) {
+ return false;
+ }
+
+ if (depth) {
+ *err = prev_depth_first(node);
+ } else {
+ *node = (*node)->previous;
+ }
+
+ return *node != NULL && W_ERROR_IS_OK(*err);
+}
+
+void tree_view_clear(struct tree_view *view)
+{
+ multilist_set_data(view->list, NULL);
+}
+
+WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root)
+{
+ multilist_set_data(view->list, NULL);
+ talloc_free(view->root);
+ view->root = root;
+ return tree_view_update(view, root->child_head);
+}
+
+WERROR tree_view_set_path(struct tree_view *view, const char **path)
+{
+ struct tree_node *top, *node;
+ WERROR rv;
+
+ top = view->root->child_head;
+ while (*path) {
+ for (node = top; node != NULL; node = node->next) {
+ if (strcmp(*path, node->name) == 0) {
+ if (path[1] && tree_node_has_children(node)) {
+ rv = tree_node_load_children(node);
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+ SMB_ASSERT(node->child_head);
+ top = node->child_head;
+ break;
+ } else {
+ tree_view_update(view, top);
+ tree_view_set_current_node(view, node);
+ return WERR_OK;
+ }
+ }
+ }
+ ++path;
+ }
+
+ return WERR_OK;
+}
+
+WERROR tree_view_update(struct tree_view *view, struct tree_node *list)
+{
+ WERROR rv;
+
+ rv = multilist_set_data(view->list, list);
+ if (W_ERROR_IS_OK(rv)) {
+ multilist_refresh(view->list);
+ }
+
+ return rv;
+}
+
+/* is this node in the current level? */
+bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node)
+{
+ const struct tree_node *first;
+
+ first = multilist_get_data(view->list);
+
+ return first && first->parent == node->parent;
+}
+
+void tree_view_set_current_node(struct tree_view *view, struct tree_node *node)
+{
+ multilist_set_current_row(view->list, node);
+}
+
+struct tree_node *tree_view_get_current_node(struct tree_view *view)
+{
+ const void *row = multilist_get_current_row(view->list);
+ return talloc_get_type_abort(row, struct tree_node);
+}
+
+void tree_view_driver(struct tree_view *view, int c)
+{
+ multilist_driver(view->list, c);
+}
+
+void tree_view_set_selected(struct tree_view *view, bool reverse)
+{
+ attr_t attr = A_NORMAL;
+
+ if (reverse) {
+ attr = A_REVERSE;
+ }
+ mvwchgat(view->window, 0, HEADING_X, 3, attr, 0, NULL);
+}
+
+void tree_view_show(struct tree_view *view)
+{
+ multilist_refresh(view->list);
+ touchwin(view->window);
+ wnoutrefresh(view->window);
+ wnoutrefresh(view->sub);
+}
+
+static int tree_view_free(struct tree_view *view)
+{
+ if (view->panel) {
+ del_panel(view->panel);
+ }
+ if (view->sub) {
+ delwin(view->sub);
+ }
+ if (view->window) {
+ delwin(view->window);
+ }
+
+ return 0;
+}
+
+static const char *tv_get_column_header(const void *data, unsigned col)
+{
+ SMB_ASSERT(col == 0);
+ return "Name";
+}
+
+static const void *tv_get_first_row(const void *data)
+{
+ if (data == NULL) {
+ return NULL;
+ }
+
+ return talloc_get_type_abort(data, struct tree_node);
+}
+
+static const void *tv_get_next_row(const void *data, const void *row)
+{
+ const struct tree_node *node;
+ SMB_ASSERT(row != NULL);
+ node = talloc_get_type_abort(row, struct tree_node);
+ return node->next;
+}
+
+static const void *tv_get_prev_row(const void *data, const void *row)
+{
+ const struct tree_node *node;
+ SMB_ASSERT(row != NULL);
+ node = talloc_get_type_abort(row, struct tree_node);
+ return node->previous;
+}
+
+static const char *tv_get_item_prefix(const void *row, unsigned col)
+{
+ struct tree_node *node;
+
+ SMB_ASSERT(col == 0);
+ SMB_ASSERT(row != NULL);
+ node = talloc_get_type_abort(row, struct tree_node);
+ if (tree_node_has_children(node)) {
+ return "+";
+ }
+ return " ";
+}
+
+static const char *tv_get_item_label(const void *row, unsigned col)
+{
+ const struct tree_node *node;
+ SMB_ASSERT(col == 0);
+ SMB_ASSERT(row != NULL);
+ node = talloc_get_type_abort(row, struct tree_node);
+ return node->name;
+}
+
+static struct multilist_accessors tv_accessors = {
+ .get_column_header = tv_get_column_header,
+ .get_first_row = tv_get_first_row,
+ .get_next_row = tv_get_next_row,
+ .get_prev_row = tv_get_prev_row,
+ .get_item_prefix = tv_get_item_prefix,
+ .get_item_label = tv_get_item_label
+};
+
+struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root,
+ int nlines, int ncols, int begin_y,
+ int begin_x)
+{
+ struct tree_view *view;
+
+ view = talloc_zero(ctx, struct tree_view);
+ if (view == NULL) {
+ return NULL;
+ }
+
+ talloc_set_destructor(view, tree_view_free);
+
+ view->window = newwin(nlines, ncols, begin_y, begin_x);
+ if (view->window == NULL) {
+ goto fail;
+ }
+ view->sub = subwin(view->window, nlines - 2, ncols - 2,
+ begin_y + 1, begin_x + 1);
+ if (view->sub == NULL) {
+ goto fail;
+ }
+ box(view->window, 0, 0);
+ mvwprintw(view->window, 0, HEADING_X, "Key");
+
+ view->panel = new_panel(view->window);
+ if (view->panel == NULL) {
+ goto fail;
+ }
+ view->root = root;
+
+ view->list = multilist_new(view, view->sub, &tv_accessors, 1);
+ if (view->list == NULL) {
+ goto fail;
+ }
+ tree_view_update(view, root->child_head);
+
+ return view;
+
+fail:
+ talloc_free(view);
+
+ return NULL;
+}
+
+void tree_view_resize(struct tree_view *view, int nlines, int ncols,
+ int begin_y, int begin_x)
+{
+ WINDOW *nwin, *nsub;
+
+ nwin = newwin(nlines, ncols, begin_y, begin_x);
+ if (nwin == NULL) {
+ return;
+ }
+ nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1);
+ if (nsub == NULL) {
+ delwin(nwin);
+ return;
+ }
+ replace_panel(view->panel, nwin);
+ delwin(view->sub);
+ delwin(view->window);
+ view->window = nwin;
+ view->sub = nsub;
+ box(view->window, 0, 0);
+ mvwprintw(view->window, 0, HEADING_X, "Key");
+ multilist_set_window(view->list, view->sub);
+ tree_view_show(view);
+}
+
+const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node)
+{
+ const char **array;
+ size_t nitems, idx;
+ struct tree_node *p;
+
+ for (nitems = 0, p = node; !tree_node_is_root(p); p = p->parent) {
+ ++nitems;
+ }
+
+ array = talloc_zero_array(ctx, const char *, nitems + 1);
+ if (array == NULL) {
+ return NULL;
+ }
+
+ for (idx = nitems - 1, p = node;
+ !tree_node_is_root(p);
+ p = p->parent, --idx) {
+ array[idx] = talloc_strdup(array, p->name);
+ if (array[idx] == NULL) {
+ talloc_free(discard_const(array));
+ return NULL;
+ }
+ }
+
+ return array;
+}
+
+/* print the path of node to label */
+size_t tree_node_print_path(WINDOW *label, struct tree_node *node)
+{
+ size_t len = 1;
+ const char **path;
+ TALLOC_CTX *frame;
+
+ if (node == NULL)
+ return 0;
+
+ werase(label);
+ wprintw(label, "/");
+
+ if (tree_node_is_top_level(node))
+ return 0;
+
+ frame = talloc_stackframe();
+ path = tree_node_get_path(frame, node->parent);
+
+ while (*path) {
+ len += strlen(*path) + 1;
+ wprintw(label, "%s/", *path);
+ ++path;
+ }
+
+ talloc_free(frame);
+
+ return len;
+}
diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h
new file mode 100644
index 0000000..4b892bb
--- /dev/null
+++ b/source3/utils/regedit_treeview.h
@@ -0,0 +1,89 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REGEDIT_TREEVIEW_H_
+#define _REGEDIT_TREEVIEW_H_
+
+#include "includes.h"
+#include <ncurses.h>
+#include <panel.h>
+
+struct registry_key;
+
+struct tree_node {
+
+ char *name;
+ struct registry_key *key;
+
+ struct tree_node *parent;
+ struct tree_node *child_head;
+ struct tree_node *previous;
+ struct tree_node *next;
+};
+
+struct multilist;
+
+struct tree_view {
+
+ struct tree_node *root;
+ WINDOW *window;
+ WINDOW *sub;
+ PANEL *panel;
+ struct multilist *list;
+};
+
+struct registry_context;
+
+struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent,
+ const char *name, struct registry_key *key);
+struct tree_node *tree_node_new_root(TALLOC_CTX *ctx,
+ struct registry_context *regctx);
+#define tree_node_is_root(node) ((node)->key == NULL)
+#define tree_node_is_top_level(node) tree_node_is_root((node)->parent)
+void tree_node_append(struct tree_node *left, struct tree_node *right);
+struct tree_node *tree_node_pop(struct tree_node **plist);
+struct tree_node *tree_node_first(struct tree_node *list);
+struct tree_node *tree_node_last(struct tree_node *list);
+bool tree_node_next(struct tree_node **node, bool depth, WERROR *err);
+bool tree_node_prev(struct tree_node **node, bool depth, WERROR *err);
+void tree_node_append_last(struct tree_node *list, struct tree_node *node);
+size_t tree_node_print_path(WINDOW *label, struct tree_node *node);
+const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node);
+struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root,
+ int nlines, int ncols,
+ int begin_y, int begin_x);
+void tree_view_set_selected(struct tree_view *view, bool select);
+void tree_view_resize(struct tree_view *view, int nlines, int ncols,
+ int begin_y, int begin_x);
+void tree_view_show(struct tree_view *view);
+void tree_view_clear(struct tree_view *view);
+WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root);
+WERROR tree_view_set_path(struct tree_view *view, const char **path);
+WERROR tree_view_update(struct tree_view *view, struct tree_node *list);
+WERROR tree_node_reopen_key(struct registry_context *ctx,
+ struct tree_node *node);
+bool tree_node_has_children(struct tree_node *node);
+WERROR tree_node_load_children(struct tree_node *node);
+void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node);
+bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node);
+void tree_view_set_current_node(struct tree_view *view, struct tree_node *node);
+struct tree_node *tree_view_get_current_node(struct tree_view *view);
+void tree_view_driver(struct tree_view *view, int c);
+
+#endif
diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c
new file mode 100644
index 0000000..78ea3fa
--- /dev/null
+++ b/source3/utils/regedit_valuelist.c
@@ -0,0 +1,496 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "regedit.h"
+#include "regedit_valuelist.h"
+#include "regedit_list.h"
+#include "lib/registry/registry.h"
+
+#define HEADING_X 3
+
+static int value_list_free(struct value_list *vl)
+{
+ if (vl->panel) {
+ del_panel(vl->panel);
+ }
+ if (vl->sub) {
+ delwin(vl->sub);
+ }
+ if (vl->window) {
+ delwin(vl->window);
+ }
+
+ return 0;
+}
+
+static const char *vl_get_column_header(const void *data, unsigned col)
+{
+ switch (col) {
+ case 0:
+ return "Name";
+ case 1:
+ return "Type";
+ case 2:
+ return "Data";
+ }
+
+ return "???";
+}
+
+static const void *vl_get_first_row(const void *data)
+{
+ const struct value_list *vl;
+
+ if (data) {
+ vl = talloc_get_type_abort(data, struct value_list);
+ if (vl->nvalues) {
+ return &vl->values[0];
+ }
+ }
+
+ return NULL;
+}
+
+static const void *vl_get_next_row(const void *data, const void *row)
+{
+ const struct value_list *vl;
+ const struct value_item *value = row;
+
+ SMB_ASSERT(data != NULL);
+ SMB_ASSERT(value != NULL);
+ vl = talloc_get_type_abort(data, struct value_list);
+ if (value == &vl->values[vl->nvalues - 1]) {
+ return NULL;
+ }
+
+ return value + 1;
+}
+
+static const void *vl_get_prev_row(const void *data, const void *row)
+{
+ const struct value_list *vl;
+ const struct value_item *value = row;
+
+ SMB_ASSERT(data != NULL);
+ SMB_ASSERT(value != NULL);
+ vl = talloc_get_type_abort(data, struct value_list);
+ if (value == &vl->values[0]) {
+ return NULL;
+ }
+
+ return value - 1;
+}
+
+static const char *vl_get_item_label(const void *row, unsigned col)
+{
+ const struct value_item *value = row;
+
+ SMB_ASSERT(value != NULL);
+ SMB_ASSERT(value->value_name != NULL);
+ switch (col) {
+ case 0:
+ return value->value_name;
+ case 1:
+ return str_regtype(value->type);
+ case 2:
+ if (value->value) {
+ return value->value;
+ }
+ return "";
+ }
+
+ return "???";
+}
+
+static struct multilist_accessors vl_accessors = {
+ .get_column_header = vl_get_column_header,
+ .get_first_row = vl_get_first_row,
+ .get_next_row = vl_get_next_row,
+ .get_prev_row = vl_get_prev_row,
+ .get_item_label = vl_get_item_label
+};
+
+struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
+ int begin_y, int begin_x)
+{
+ struct value_list *vl;
+
+ vl = talloc_zero(ctx, struct value_list);
+ if (vl == NULL) {
+ return NULL;
+ }
+
+ talloc_set_destructor(vl, value_list_free);
+
+ vl->window = newwin(nlines, ncols, begin_y, begin_x);
+ if (vl->window == NULL) {
+ goto fail;
+ }
+ vl->sub = subwin(vl->window, nlines - 2, ncols - 2,
+ begin_y + 1, begin_x + 1);
+ if (vl->sub == NULL) {
+ goto fail;
+ }
+ box(vl->window, 0, 0);
+ mvwprintw(vl->window, 0, HEADING_X, "Value");
+
+ vl->panel = new_panel(vl->window);
+ if (vl->panel == NULL) {
+ goto fail;
+ }
+
+ vl->list = multilist_new(vl, vl->sub, &vl_accessors, 3);
+ if (vl->list == NULL) {
+ goto fail;
+ }
+
+ return vl;
+
+fail:
+ talloc_free(vl);
+
+ return NULL;
+}
+
+void value_list_set_selected(struct value_list *vl, bool reverse)
+{
+ attr_t attr = A_NORMAL;
+
+ if (reverse) {
+ attr = A_REVERSE;
+ }
+ mvwchgat(vl->window, 0, HEADING_X, 5, attr, 0, NULL);
+}
+
+void value_list_resize(struct value_list *vl, int nlines, int ncols,
+ int begin_y, int begin_x)
+{
+ WINDOW *nwin, *nsub;
+
+ nwin = newwin(nlines, ncols, begin_y, begin_x);
+ if (nwin == NULL) {
+ return;
+ }
+ nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1);
+ if (nsub == NULL) {
+ delwin(nwin);
+ return;
+ }
+ replace_panel(vl->panel, nwin);
+ delwin(vl->sub);
+ delwin(vl->window);
+ vl->window = nwin;
+ vl->sub = nsub;
+ box(vl->window, 0, 0);
+ mvwprintw(vl->window, 0, HEADING_X, "Value");
+ multilist_set_window(vl->list, vl->sub);
+ value_list_show(vl);
+}
+
+static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key)
+{
+ const char *classname;
+ uint32_t num_subkeys;
+ uint32_t num_values;
+ NTTIME last_change_time;
+ uint32_t max_subkeynamelen;
+ uint32_t max_valnamelen;
+ uint32_t max_valbufsize;
+ WERROR rv;
+
+ rv = reg_key_get_info(ctx, key, &classname, &num_subkeys,
+ &num_values, &last_change_time,
+ &max_subkeynamelen, &max_valnamelen,
+ &max_valbufsize);
+
+ if (W_ERROR_IS_OK(rv)) {
+ return num_values;
+ }
+
+ return 0;
+}
+
+void value_list_show(struct value_list *vl)
+{
+ multilist_refresh(vl->list);
+ touchwin(vl->window);
+ wnoutrefresh(vl->window);
+ wnoutrefresh(vl->sub);
+}
+
+static bool string_is_printable(const char *s)
+{
+ const char *p;
+
+ for (p = s; *p; ++p) {
+ if (!isprint(*p)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem)
+{
+ char *tmp = NULL;
+
+/* This is adapted from print_registry_value() in net_registry_util.c */
+
+ switch(vitem->type) {
+ case REG_DWORD: {
+ uint32_t v = 0;
+ if (vitem->data.length >= 4) {
+ v = IVAL(vitem->data.data, 0);
+ }
+ tmp = talloc_asprintf(ctx, "0x%08x (%u)", v, v);
+ break;
+ }
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ const char *s;
+
+ if (!pull_reg_sz(ctx, &vitem->data, &s)) {
+ break;
+ }
+ vitem->unprintable = !string_is_printable(s);
+ if (vitem->unprintable) {
+ tmp = talloc_asprintf(ctx, "(unprintable)");
+ } else {
+ tmp = talloc_asprintf(ctx, "%s", s);
+ }
+ break;
+ }
+ case REG_MULTI_SZ: {
+ size_t i, len;
+ const char **a;
+ const char *val;
+
+ if (!pull_reg_multi_sz(ctx, &vitem->data, &a)) {
+ break;
+ }
+ for (len = 0; a[len] != NULL; ++len) {
+ }
+ tmp = talloc_asprintf(ctx, "(%u) ", (unsigned)len);
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ for (i = 0; i < len; ++i) {
+ if (!string_is_printable(a[i])) {
+ val = "(unprintable)";
+ vitem->unprintable = true;
+ } else {
+ val = a[i];
+ }
+ if (i == len - 1) {
+ tmp = talloc_asprintf_append(tmp,
+ "[%u]=\"%s\"",
+ (unsigned)i, val);
+ } else {
+ tmp = talloc_asprintf_append(tmp,
+ "[%u]=\"%s\", ",
+ (unsigned)i, val);
+ }
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+ }
+ break;
+ }
+ case REG_BINARY:
+ tmp = talloc_asprintf(ctx, "(%d bytes)",
+ (int)vitem->data.length);
+ break;
+ default:
+ tmp = talloc_asprintf(ctx, "(unknown)");
+ break;
+ }
+
+ if (tmp == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ vitem->value = tmp;
+
+ return WERR_OK;
+}
+
+static int vitem_cmp(struct value_item *a, struct value_item *b)
+{
+ return strcmp(a->value_name, b->value_name);
+}
+
+/* load only the value names into memory to enable searching */
+WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key)
+{
+ uint32_t nvalues;
+ uint32_t idx;
+ struct value_item *vitem, *new_items;
+ WERROR rv;
+
+ multilist_set_data(vl->list, NULL);
+ vl->nvalues = 0;
+ TALLOC_FREE(vl->values);
+
+ nvalues = get_num_values(vl, key);
+ if (nvalues == 0) {
+ return WERR_OK;
+ }
+
+ new_items = talloc_zero_array(vl, struct value_item, nvalues);
+ if (new_items == NULL) {
+ return WERR_NOT_ENOUGH_MEMORY;
+ }
+
+ for (idx = 0; idx < nvalues; ++idx) {
+ vitem = &new_items[idx];
+ rv = reg_key_get_value_by_index(new_items, key, idx,
+ &vitem->value_name,
+ &vitem->type,
+ &vitem->data);
+ if (!W_ERROR_IS_OK(rv)) {
+ talloc_free(new_items);
+ return rv;
+ }
+ }
+
+ TYPESAFE_QSORT(new_items, nvalues, vitem_cmp);
+ vl->nvalues = nvalues;
+ vl->values = new_items;
+
+ return rv;
+}
+
+/* sync up the UI with the list */
+WERROR value_list_sync(struct value_list *vl)
+{
+ uint32_t idx;
+ WERROR rv;
+
+ for (idx = 0; idx < vl->nvalues; ++idx) {
+ rv = append_data_summary(vl->values, &vl->values[idx]);
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+ }
+
+ rv = multilist_set_data(vl->list, vl);
+ if (W_ERROR_IS_OK(rv)) {
+ multilist_refresh(vl->list);
+ }
+
+ return rv;
+}
+
+WERROR value_list_load(struct value_list *vl, struct registry_key *key)
+{
+ WERROR rv;
+
+ rv = value_list_load_quick(vl, key);
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+
+ rv = value_list_sync(vl);
+
+ return rv;
+}
+
+struct value_item *value_list_find_next_item(struct value_list *vl,
+ struct value_item *vitem,
+ const char *s,
+ regedit_search_match_fn_t match)
+{
+ struct value_item *end;
+
+ if (!vl->values) {
+ return NULL;
+ }
+
+ if (vitem) {
+ ++vitem;
+ } else {
+ vitem = &vl->values[0];
+ }
+
+ for (end = &vl->values[vl->nvalues]; vitem < end; ++vitem) {
+ if (match(vitem->value_name, s)) {
+ return vitem;
+ }
+ }
+
+ return NULL;
+}
+
+struct value_item *value_list_find_prev_item(struct value_list *vl,
+ struct value_item *vitem,
+ const char *s,
+ regedit_search_match_fn_t match)
+{
+ struct value_item *end;
+
+ if (!vl->values) {
+ return NULL;
+ }
+
+ if (vitem) {
+ --vitem;
+ } else {
+ vitem = &vl->values[vl->nvalues - 1];
+ }
+
+ for (end = &vl->values[-1]; vitem > end; --vitem) {
+ if (match(vitem->value_name, s)) {
+ return vitem;
+ }
+ }
+
+ return NULL;
+}
+
+struct value_item *value_list_get_current_item(struct value_list *vl)
+{
+ return discard_const_p(struct value_item,
+ multilist_get_current_row(vl->list));
+}
+
+void value_list_set_current_item_by_name(struct value_list *vl,
+ const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < vl->nvalues; ++i) {
+ if (strequal(vl->values[i].value_name, name)) {
+ multilist_set_current_row(vl->list, &vl->values[i]);
+ return;
+ }
+ }
+}
+
+void value_list_set_current_item(struct value_list *vl,
+ const struct value_item *item)
+{
+ multilist_set_current_row(vl->list, item);
+}
+
+void value_list_driver(struct value_list *vl, int c)
+{
+ multilist_driver(vl->list, c);
+}
diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h
new file mode 100644
index 0000000..1178389
--- /dev/null
+++ b/source3/utils/regedit_valuelist.h
@@ -0,0 +1,72 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _REGEDIT_VALUELIST_H_
+#define _REGEDIT_VALUELIST_H_
+
+#include <ncurses.h>
+#include <panel.h>
+
+struct registry_key;
+
+struct value_item {
+ uint32_t type;
+ DATA_BLOB data;
+ const char *value_name;
+ char *value;
+ bool unprintable;
+};
+
+struct multilist;
+
+struct value_list {
+ WINDOW *window;
+ WINDOW *sub;
+ PANEL *panel;
+ size_t nvalues;
+ struct value_item *values;
+ struct multilist *list;
+};
+struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols,
+ int begin_y, int begin_x);
+void value_list_show(struct value_list *vl);
+void value_list_set_selected(struct value_list *vl, bool select);
+const char **value_list_load_names(TALLOC_CTX *ctx, struct registry_key *key);
+WERROR value_list_load(struct value_list *vl, struct registry_key *key);
+void value_list_resize(struct value_list *vl, int nlines, int ncols,
+ int begin_y, int begin_x);
+struct value_item *value_list_get_current_item(struct value_list *vl);
+void value_list_set_current_item(struct value_list *vl,
+ const struct value_item *item);
+void value_list_set_current_item_by_name(struct value_list *vl,
+ const char *name);
+void value_list_driver(struct value_list *vl, int c);
+
+WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key);
+WERROR value_list_sync(struct value_list *vl);
+struct value_item *value_list_find_next_item(struct value_list *vl,
+ struct value_item *vitem,
+ const char *s,
+ regedit_search_match_fn_t match);
+struct value_item *value_list_find_prev_item(struct value_list *vl,
+ struct value_item *vitem,
+ const char *s,
+ regedit_search_match_fn_t match);
+
+#endif
diff --git a/source3/utils/regedit_wrap.c b/source3/utils/regedit_wrap.c
new file mode 100644
index 0000000..93297eb
--- /dev/null
+++ b/source3/utils/regedit_wrap.c
@@ -0,0 +1,143 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Registry Editor
+ * Copyright (C) Christopher Davis 2012
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Wrap s3 registry API calls to avoid conflicts with 'struct registry_key',
+ etc, in s4 libregistry. */
+
+#include "includes.h"
+#include "registry.h"
+#include "registry/reg_api.h"
+#include "registry/reg_init_basic.h"
+#include "registry/reg_util_token.h"
+
+#include "regedit.h"
+
+WERROR reg_openhive_wrap(TALLOC_CTX *ctx, const char *hive,
+ struct samba3_registry_key *pkey)
+{
+ struct security_token *token;
+ WERROR rv;
+
+ SMB_ASSERT(pkey->key == NULL);
+
+ rv = ntstatus_to_werror(registry_create_admin_token(ctx, &token));
+ if (!W_ERROR_IS_OK(rv)) {
+ return rv;
+ }
+
+ return reg_openhive(ctx, hive, REG_KEY_READ | REG_KEY_WRITE, token,
+ &pkey->key);
+}
+
+WERROR reg_openkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent,
+ const char *name, struct samba3_registry_key *pkey)
+{
+ SMB_ASSERT(pkey->key == NULL);
+ return reg_openkey(ctx, parent->key, name,
+ REG_KEY_READ | REG_KEY_WRITE, &pkey->key);
+}
+
+WERROR reg_enumvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ uint32_t idx, char **name, uint32_t *type,
+ DATA_BLOB *data)
+{
+ struct registry_value *val = NULL;
+ WERROR rv;
+
+ rv = reg_enumvalue(ctx, key->key, idx, name, &val);
+
+ if (val && W_ERROR_IS_OK(rv)) {
+ *type = (uint32_t)val->type;
+ *data = val->data;
+ }
+
+ return rv;
+}
+
+WERROR reg_queryvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ const char *name, uint32_t *type, DATA_BLOB *data)
+{
+ struct registry_value *val = NULL;
+ WERROR rv;
+
+ rv = reg_queryvalue(ctx, key->key, name, &val);
+
+ if (val && W_ERROR_IS_OK(rv)) {
+ *type = (uint32_t)val->type;
+ *data = val->data;
+ }
+
+ return rv;
+}
+
+WERROR reg_enumkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key,
+ uint32_t idx, char **name, NTTIME *last_write_time)
+{
+ return reg_enumkey(ctx, key->key, idx, name, last_write_time);
+}
+
+WERROR reg_createkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent,
+ const char *subkeypath,
+ struct samba3_registry_key *pkey)
+{
+ enum winreg_CreateAction act;
+
+ SMB_ASSERT(pkey->key == NULL);
+ return reg_createkey(ctx, parent->key, subkeypath,
+ REG_KEY_READ | REG_KEY_WRITE, &pkey->key, &act);
+}
+
+WERROR reg_deletekey_wrap(struct samba3_registry_key *parent, const char *path)
+{
+ return reg_deletekey(parent->key, path);
+}
+
+WERROR reg_deletevalue_wrap(struct samba3_registry_key *key, const char *name)
+{
+ return reg_deletevalue(key->key, name);
+}
+
+WERROR reg_queryinfokey_wrap(struct samba3_registry_key *key,
+ uint32_t *num_subkeys, uint32_t *max_subkeylen,
+ uint32_t *max_subkeysize, uint32_t *num_values,
+ uint32_t *max_valnamelen,
+ uint32_t *max_valbufsize, uint32_t *secdescsize,
+ NTTIME *last_changed_time)
+{
+ return reg_queryinfokey(key->key, num_subkeys, max_subkeylen,
+ max_subkeysize, num_values, max_valnamelen,
+ max_valbufsize, secdescsize,
+ last_changed_time);
+}
+
+WERROR reg_setvalue_wrap(struct samba3_registry_key *key, const char *name,
+ uint32_t type, const DATA_BLOB data)
+{
+ struct registry_value val;
+
+ val.type = type;
+ val.data = data;
+
+ return reg_setvalue(key->key, name, &val);
+}
+
+WERROR reg_init_wrap(void)
+{
+ return registry_init_basic();
+}
diff --git a/source3/utils/sharesec.c b/source3/utils/sharesec.c
new file mode 100644
index 0000000..a6481e2
--- /dev/null
+++ b/source3/utils/sharesec.c
@@ -0,0 +1,613 @@
+/*
+ * Unix SMB/Netbios implementation.
+ * Utility for managing share permissions
+ *
+ * Copyright (C) Tim Potter 2000
+ * Copyright (C) Jeremy Allison 2000
+ * Copyright (C) Jelmer Vernooij 2003
+ * Copyright (C) Gerald (Jerry) Carter 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+struct cli_state;
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "../libcli/security/security.h"
+#include "passdb/machine_sid.h"
+#include "util_sd.h"
+#include "cmdline_contexts.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/param/param.h"
+
+static TALLOC_CTX *ctx;
+
+enum acl_mode { SMB_ACL_DELETE,
+ SMB_ACL_MODIFY,
+ SMB_ACL_ADD,
+ SMB_ACL_SET,
+ SMB_SD_DELETE,
+ SMB_SD_SETSDDL,
+ SMB_SD_VIEWSDDL,
+ SMB_ACL_VIEW,
+ SMB_ACL_VIEW_ALL };
+
+/********************************************************************
+********************************************************************/
+
+static struct security_descriptor* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size )
+{
+ struct security_descriptor *sd = NULL;
+ struct security_ace *ace;
+ struct security_acl *theacl;
+ int num_ace;
+ const char *pacl;
+ int i;
+
+ if ( !szACL )
+ return NULL;
+
+ pacl = szACL;
+ num_ace = count_chars( pacl, ',' ) + 1;
+
+ if ( !(ace = talloc_zero_array( mem_ctx, struct security_ace, num_ace )) )
+ return NULL;
+
+ for ( i=0; i<num_ace; i++ ) {
+ char *end_acl = strchr_m( pacl, ',' );
+ fstring acl_string;
+
+ strncpy( acl_string, pacl, MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1) );
+ acl_string[MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1)] = '\0';
+
+ if ( !parse_ace(NULL, &ace[i], acl_string ) )
+ return NULL;
+
+ pacl = end_acl;
+ pacl++;
+ }
+
+ if ( !(theacl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) )
+ return NULL;
+
+ sd = make_sec_desc( mem_ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE,
+ NULL, NULL, NULL, theacl, sd_size);
+
+ return sd;
+}
+
+/* add an ACE to a list of ACEs in a struct security_acl */
+static bool add_ace(TALLOC_CTX *mem_ctx, struct security_acl **the_acl, struct security_ace *ace)
+{
+ struct security_acl *acl = *the_acl;
+
+ if (acl == NULL) {
+ acl = make_sec_acl(mem_ctx, 3, 1, ace);
+ if (acl == NULL) {
+ return false;
+ }
+ }
+
+ if (acl->num_aces == UINT32_MAX) {
+ return false;
+ }
+ ADD_TO_ARRAY(
+ acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
+ *the_acl = acl;
+ return True;
+}
+
+/* The MSDN is contradictory over the ordering of ACE entries in an ACL.
+ However NT4 gives a "The information may have been modified by a
+ computer running Windows NT 5.0" if denied ACEs do not appear before
+ allowed ACEs. */
+
+static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
+{
+ if (security_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
+ return dom_sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->access_mask != ace2->access_mask)
+ return ace1->access_mask - ace2->access_mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(struct security_ace));
+}
+
+static void sort_acl(struct security_acl *the_acl)
+{
+ uint32_t i;
+ if (!the_acl) return;
+
+ TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (security_ace_equal(&the_acl->aces[i-1],
+ &the_acl->aces[i])) {
+ ARRAY_DEL_ELEMENT(
+ the_acl->aces, i, the_acl->num_aces);
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+
+static int change_share_sec(TALLOC_CTX *mem_ctx, const char *sharename, char *the_acl, enum acl_mode mode)
+{
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *old = NULL;
+ size_t sd_size = 0;
+ uint32_t i, j;
+ NTSTATUS status;
+
+ if (mode != SMB_ACL_SET && mode != SMB_SD_DELETE) {
+ if (!(old = get_share_security( mem_ctx, sharename, &sd_size )) ) {
+ fprintf(stderr, "Unable to retrieve permissions for share "
+ "[%s]\n", sharename);
+ return -1;
+ }
+ }
+
+ if ( (mode != SMB_ACL_VIEW && mode != SMB_SD_DELETE) &&
+ !(sd = parse_acl_string(mem_ctx, the_acl, &sd_size )) ) {
+ fprintf( stderr, "Failed to parse acl\n");
+ return -1;
+ }
+
+ switch (mode) {
+ case SMB_ACL_VIEW_ALL:
+ /* should not happen */
+ return 0;
+ case SMB_ACL_VIEW:
+ sec_desc_print(NULL, stdout, old, false);
+ return 0;
+ case SMB_ACL_DELETE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (security_ace_equal(&sd->dacl->aces[i],
+ &old->dacl->aces[j])) {
+ uint32_t k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->aces[k] = old->dacl->aces[k+1];
+ }
+ old->dacl->num_aces--;
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("ACL for ACE:");
+ print_ace(NULL, stdout, &sd->dacl->aces[i], false);
+ printf(" not found\n");
+ }
+ }
+ break;
+ case SMB_ACL_MODIFY:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (dom_sid_equal(&sd->dacl->aces[i].trustee,
+ &old->dacl->aces[j].trustee)) {
+ old->dacl->aces[j] = sd->dacl->aces[i];
+ found = True;
+ }
+ }
+
+ if (!found) {
+ struct dom_sid_buf buf;
+ printf("ACL for SID %s not found\n",
+ dom_sid_str_buf(&sd->dacl->aces[i].trustee, &buf));
+ }
+ }
+
+ if (sd->owner_sid) {
+ old->owner_sid = sd->owner_sid;
+ }
+
+ if (sd->group_sid) {
+ old->group_sid = sd->group_sid;
+ }
+ break;
+ case SMB_ACL_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(mem_ctx, &old->dacl, &sd->dacl->aces[i]);
+ }
+ break;
+ case SMB_ACL_SET:
+ old = sd;
+ break;
+ case SMB_SD_DELETE:
+ status = delete_share_security(sharename);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf( stderr, "Failed to delete security descriptor for "
+ "share [%s]\n", sharename );
+ return -1;
+ }
+ return 0;
+ default:
+ fprintf(stderr, "invalid command\n");
+ return -1;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ status = set_share_security(sharename, old);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf( stderr, "Failed to store acl for share [%s]\n", sharename );
+ return 2;
+ }
+ return 0;
+}
+
+static int set_sharesec_sddl(const char *sharename, const char *sddl)
+{
+ struct security_descriptor *sd;
+ NTSTATUS status;
+
+ sd = sddl_decode(talloc_tos(), sddl, get_global_sam_sid());
+ if (sd == NULL) {
+ fprintf(stderr, "Failed to parse acl\n");
+ return -1;
+ }
+
+ status = set_share_security(sharename, sd);
+ TALLOC_FREE(sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr, "Failed to store acl for share [%s]\n",
+ sharename);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int view_sharesec_sddl(const char *sharename)
+{
+ struct security_descriptor *sd;
+ size_t sd_size;
+ char *acl;
+
+ sd = get_share_security(talloc_tos(), sharename, &sd_size);
+ if (sd == NULL) {
+ fprintf(stderr, "Unable to retrieve permissions for share "
+ "[%s]\n", sharename);
+ return -1;
+ }
+
+ acl = sddl_encode(talloc_tos(), sd, get_global_sam_sid());
+ TALLOC_FREE(sd);
+ if (acl == NULL) {
+ fprintf(stderr, "Unable to sddl-encode permissions for share "
+ "[%s]\n", sharename);
+ return -1;
+ }
+ printf("%s\n", acl);
+ TALLOC_FREE(acl);
+ return 0;
+}
+
+/********************************************************************
+ main program
+********************************************************************/
+
+enum {
+ OPT_VIEW_ALL = 1000,
+ OPT_VIEW_SDDL,
+};
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ int retval = 0;
+ enum acl_mode mode = SMB_ACL_SET;
+ static char *the_acl = NULL;
+ fstring sharename;
+ bool force_acl = False;
+ int snum;
+ poptContext pc;
+ bool initialize_sid = False;
+ bool ok;
+ struct loadparm_context *lp_ctx = NULL;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "remove",
+ .shortName = 'r',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &the_acl,
+ .val = 'r',
+ .descrip = "Remove ACEs",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "modify",
+ .shortName = 'm',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &the_acl,
+ .val = 'm',
+ .descrip = "Modify existing ACEs",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "add",
+ .shortName = 'a',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &the_acl,
+ .val = 'a',
+ .descrip = "Add ACEs",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "replace",
+ .shortName = 'R',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &the_acl,
+ .val = 'R',
+ .descrip = "Overwrite share permission ACL",
+ .argDescrip = "ACLS",
+ },
+ {
+ .longName = "delete",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'D',
+ .descrip = "Delete the entire security descriptor",
+ },
+ {
+ .longName = "setsddl",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_STRING,
+ .arg = the_acl,
+ .val = 'S',
+ .descrip = "Set the SD in sddl format",
+ },
+ {
+ .longName = "viewsddl",
+ .argInfo = POPT_ARG_NONE,
+ .arg = the_acl,
+ .val = OPT_VIEW_SDDL,
+ .descrip = "View the SD in sddl format",
+ },
+ {
+ .longName = "view",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'v',
+ .descrip = "View current share permissions",
+ },
+ {
+ .longName = "view-all",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_VIEW_ALL,
+ .descrip = "View all current share permissions",
+ },
+ {
+ .longName = "machine-sid",
+ .shortName = 'M',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'M',
+ .descrip = "Initialize the machine SID",
+ },
+ {
+ .longName = "force",
+ .shortName = 'F',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'F',
+ .descrip = "Force storing the ACL",
+ .argDescrip = "ACLS",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ if ( !(ctx = talloc_stackframe()) ) {
+ fprintf( stderr, "Failed to initialize talloc context!\n");
+ return -1;
+ }
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(ctx,
+ SAMBA_CMDLINE_CONFIG_NONE,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(ctx);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "sharename\n");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'r':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_DELETE;
+ break;
+
+ case 'm':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_MODIFY;
+ break;
+
+ case 'a':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_ADD;
+ break;
+
+ case 'R':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_SET;
+ break;
+
+ case 'D':
+ mode = SMB_SD_DELETE;
+ break;
+
+ case 'S':
+ mode = SMB_SD_SETSDDL;
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ break;
+
+ case OPT_VIEW_SDDL:
+ mode = SMB_SD_VIEWSDDL;
+ break;
+
+ case 'v':
+ mode = SMB_ACL_VIEW;
+ break;
+
+ case 'F':
+ force_acl = True;
+ break;
+
+ case 'M':
+ initialize_sid = True;
+ break;
+ case OPT_VIEW_ALL:
+ mode = SMB_ACL_VIEW_ALL;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ setlinebuf(stdout);
+
+ lp_load_with_registry_shares(get_dyn_CONFIGFILE());
+
+ /* check for initializing secrets.tdb first */
+
+ if ( initialize_sid ) {
+ struct dom_sid *sid = get_global_sam_sid();
+ struct dom_sid_buf buf;
+
+ if ( !sid ) {
+ fprintf( stderr, "Failed to retrieve Machine SID!\n");
+ retval = 3;
+ goto done;
+ }
+
+ printf ("%s\n", dom_sid_str_buf(sid, &buf) );
+ retval = 0;
+ goto done;
+ }
+
+ if ( mode == SMB_ACL_VIEW && force_acl ) {
+ fprintf( stderr, "Invalid combination of -F and -v\n");
+ retval = -1;
+ goto done;
+ }
+
+ if (mode == SMB_ACL_VIEW_ALL) {
+ int i;
+
+ for (i=0; i<lp_numservices(); i++) {
+ TALLOC_CTX *frame = talloc_stackframe();
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char *service = lp_servicename(frame, lp_sub, i);
+
+ if (service == NULL) {
+ continue;
+ }
+
+ printf("[%s]\n", service);
+ change_share_sec(frame, service, NULL, SMB_ACL_VIEW);
+ printf("\n");
+ TALLOC_FREE(frame);
+ }
+ goto done;
+ }
+
+ /* get the sharename */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ retval = -1;
+ goto done;
+ }
+
+ fstrcpy(sharename, poptGetArg(pc));
+
+ snum = lp_servicenumber( sharename );
+
+ if ( snum == -1 && !force_acl ) {
+ fprintf( stderr, "Invalid sharename: %s\n", sharename);
+ retval = -1;
+ goto done;
+ }
+
+ switch (mode) {
+ case SMB_SD_SETSDDL:
+ retval = set_sharesec_sddl(sharename, the_acl);
+ break;
+ case SMB_SD_VIEWSDDL:
+ retval = view_sharesec_sddl(sharename);
+ break;
+ default:
+ retval = change_share_sec(ctx, sharename, the_acl, mode);
+ break;
+ }
+
+done:
+ gfree_all();
+ poptFreeContext(pc);
+ talloc_destroy(ctx);
+
+ return retval;
+}
diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c
new file mode 100644
index 0000000..ff11ba4
--- /dev/null
+++ b/source3/utils/smbcacls.c
@@ -0,0 +1,2614 @@
+/*
+ Unix SMB/CIFS implementation.
+ ACL get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Jelmer Vernooij 2003
+ Copyright (C) Noel Power <noel.power@suse.com> 2013
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/clirap.h"
+#include "passdb/machine_sid.h"
+#include "../librpc/gen_ndr/ndr_lsa_c.h"
+#include "util_sd.h"
+#include "lib/param/param.h"
+
+static char DIRSEP_CHAR = '\\';
+
+static int inheritance = 0;
+static const char *save_file = NULL;
+static const char *restore_file = NULL;
+static int recurse;
+static int test_args;
+static int sddl;
+static int query_sec_info = -1;
+static int set_sec_info = -1;
+static bool want_mxac;
+
+static const char *domain_sid = NULL;
+
+enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
+enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+struct cacl_callback_state {
+ struct cli_credentials *creds;
+ struct cli_state *cli;
+ struct security_descriptor *aclsd;
+ struct security_acl *acl_to_add;
+ enum acl_mode mode;
+ char *the_acl;
+ bool acl_no_propagate;
+ bool numeric;
+};
+
+static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli,
+ struct dom_sid *sid)
+{
+ union lsa_PolicyInformation *info = NULL;
+ struct smbXcli_tcon *orig_tcon = NULL;
+ char *orig_share = NULL;
+ struct rpc_pipe_client *rpc_pipe = NULL;
+ struct policy_handle handle;
+ NTSTATUS status, result;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (cli_state_has_tcon(cli)) {
+ cli_state_save_tcon_share(cli, &orig_tcon, &orig_share);
+ }
+
+ status = cli_tree_connect(cli, "IPC$", "?????", NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto tdis;
+ }
+
+ status = rpccli_lsa_open_policy(rpc_pipe, frame, True,
+ GENERIC_EXECUTE_ACCESS, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto tdis;
+ }
+
+ status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle,
+ frame, &handle,
+ LSA_POLICY_INFO_DOMAIN,
+ &info, &result);
+
+ if (any_nt_status_not_ok(status, result, &status)) {
+ goto tdis;
+ }
+
+ *sid = *info->domain.sid;
+
+tdis:
+ TALLOC_FREE(rpc_pipe);
+ cli_tdis(cli);
+done:
+ cli_state_restore_tcon_share(cli, orig_tcon, orig_share);
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static struct dom_sid *get_domain_sid(struct cli_state *cli)
+{
+ NTSTATUS status;
+ struct dom_sid_buf buf;
+
+ struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid);
+ if (sid == NULL) {
+ DEBUG(0, ("Out of memory\n"));
+ return NULL;
+ }
+
+ if (domain_sid) {
+ if (!dom_sid_parse(domain_sid, sid)) {
+ DEBUG(0,("failed to parse domain sid\n"));
+ TALLOC_FREE(sid);
+ }
+ } else {
+ status = cli_lsa_lookup_domain_sid(cli, sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status)));
+ TALLOC_FREE(sid);
+ }
+
+ }
+
+ DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf)));
+ return sid;
+}
+
+/* add an ACE to a list of ACEs in a struct security_acl */
+static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl,
+ const struct security_ace *ace)
+
+{
+ struct security_acl *acl = *the_acl;
+
+ if (acl == NULL) {
+ acl = make_sec_acl(ctx, 3, 0, NULL);
+ if (acl == NULL) {
+ return false;
+ }
+ }
+
+ if (acl->num_aces == UINT32_MAX) {
+ return false;
+ }
+ ADD_TO_ARRAY(
+ acl, struct security_ace, *ace, &acl->aces, &acl->num_aces);
+ *the_acl = acl;
+ return True;
+}
+
+static bool add_ace(struct security_acl **the_acl, struct security_ace *ace)
+{
+ return add_ace_with_ctx(talloc_tos(), the_acl, ace);
+}
+
+/* parse a ascii version of a security descriptor */
+static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
+{
+ const char *p = str;
+ char *tok;
+ struct security_descriptor *ret = NULL;
+ size_t sd_size;
+ struct dom_sid owner_sid = { .num_auths = 0 };
+ bool have_owner = false;
+ struct dom_sid group_sid = { .num_auths = 0 };
+ bool have_group = false;
+ struct security_acl *dacl=NULL;
+ int revision=1;
+
+ while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) {
+ if (strncmp(tok,"REVISION:", 9) == 0) {
+ revision = strtol(tok+9, NULL, 16);
+ continue;
+ }
+
+ if (strncmp(tok,"OWNER:", 6) == 0) {
+ if (have_owner) {
+ printf("Only specify owner once\n");
+ goto done;
+ }
+ if (!StringToSid(cli, &owner_sid, tok+6)) {
+ printf("Failed to parse owner sid\n");
+ goto done;
+ }
+ have_owner = true;
+ continue;
+ }
+
+ if (strncmp(tok,"GROUP:", 6) == 0) {
+ if (have_group) {
+ printf("Only specify group once\n");
+ goto done;
+ }
+ if (!StringToSid(cli, &group_sid, tok+6)) {
+ printf("Failed to parse group sid\n");
+ goto done;
+ }
+ have_group = true;
+ continue;
+ }
+
+ if (strncmp(tok,"ACL:", 4) == 0) {
+ struct security_ace ace;
+ if (!parse_ace(cli, &ace, tok+4)) {
+ goto done;
+ }
+ if(!add_ace(&dacl, &ace)) {
+ printf("Failed to add ACL %s\n", tok);
+ goto done;
+ }
+ continue;
+ }
+
+ printf("Failed to parse token '%s' in security descriptor,\n", tok);
+ goto done;
+ }
+
+ ret = make_sec_desc(
+ ctx,
+ revision,
+ SEC_DESC_SELF_RELATIVE,
+ have_owner ? &owner_sid : NULL,
+ have_group ? &group_sid : NULL,
+ NULL,
+ dacl,
+ &sd_size);
+
+done:
+ return ret;
+}
+
+/*****************************************************
+get fileinfo for filename
+*******************************************************/
+static uint16_t get_fileinfo(struct cli_state *cli, const char *filename)
+{
+ uint16_t fnum = (uint16_t)-1;
+ NTSTATUS status;
+ struct smb_create_returns cr = {0};
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ status = cli_ntcreate(
+ cli, /* cli */
+ filename, /* fname */
+ 0, /* CreatFlags */
+ READ_CONTROL_ACCESS, /* CreatFlags */
+ 0, /* FileAttributes */
+ FILE_SHARE_READ|
+ FILE_SHARE_WRITE, /* ShareAccess */
+ FILE_OPEN, /* CreateDisposition */
+ 0x0, /* CreateOptions */
+ 0x0, /* SecurityFlags */
+ &fnum, /* pfid */
+ &cr); /* cr */
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open %s: %s\n", filename, nt_errstr(status));
+ return 0;
+ }
+
+ cli_close(cli, fnum);
+ return cr.file_attributes;
+}
+
+/*****************************************************
+get sec desc for filename
+*******************************************************/
+static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx,
+ struct cli_state *cli,
+ const char *filename)
+{
+ uint16_t fnum = (uint16_t)-1;
+ struct security_descriptor *sd;
+ NTSTATUS status;
+ uint32_t sec_info;
+ uint32_t desired_access = 0;
+
+ if (query_sec_info == -1) {
+ sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL;
+ } else {
+ sec_info = query_sec_info;
+ }
+
+ if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) {
+ desired_access |= SEC_STD_READ_CONTROL;
+ }
+ if (sec_info & SECINFO_SACL) {
+ desired_access |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+
+ if (desired_access == 0) {
+ desired_access |= SEC_STD_READ_CONTROL;
+ }
+
+ status = cli_ntcreate(cli, filename, 0, desired_access,
+ 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x0, 0x0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open %s: %s\n", filename, nt_errstr(status));
+ return NULL;
+ }
+
+ status = cli_query_security_descriptor(cli, fnum, sec_info,
+ ctx, &sd);
+
+ cli_close(cli, fnum);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to get security descriptor: %s\n",
+ nt_errstr(status));
+ return NULL;
+ }
+ return sd;
+}
+
+static struct security_descriptor *get_secdesc(struct cli_state *cli,
+ const char *filename)
+{
+ return get_secdesc_with_ctx(talloc_tos(), cli, filename);
+}
+/*****************************************************
+set sec desc for filename
+*******************************************************/
+static bool set_secdesc(struct cli_state *cli, const char *filename,
+ struct security_descriptor *sd)
+{
+ uint16_t fnum = (uint16_t)-1;
+ bool result=true;
+ NTSTATUS status;
+ uint32_t desired_access = 0;
+ uint32_t sec_info;
+
+ if (set_sec_info == -1) {
+ sec_info = 0;
+
+ if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) {
+ sec_info |= SECINFO_DACL;
+ }
+ if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) {
+ sec_info |= SECINFO_SACL;
+ }
+ if (sd->owner_sid) {
+ sec_info |= SECINFO_OWNER;
+ }
+ if (sd->group_sid) {
+ sec_info |= SECINFO_GROUP;
+ }
+ } else {
+ sec_info = set_sec_info;
+ }
+
+ /* Make the desired_access more specific. */
+ if (sec_info & SECINFO_DACL) {
+ desired_access |= SEC_STD_WRITE_DAC;
+ }
+ if (sec_info & SECINFO_SACL) {
+ desired_access |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+ if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) {
+ desired_access |= SEC_STD_WRITE_OWNER;
+ }
+
+ status = cli_ntcreate(cli, filename, 0,
+ desired_access,
+ 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ FILE_OPEN, 0x0, 0x0, &fnum, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to open %s: %s\n", filename, nt_errstr(status));
+ return false;
+ }
+
+ status = cli_set_security_descriptor(cli, fnum, sec_info, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("ERROR: security descriptor set failed: %s\n",
+ nt_errstr(status));
+ result=false;
+ }
+
+ cli_close(cli, fnum);
+ return result;
+}
+
+/*****************************************************
+get maximum access for a file
+*******************************************************/
+static int cacl_mxac(struct cli_state *cli, const char *filename)
+{
+ NTSTATUS status;
+ uint32_t mxac;
+
+ status = cli_query_mxac(cli, filename, &mxac);
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Failed to get mxac: %s\n", nt_errstr(status));
+ return EXIT_FAILED;
+ }
+
+ printf("Maximum access: 0x%x\n", mxac);
+
+ return EXIT_OK;
+}
+
+
+/*****************************************************
+dump the acls for a file
+*******************************************************/
+static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric)
+{
+ struct security_descriptor *sd;
+ int ret;
+
+ if (test_args) {
+ return EXIT_OK;
+ }
+
+ sd = get_secdesc(cli, filename);
+ if (sd == NULL) {
+ return EXIT_FAILED;
+ }
+
+ if (sddl) {
+ char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli));
+ if (str == NULL) {
+ return EXIT_FAILED;
+ }
+ printf("%s\n", str);
+ TALLOC_FREE(str);
+ } else {
+ sec_desc_print(cli, stdout, sd, numeric);
+ }
+
+ if (want_mxac) {
+ ret = cacl_mxac(cli, filename);
+ if (ret != EXIT_OK) {
+ return ret;
+ }
+ }
+
+ return EXIT_OK;
+}
+
+/*****************************************************
+Change the ownership or group ownership of a file. Just
+because the NT docs say this can't be done :-). JRA.
+*******************************************************/
+
+static int owner_set(struct cli_state *cli, enum chown_mode change_mode,
+ const char *filename, const char *new_username)
+{
+ struct dom_sid sid;
+ struct security_descriptor *sd;
+ size_t sd_size;
+
+ if (!StringToSid(cli, &sid, new_username))
+ return EXIT_PARSE_ERROR;
+
+ sd = make_sec_desc(talloc_tos(),
+ SECURITY_DESCRIPTOR_REVISION_1,
+ SEC_DESC_SELF_RELATIVE,
+ (change_mode == REQUEST_CHOWN) ? &sid : NULL,
+ (change_mode == REQUEST_CHGRP) ? &sid : NULL,
+ NULL, NULL, &sd_size);
+
+ if (!set_secdesc(cli, filename, sd)) {
+ return EXIT_FAILED;
+ }
+
+ return EXIT_OK;
+}
+
+
+/* The MSDN is contradictory over the ordering of ACE entries in an
+ ACL. However NT4 gives a "The information may have been modified
+ by a computer running Windows NT 5.0" if denied ACEs do not appear
+ before allowed ACEs. At
+ http://technet.microsoft.com/en-us/library/cc781716.aspx the
+ canonical order is specified as "Explicit Deny, Explicit Allow,
+ Inherited ACEs unchanged" */
+
+static int ace_compare(struct security_ace *ace1, struct security_ace *ace2)
+{
+ if (security_ace_equal(ace1, ace2))
+ return 0;
+
+ if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
+ !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
+ return 1;
+ if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
+ (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
+ return -1;
+ if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) &&
+ (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE))
+ return ace1 - ace2;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (dom_sid_compare(&ace1->trustee, &ace2->trustee))
+ return dom_sid_compare(&ace1->trustee, &ace2->trustee);
+
+ if (ace1->flags != ace2->flags)
+ return ace1->flags - ace2->flags;
+
+ if (ace1->access_mask != ace2->access_mask)
+ return ace1->access_mask - ace2->access_mask;
+
+ if (ace1->size != ace2->size)
+ return ace1->size - ace2->size;
+
+ return memcmp(ace1, ace2, sizeof(struct security_ace));
+}
+
+static void sort_acl(struct security_acl *the_acl)
+{
+ uint32_t i;
+ if (!the_acl) return;
+
+ TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (security_ace_equal(&the_acl->aces[i-1],
+ &the_acl->aces[i])) {
+ ARRAY_DEL_ELEMENT(
+ the_acl->aces, i, the_acl->num_aces);
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/*****************************************************
+set the ACLs on a file given a security descriptor
+*******************************************************/
+
+static int cacl_set_from_sd(struct cli_state *cli, const char *filename,
+ struct security_descriptor *sd, enum acl_mode mode,
+ bool numeric)
+{
+ struct security_descriptor *old = NULL;
+ uint32_t i, j;
+ size_t sd_size;
+ int result = EXIT_OK;
+
+ if (!sd) return EXIT_PARSE_ERROR;
+ if (test_args) return EXIT_OK;
+
+ if (mode != SMB_ACL_SET) {
+ /*
+ * Do not fetch old ACL when it will be overwritten
+ * completely with a new one.
+ */
+ old = get_secdesc(cli, filename);
+
+ if (!old) {
+ return EXIT_FAILED;
+ }
+ }
+
+ /* the logic here is rather more complex than I would like */
+ switch (mode) {
+ case SMB_ACL_DELETE:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (security_ace_equal(&sd->dacl->aces[i],
+ &old->dacl->aces[j])) {
+ uint32_t k;
+ for (k=j; k<old->dacl->num_aces-1;k++) {
+ old->dacl->aces[k] = old->dacl->aces[k+1];
+ }
+ old->dacl->num_aces--;
+ found = True;
+ break;
+ }
+ }
+
+ if (!found) {
+ printf("ACL for ACE:");
+ print_ace(cli, stdout, &sd->dacl->aces[i],
+ numeric);
+ printf(" not found\n");
+ }
+ }
+ break;
+
+ case SMB_ACL_MODIFY:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ bool found = False;
+
+ for (j=0;old->dacl && j<old->dacl->num_aces;j++) {
+ if (dom_sid_equal(&sd->dacl->aces[i].trustee,
+ &old->dacl->aces[j].trustee)) {
+ old->dacl->aces[j] = sd->dacl->aces[i];
+ found = True;
+ }
+ }
+
+ if (!found) {
+ fstring str;
+
+ SidToString(cli, str,
+ &sd->dacl->aces[i].trustee,
+ numeric);
+ printf("ACL for SID %s not found\n", str);
+ }
+ }
+
+ if (sd->owner_sid) {
+ old->owner_sid = sd->owner_sid;
+ }
+
+ if (sd->group_sid) {
+ old->group_sid = sd->group_sid;
+ }
+
+ break;
+
+ case SMB_ACL_ADD:
+ for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) {
+ add_ace(&old->dacl, &sd->dacl->aces[i]);
+ }
+ break;
+
+ case SMB_ACL_SET:
+ old = sd;
+ break;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ /* Create new security descriptor and set it */
+
+ /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER.
+ But if we're sending an owner, even if it's the same as the one
+ that already exists then W2K3 insists we open with WRITE_OWNER access.
+ I need to check that setting a SD with no owner set works against WNT
+ and W2K. JRA.
+ */
+
+ sd = make_sec_desc(talloc_tos(),old->revision, old->type,
+ old->owner_sid, old->group_sid,
+ NULL, old->dacl, &sd_size);
+
+ if (!set_secdesc(cli, filename, sd)) {
+ result = EXIT_FAILED;
+ }
+
+ return result;
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+
+static int cacl_set(struct cli_state *cli, const char *filename,
+ char *the_acl, enum acl_mode mode, bool numeric)
+{
+ struct security_descriptor *sd = NULL;
+
+ if (sddl) {
+ const char *msg = NULL;
+ size_t msg_offset = 0;
+ enum ace_condition_flags flags =
+ ACE_CONDITION_FLAG_ALLOW_DEVICE;
+ sd = sddl_decode_err_msg(talloc_tos(),
+ the_acl,
+ get_domain_sid(cli),
+ flags,
+ &msg,
+ &msg_offset);
+ if (sd == NULL) {
+ DBG_ERR("could not decode '%s'\n", the_acl);
+ if (msg != NULL) {
+ DBG_ERR(" %*c\n",
+ (int)msg_offset, '^');
+ DBG_ERR("error '%s'\n", msg);
+ }
+ }
+ } else {
+ sd = sec_desc_parse(talloc_tos(), cli, the_acl);
+ }
+
+ if (sd == NULL) {
+ return EXIT_PARSE_ERROR;
+ }
+ if (test_args) {
+ return EXIT_OK;
+ }
+ return cacl_set_from_sd(cli, filename, sd, mode, numeric);
+}
+
+/*****************************************************
+set the inherit on a file
+*******************************************************/
+static int inherit(struct cli_state *cli, const char *filename,
+ const char *type)
+{
+ struct security_descriptor *old,*sd;
+ uint32_t oldattr;
+ size_t sd_size;
+ int result = EXIT_OK;
+
+ old = get_secdesc(cli, filename);
+
+ if (!old) {
+ return EXIT_FAILED;
+ }
+
+ oldattr = get_fileinfo(cli,filename);
+
+ if (strcmp(type,"allow")==0) {
+ if ((old->type & SEC_DESC_DACL_PROTECTED) ==
+ SEC_DESC_DACL_PROTECTED) {
+ uint32_t i;
+ char *parentname,*temp;
+ struct security_descriptor *parent;
+ temp = talloc_strdup(talloc_tos(), filename);
+
+ old->type=old->type & (~SEC_DESC_DACL_PROTECTED);
+
+ /* look at parent and copy in all its inheritable ACL's. */
+ string_replace(temp, '\\', '/');
+ if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) {
+ return EXIT_FAILED;
+ }
+ string_replace(parentname, '/', '\\');
+ parent = get_secdesc(cli,parentname);
+ if (parent == NULL) {
+ return EXIT_FAILED;
+ }
+ for (i=0;i<parent->dacl->num_aces;i++) {
+ struct security_ace *ace=&parent->dacl->aces[i];
+ /* Add inherited flag to all aces */
+ ace->flags=ace->flags|
+ SEC_ACE_FLAG_INHERITED_ACE;
+ if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
+ if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) ==
+ SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ add_ace(&old->dacl, ace);
+ }
+ } else {
+ if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) ==
+ SEC_ACE_FLAG_OBJECT_INHERIT) {
+ /* clear flags for files */
+ ace->flags=0;
+ add_ace(&old->dacl, ace);
+ }
+ }
+ }
+ } else {
+ printf("Already set to inheritable permissions.\n");
+ return EXIT_FAILED;
+ }
+ } else if (strcmp(type,"remove")==0) {
+ if ((old->type & SEC_DESC_DACL_PROTECTED) !=
+ SEC_DESC_DACL_PROTECTED) {
+ old->type=old->type | SEC_DESC_DACL_PROTECTED;
+
+ /* remove all inherited ACL's. */
+ if (old->dacl) {
+ int i;
+ struct security_acl *temp=old->dacl;
+ old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL);
+ for (i=temp->num_aces-1;i>=0;i--) {
+ struct security_ace *ace=&temp->aces[i];
+ /* Remove all ace with INHERITED flag set */
+ if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) !=
+ SEC_ACE_FLAG_INHERITED_ACE) {
+ add_ace(&old->dacl,ace);
+ }
+ }
+ }
+ } else {
+ printf("Already set to no inheritable permissions.\n");
+ return EXIT_FAILED;
+ }
+ } else if (strcmp(type,"copy")==0) {
+ if ((old->type & SEC_DESC_DACL_PROTECTED) !=
+ SEC_DESC_DACL_PROTECTED) {
+ old->type=old->type | SEC_DESC_DACL_PROTECTED;
+
+ /*
+ * convert all inherited ACL's to non
+ * inherited ACL's.
+ */
+ if (old->dacl) {
+ uint32_t i;
+ for (i=0;i<old->dacl->num_aces;i++) {
+ struct security_ace *ace=&old->dacl->aces[i];
+ /* Remove INHERITED FLAG from all aces */
+ ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE);
+ }
+ }
+ } else {
+ printf("Already set to no inheritable permissions.\n");
+ return EXIT_FAILED;
+ }
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ sd = make_sec_desc(talloc_tos(),old->revision, old->type,
+ old->owner_sid, old->group_sid,
+ NULL, old->dacl, &sd_size);
+
+ if (!set_secdesc(cli, filename, sd)) {
+ result = EXIT_FAILED;
+ }
+
+ return result;
+}
+
+/*****************************************************
+ Return a connection to a server.
+*******************************************************/
+static struct cli_state *connect_one(struct cli_credentials *creds,
+ const char *server, const char *share)
+{
+ struct cli_state *c = NULL;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+
+ nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
+ NULL, 0,
+ share, "?????",
+ creds,
+ flags);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ return c;
+}
+
+/*
+ * Process resulting combination of mask & fname ensuring
+ * terminated with wildcard
+ */
+static char *build_dirname(TALLOC_CTX *ctx,
+ const char *mask, char *dir, char *fname)
+{
+ char *mask2 = NULL;
+ char *p = NULL;
+
+ mask2 = talloc_strdup(ctx, mask);
+ if (!mask2) {
+ return NULL;
+ }
+ p = strrchr_m(mask2, DIRSEP_CHAR);
+ if (p) {
+ p[1] = 0;
+ } else {
+ mask2[0] = '\0';
+ }
+ mask2 = talloc_asprintf_append(mask2,
+ "%s\\*",
+ fname);
+ return mask2;
+}
+
+/*
+ * Returns a copy of the ACL flags in ace modified according
+ * to some inheritance rules.
+ * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children
+ * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only)
+ * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
+ * stripped from flags to be propagated to non-container children
+ * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are
+ * stripped from flags to be propagated if the NP flag
+ * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present
+ */
+
+static uint8_t get_flags_to_propagate(bool is_container,
+ struct security_ace *ace)
+{
+ uint8_t newflags = ace->flags;
+ /* OBJECT inheritance */
+ bool acl_objinherit = (ace->flags &
+ SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT;
+ /* CONTAINER inheritance */
+ bool acl_cntrinherit = (ace->flags &
+ SEC_ACE_FLAG_CONTAINER_INHERIT) ==
+ SEC_ACE_FLAG_CONTAINER_INHERIT;
+ /* PROHIBIT inheritance */
+ bool prohibit_inheritance = ((ace->flags &
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) ==
+ SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
+
+ /* Assume we are not propagating the ACE */
+
+ newflags &= ~SEC_ACE_FLAG_INHERITED_ACE;
+ /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */
+ if (acl_cntrinherit || acl_objinherit) {
+ /*
+ * object inherit ( alone ) on a container needs
+ * SEC_ACE_FLAG_INHERIT_ONLY
+ */
+ if (is_container) {
+ if (acl_objinherit && !acl_cntrinherit) {
+ newflags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+ /*
+ * this is tricky, the only time we would not
+ * propagate the ace for a container is if
+ * prohibit_inheritance is set and object inheritance
+ * alone is set
+ */
+ if ((prohibit_inheritance
+ && acl_objinherit
+ && !acl_cntrinherit) == false) {
+ newflags |= SEC_ACE_FLAG_INHERITED_ACE;
+ }
+ } else {
+ /*
+ * don't apply object/container inheritance flags to
+ * non dirs
+ */
+ newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
+ | SEC_ACE_FLAG_CONTAINER_INHERIT
+ | SEC_ACE_FLAG_INHERIT_ONLY);
+ /*
+ * only apply ace to file if object inherit
+ */
+ if (acl_objinherit) {
+ newflags |= SEC_ACE_FLAG_INHERITED_ACE;
+ }
+ }
+
+ /* if NP is specified strip NP and all OI/CI INHERIT flags */
+ if (prohibit_inheritance) {
+ newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT
+ | SEC_ACE_FLAG_CONTAINER_INHERIT
+ | SEC_ACE_FLAG_INHERIT_ONLY
+ | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT);
+ }
+ }
+ return newflags;
+}
+
+/*
+ * This function builds a new acl for 'caclfile', first it removes any
+ * existing inheritable ace(s) from the current acl of caclfile, secondly it
+ * applies any inheritable acls of the parent of caclfile ( inheritable acls of
+ * caclfile's parent are passed via acl_to_add member of cbstate )
+ *
+ */
+static NTSTATUS propagate_inherited_aces(char *caclfile,
+ struct cacl_callback_state *cbstate)
+{
+ TALLOC_CTX *aclctx = NULL;
+ NTSTATUS status;
+ int result;
+ int fileattr;
+ struct security_descriptor *old = NULL;
+ bool is_container = false;
+ struct security_acl *acl_to_add = cbstate->acl_to_add;
+ struct security_acl *acl_to_remove = NULL;
+ uint32_t i, j;
+
+ aclctx = talloc_new(NULL);
+ if (aclctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile);
+
+ if (!old) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ /* inhibit propagation? */
+ if ((old->type & SEC_DESC_DACL_PROTECTED) ==
+ SEC_DESC_DACL_PROTECTED){
+ status = NT_STATUS_OK;
+ goto out;
+ }
+
+ fileattr = get_fileinfo(cbstate->cli, caclfile);
+ is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY);
+
+ /* find acl(s) that are inherited */
+ for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
+
+ if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) {
+ if (!add_ace_with_ctx(aclctx, &acl_to_remove,
+ &old->dacl->aces[j])) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ }
+
+ /* remove any acl(s) that are inherited */
+ if (acl_to_remove) {
+ for (i = 0; i < acl_to_remove->num_aces; i++) {
+ struct security_ace ace = acl_to_remove->aces[i];
+ for (j = 0; old->dacl && j < old->dacl->num_aces; j++) {
+
+ if (security_ace_equal(&ace,
+ &old->dacl->aces[j])) {
+ uint32_t k;
+ for (k = j; k < old->dacl->num_aces-1;
+ k++) {
+ old->dacl->aces[k] =
+ old->dacl->aces[k+1];
+ }
+ old->dacl->num_aces--;
+ break;
+ }
+ }
+ }
+ }
+ /* propagate any inheritable ace to be added */
+ if (acl_to_add) {
+ for (i = 0; i < acl_to_add->num_aces; i++) {
+ struct security_ace ace = acl_to_add->aces[i];
+ bool is_objectinherit = (ace.flags &
+ SEC_ACE_FLAG_OBJECT_INHERIT) ==
+ SEC_ACE_FLAG_OBJECT_INHERIT;
+ bool is_inherited;
+ /* don't propagate flags to a file unless OI */
+ if (!is_objectinherit && !is_container) {
+ continue;
+ }
+ /*
+ * adjust flags according to inheritance
+ * rules
+ */
+ ace.flags = get_flags_to_propagate(is_container, &ace);
+ is_inherited = (ace.flags &
+ SEC_ACE_FLAG_INHERITED_ACE) ==
+ SEC_ACE_FLAG_INHERITED_ACE;
+ /* don't propagate non inherited flags */
+ if (!is_inherited) {
+ continue;
+ }
+ if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ }
+
+ result = cacl_set_from_sd(cbstate->cli, caclfile,
+ old,
+ SMB_ACL_SET, cbstate->numeric);
+ if (result != EXIT_OK) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(aclctx);
+ return status;
+}
+
+/*
+ * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or
+ * SEC_ACE_FLAG_CONTAINER_INHERIT
+ */
+static bool is_inheritable_ace(struct security_ace *ace)
+{
+ uint8_t flags = ace->flags;
+ if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT
+ | SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ return true;
+ }
+ return false;
+}
+
+/* This method does some basic sanity checking with respect to automatic
+ * inheritance. e.g. it checks if it is possible to do a set, it detects illegal
+ * attempts to set inherited permissions directly. Additionally this method
+ * does some basic initialisation for instance it parses the ACL passed on the
+ * command line.
+ */
+static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename,
+ struct cacl_callback_state *cbstate)
+{
+ NTSTATUS result;
+ char *the_acl = cbstate->the_acl;
+ struct cli_state *cli = cbstate->cli;
+ enum acl_mode mode = cbstate->mode;
+ struct security_descriptor *sd = NULL;
+ struct security_descriptor *old = NULL;
+ uint32_t j;
+ bool propagate = false;
+
+ old = get_secdesc_with_ctx(ctx, cli, filename);
+ if (old == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* parse acl passed on the command line */
+ if (sddl) {
+ const char *msg = NULL;
+ size_t msg_offset = 0;
+ enum ace_condition_flags flags =
+ ACE_CONDITION_FLAG_ALLOW_DEVICE;
+
+ cbstate->aclsd = sddl_decode_err_msg(ctx,
+ the_acl,
+ get_domain_sid(cli),
+ flags,
+ &msg,
+ &msg_offset);
+ if (cbstate->aclsd == NULL) {
+ DBG_ERR("could not decode '%s'\n", the_acl);
+ if (msg != NULL) {
+ DBG_ERR(" %*c\n",
+ (int)msg_offset, '^');
+ DBG_ERR("error '%s'\n", msg);
+ }
+ }
+ } else {
+ cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl);
+ }
+
+ if (!cbstate->aclsd) {
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ sd = cbstate->aclsd;
+
+ /* set operation if inheritance is enabled doesn't make sense */
+ if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) !=
+ SEC_DESC_DACL_PROTECTED)){
+ d_printf("Inheritance enabled at %s, can't apply set operation\n",filename);
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+
+ }
+
+ /*
+ * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE
+ * flags that are set
+ */
+ for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
+ struct security_ace *ace = &sd->dacl->aces[j];
+ if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) {
+ d_printf("Illegal parameter %s\n", the_acl);
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ if (!propagate) {
+ if (is_inheritable_ace(ace)) {
+ propagate = true;
+ }
+ }
+ }
+
+ result = NT_STATUS_OK;
+out:
+ cbstate->acl_no_propagate = !propagate;
+ return result;
+}
+
+/*
+ * This method builds inheritable ace(s) from filename (which should be
+ * a container) that need propagating to children in order to provide
+ * automatic inheritance. Those inheritable ace(s) are stored in
+ * acl_to_add member of cbstate for later processing
+ * (see propagate_inherited_aces)
+ */
+static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename,
+ struct cacl_callback_state *cbstate)
+{
+ NTSTATUS result;
+ struct cli_state *cli = NULL;
+ struct security_descriptor *sd = NULL;
+ struct security_acl *acl_to_add = NULL;
+ uint32_t j;
+
+ cli = cbstate->cli;
+ sd = get_secdesc_with_ctx(ctx, cli, filename);
+
+ if (sd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /*
+ * Check if any inheritance related flags are used, if not then
+ * nothing to do. At the same time populate acls for inheritance
+ * related ace(s) that need to be added to or deleted from children as
+ * a result of inheritance propagation.
+ */
+
+ for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) {
+ struct security_ace *ace = &sd->dacl->aces[j];
+ if (is_inheritable_ace(ace)) {
+ bool added = add_ace_with_ctx(ctx, &acl_to_add, ace);
+ if (!added) {
+ result = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ }
+ }
+ cbstate->acl_to_add = acl_to_add;
+ result = NT_STATUS_OK;
+out:
+ return result;
+}
+
+/*
+ * Callback handler to handle child elements processed by cli_list, we attempt
+ * to propagate inheritable ace(s) to each child via the function
+ * propagate_inherited_aces. Children that are themselves directories are passed
+ * to cli_list again ( to descend the directory structure )
+ */
+static NTSTATUS cacl_set_cb(struct file_info *f,
+ const char *mask, void *state)
+{
+ struct cacl_callback_state *cbstate =
+ (struct cacl_callback_state *)state;
+ struct cli_state *cli = NULL;
+ struct cli_credentials *creds = NULL;
+
+ TALLOC_CTX *dirctx = NULL;
+ NTSTATUS status;
+ struct cli_state *targetcli = NULL;
+
+ char *dir = NULL;
+ char *dir_end = NULL;
+ char *mask2 = NULL;
+ char *targetpath = NULL;
+ char *caclfile = NULL;
+
+ dirctx = talloc_new(NULL);
+ if (!dirctx) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ cli = cbstate->cli;
+ creds = cbstate->creds;
+
+ /* Work out the directory. */
+ dir = talloc_strdup(dirctx, mask);
+ if (!dir) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ dir_end = strrchr(dir, DIRSEP_CHAR);
+ if (dir_end != NULL) {
+ *dir_end = '\0';
+ }
+
+ if (!f->name || !f->name[0]) {
+ d_printf("Empty dir name returned. Possible server misconfiguration.\n");
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ struct cacl_callback_state dir_cbstate;
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
+ | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_HIDDEN;
+ dir_end = NULL;
+
+ /* ignore special '.' & '..' */
+ if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
+ status = NT_STATUS_OK;
+ goto out;
+ }
+
+ mask2 = build_dirname(dirctx, mask, dir, f->name);
+ if (mask2 == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ /* check for dfs */
+ status = cli_resolve_path(dirctx, "", creds, cli,
+ mask2, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ /*
+ * prepare path to caclfile, remove any existing wildcard
+ * chars and convert path separators.
+ */
+
+ caclfile = talloc_strdup(dirctx, targetpath);
+ if (!caclfile) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ dir_end = strrchr(caclfile, '*');
+ if (dir_end != NULL) {
+ *dir_end = '\0';
+ }
+
+ string_replace(caclfile, '/', '\\');
+ /*
+ * make directory specific copy of cbstate here
+ * (for this directory level) to be available as
+ * the parent cbstate for the children of this directory.
+ * Note: cbstate is overwritten for the current file being
+ * processed.
+ */
+ dir_cbstate = *cbstate;
+ dir_cbstate.cli = targetcli;
+
+ /*
+ * propagate any inherited ace from our parent
+ */
+ status = propagate_inherited_aces(caclfile, &dir_cbstate);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ /*
+ * get inheritable ace(s) for this dir/container
+ * that will be propagated to its children
+ */
+ status = get_inheritable_aces(dirctx, caclfile,
+ &dir_cbstate);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ /*
+ * ensure cacl_set_cb gets called for children
+ * of this directory (targetpath)
+ */
+ status = cli_list(targetcli, targetpath,
+ attribute, cacl_set_cb,
+ (void *)&dir_cbstate);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ } else {
+ /*
+ * build full path to caclfile and replace '/' with '\' so
+ * other utility functions can deal with it
+ */
+
+ targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name);
+ if (!targetpath) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ string_replace(targetpath, '/', '\\');
+
+ /* attempt to propagate any inherited ace to file caclfile */
+ status = propagate_inherited_aces(targetpath, cbstate);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ }
+ status = NT_STATUS_OK;
+out:
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("error %s: processing %s\n",
+ nt_errstr(status),
+ targetpath);
+ }
+ TALLOC_FREE(dirctx);
+ return status;
+}
+
+
+/*
+ * Wrapper around cl_list to descend the directory tree pointed to by 'filename',
+ * helper callback function 'cacl_set_cb' handles the child elements processed
+ * by cli_list.
+ */
+static int inheritance_cacl_set(char *filename,
+ struct cacl_callback_state *cbstate)
+{
+ int result;
+ NTSTATUS ntstatus;
+ int fileattr;
+ char *mask = NULL;
+ struct cli_state *cli = cbstate->cli;
+ TALLOC_CTX *ctx = NULL;
+ bool isdirectory = false;
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM
+ | FILE_ATTRIBUTE_HIDDEN;
+ ctx = talloc_init("inherit_set");
+ if (ctx == NULL) {
+ d_printf("out of memory\n");
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ /* ensure we have a filename that starts with '\' */
+ if (!filename || *filename != DIRSEP_CHAR) {
+ /* illegal or no filename */
+ result = EXIT_FAILED;
+ d_printf("illegal or missing name '%s'\n", filename);
+ goto out;
+ }
+
+
+ fileattr = get_fileinfo(cli, filename);
+ isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ == FILE_ATTRIBUTE_DIRECTORY;
+
+ /*
+ * if we've got as far as here then we have already evaluated
+ * the args.
+ */
+ if (test_args) {
+ result = EXIT_OK;
+ goto out;
+ }
+
+ mask = NULL;
+ /* make sure we have a trailing '\*' for directory */
+ if (!isdirectory) {
+ mask = talloc_strdup(ctx, filename);
+ } else if (strlen(filename) > 1) {
+ /*
+ * if the passed file name doesn't have a trailing '\'
+ * append it.
+ */
+ char *name_end = strrchr(filename, DIRSEP_CHAR);
+ if (name_end != filename + strlen(filename) + 1) {
+ mask = talloc_asprintf(ctx, "%s\\*", filename);
+ } else {
+ mask = talloc_strdup(ctx, filename);
+ }
+ } else {
+ /* filename is a single '\', just append '*' */
+ mask = talloc_asprintf_append(mask, "%s*", filename);
+ }
+
+ if (!mask) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ /*
+ * prepare for automatic propagation of the acl passed on the
+ * cmdline.
+ */
+
+ ntstatus = prepare_inheritance_propagation(ctx, filename,
+ cbstate);
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ d_printf("error: %s processing %s\n",
+ nt_errstr(ntstatus), filename);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ result = cacl_set_from_sd(cli, filename, cbstate->aclsd,
+ cbstate->mode, cbstate->numeric);
+
+ /*
+ * strictly speaking it could be considered an error if a file was
+ * specified with '--propagate-inheritance'. However we really want
+ * to eventually get rid of '--propagate-inheritance' so we will be
+ * more forgiving here and instead just exit early.
+ */
+ if (!isdirectory || (result != EXIT_OK)) {
+ goto out;
+ }
+
+ /* check if there is actually any need to propagate */
+ if (cbstate->acl_no_propagate) {
+ goto out;
+ }
+ /* get inheritable attributes this parent container (e.g. filename) */
+ ntstatus = get_inheritable_aces(ctx, filename, cbstate);
+ if (NT_STATUS_IS_OK(ntstatus)) {
+ /* process children */
+ ntstatus = cli_list(cli, mask, attribute,
+ cacl_set_cb,
+ (void *)cbstate);
+ }
+
+ if (!NT_STATUS_IS_OK(ntstatus)) {
+ d_printf("error: %s processing %s\n",
+ nt_errstr(ntstatus), filename);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+out:
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+struct diritem {
+ struct diritem *prev, *next;
+ /*
+ * dirname and targetpath below are sanitized,
+ * e.g.
+ * + start and end with '\'
+ * + have no trailing '*'
+ * + all '/' have been converted to '\'
+ */
+ char *dirname;
+ char *targetpath;
+ struct cli_state *targetcli;
+};
+
+struct save_restore_stats
+{
+ int success;
+ int failure;
+};
+
+struct dump_context {
+ struct diritem *list;
+ struct cli_credentials *creds;
+ struct cli_state *cli;
+ struct save_restore_stats *stats;
+ int save_fd;
+ struct diritem *dir;
+ NTSTATUS status;
+};
+
+static int write_dacl(struct dump_context *ctx,
+ struct cli_state *cli,
+ const char *filename,
+ const char *origfname)
+{
+ struct security_descriptor *sd = NULL;
+ char *str = NULL;
+ const char *output_fmt = "%s\r\n%s\r\n";
+ const char *tmp = NULL;
+ char *out_str = NULL;
+ uint8_t *dest = NULL;
+ ssize_t s_len;
+ size_t d_len;
+ bool ok;
+ int result;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (test_args) {
+ return EXIT_OK;
+ }
+
+ if (ctx->save_fd < 0) {
+ DBG_ERR("error processing %s no file descriptor\n", filename);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ sd = get_secdesc(cli, filename);
+ if (sd == NULL) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ sd->owner_sid = NULL;
+ sd->group_sid = NULL;
+
+ str = sddl_encode(frame, sd, get_domain_sid(cli));
+ if (str == NULL) {
+ DBG_ERR("error processing %s couldn't encode DACL\n", filename);
+ result = EXIT_FAILED;
+ goto out;
+ }
+ /*
+ * format of icacls save file is
+ * a line containing the path of the file/dir
+ * followed by a line containing the sddl format
+ * of the dacl.
+ * The format of the strings are null terminated
+ * 16-bit Unicode. Each line is terminated by "\r\n"
+ */
+
+ tmp = origfname;
+ /* skip leading '\' */
+ if (tmp[0] == '\\') {
+ tmp++;
+ }
+ out_str = talloc_asprintf(frame, output_fmt, tmp, str);
+
+ if (out_str == NULL) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ s_len = strlen(out_str);
+
+ ok = convert_string_talloc(out_str,
+ CH_UNIX,
+ CH_UTF16,
+ out_str,
+ s_len, (void **)(void *)&dest, &d_len);
+ if (!ok) {
+ DBG_ERR("error processing %s out of memory\n", tmp);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ if (write(ctx->save_fd, dest, d_len) != d_len) {
+ DBG_ERR("error processing %s failed to write to file.\n", tmp);
+ result = EXIT_FAILED;
+ goto out;
+ }
+ fsync(ctx->save_fd);
+
+ result = EXIT_OK;
+ ctx->stats->success += 1;
+ fprintf(stdout, "Successfully processed file: %s\n", tmp);
+out:
+ TALLOC_FREE(frame);
+ if (result != EXIT_OK) {
+ ctx->stats->failure += 1;
+ }
+ return result;
+}
+
+/*
+ * Sanitize directory name.
+ * Given a directory name 'dir' ensure it;
+ * o starts with '\'
+ * o ends with '\'
+ * o doesn't end with trailing '*'
+ * o ensure all '/' are converted to '\'
+ */
+
+static char *sanitize_dirname(TALLOC_CTX *ctx,
+ const char *dir)
+{
+ char *mask = NULL;
+ char *name_end = NULL;
+
+ mask = talloc_strdup(ctx, dir);
+ name_end = strrchr(mask, '*');
+ if (name_end) {
+ *name_end = '\0';
+ }
+
+ name_end = strrchr(mask, DIRSEP_CHAR);
+
+ if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) {
+ mask = talloc_asprintf(ctx, "%s\\", mask);
+ }
+
+ string_replace(mask, '/', '\\');
+ return mask;
+}
+
+/*
+ * Process each entry (child) of a directory.
+ * Each entry, regardless of whether it is itself a file or directory
+ * has it's dacl written to the restore/save file.
+ * Each directory is saved to context->list (for further processing)
+ * write_dacl will update the stats (success/fail)
+ */
+static NTSTATUS cacl_dump_dacl_cb(struct file_info *f,
+ const char *mask, void *state)
+{
+ struct dump_context *ctx = talloc_get_type_abort(state,
+ struct dump_context);
+
+ NTSTATUS status;
+
+ char *mask2 = NULL;
+ char *targetpath = NULL;
+ char *unresolved = NULL;
+
+ /*
+ * if we have already encountered an error
+ * bail out
+ */
+ if (!NT_STATUS_IS_OK(ctx->status)) {
+ return ctx->status;
+ }
+
+ if (!f->name || !f->name[0]) {
+ DBG_ERR("Empty dir name returned. Possible server "
+ "misconfiguration.\n");
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ mask2 = sanitize_dirname(ctx, mask);
+ if (!mask2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ if (f->attr & FILE_ATTRIBUTE_DIRECTORY) {
+ struct diritem *item = NULL;
+
+ /* ignore special '.' & '..' */
+ if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) {
+ status = NT_STATUS_OK;
+ goto out;
+ }
+
+ /* Work out the directory. */
+ unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
+ if (!unresolved) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
+
+ if (unresolved == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ item = talloc_zero(ctx, struct diritem);
+ if (item == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ item->dirname = unresolved;
+
+ mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name);
+ if (!mask2) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli,
+ mask2, &item->targetcli, &targetpath);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("error failed to resolve: %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ item->targetpath = sanitize_dirname(ctx, targetpath);
+ if (!item->targetpath) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (write_dacl(ctx,
+ item->targetcli,
+ item->targetpath, unresolved) != EXIT_OK) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ /*
+ * cli_list happily ignores error encountered
+ * when processing the callback so we need
+ * to save any error status encountered while
+ * processing directories (so we can stop recursing
+ * those as soon as possible).
+ * Changing the current behaviour of the callback
+ * handling by cli_list would be I think be too
+ * risky.
+ */
+ ctx->status = status;
+ goto out;
+ }
+
+ DLIST_ADD_END(ctx->list, item);
+
+ } else {
+ unresolved = sanitize_dirname(ctx, ctx->dir->dirname);
+ if (!unresolved) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name);
+
+ if (!unresolved) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ /*
+ * build full path to the file and replace '/' with '\' so
+ * other utility functions can deal with it
+ */
+
+ targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name);
+
+ if (!targetpath) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (write_dacl(ctx,
+ ctx->dir->targetcli,
+ targetpath, unresolved) != EXIT_OK) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ /*
+ * cli_list happily ignores error encountered
+ * when processing the callback so we need
+ * to save any error status encountered while
+ * processing directories (so we can stop recursing
+ * those as soon as possible).
+ * Changing the current behaviour of the callback
+ * handling by cli_list would be I think be too
+ * risky.
+ */
+ ctx->status = status;
+ goto out;
+ }
+ }
+ status = NT_STATUS_OK;
+out:
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("error %s: processing %s\n",
+ nt_errstr(status), targetpath);
+ }
+ return status;
+}
+
+/*
+ * dump_ctx contains a list of directories to be processed
+ * + each directory 'dir' is scanned by cli_list, the cli_list
+ * callback 'cacl_dump_dacl_cb' writes out the dacl of each
+ * child of 'dir' (regardless of whether it is a dir or file)
+ * to the restore/save file. Additionally any directories encountered
+ * are returned in the passed in dump_ctx->list member
+ * + the directory list returned from cli_list is passed and processed
+ * by recursively calling dump_dacl_dirtree
+ *
+ */
+static int dump_dacl_dirtree(struct dump_context *dump_ctx)
+{
+ struct diritem *item = NULL;
+ struct dump_context *new_dump_ctx = NULL;
+ int result;
+ for (item = dump_ctx->list; item; item = item->next) {
+ uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY
+ | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
+ NTSTATUS status;
+ char *mask = NULL;
+
+ new_dump_ctx = talloc_zero(dump_ctx, struct dump_context);
+
+ if (new_dump_ctx == NULL) {
+ DBG_ERR("out of memory\n");
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ if (item->targetcli == NULL) {
+ status = cli_resolve_path(new_dump_ctx,
+ "",
+ dump_ctx->creds,
+ dump_ctx->cli,
+ item->dirname,
+ &item->targetcli,
+ &item->targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to resolve path %s "
+ "error: %s\n",
+ item->dirname, nt_errstr(status));
+ result = EXIT_FAILED;
+ goto out;
+ }
+ }
+ new_dump_ctx->creds = dump_ctx->creds;
+ new_dump_ctx->save_fd = dump_ctx->save_fd;
+ new_dump_ctx->stats = dump_ctx->stats;
+ new_dump_ctx->dir = item;
+ new_dump_ctx->cli = item->targetcli;
+
+ mask = talloc_asprintf(new_dump_ctx, "%s*",
+ new_dump_ctx->dir->targetpath);
+ status = cli_list(new_dump_ctx->dir->targetcli,
+ mask,
+ attribute, cacl_dump_dacl_cb, new_dump_ctx);
+
+ if (!NT_STATUS_IS_OK(status) ||
+ !NT_STATUS_IS_OK(new_dump_ctx->status)) {
+ NTSTATUS tmpstatus;
+ if (!NT_STATUS_IS_OK(status)) {
+ /*
+ * cli_list failed for some reason
+ * so we need to update the failure stat
+ */
+ new_dump_ctx->stats->failure += 1;
+ tmpstatus = status;
+ } else {
+ /* cacl_dump_dacl_cb should have updated stat */
+ tmpstatus = new_dump_ctx->status;
+ }
+ DBG_ERR("error %s: processing %s\n",
+ nt_errstr(tmpstatus), item->dirname);
+ result = EXIT_FAILED;
+ goto out;
+ }
+ result = dump_dacl_dirtree(new_dump_ctx);
+ if (result != EXIT_OK) {
+ goto out;
+ }
+ }
+
+ result = EXIT_OK;
+out:
+ TALLOC_FREE(new_dump_ctx);
+ return result;
+}
+
+static int cacl_dump_dacl(struct cli_state *cli,
+ struct cli_credentials *creds,
+ char *filename)
+{
+ int fileattr;
+ char *mask = NULL;
+ TALLOC_CTX *ctx = NULL;
+ bool isdirectory = false;
+ int result;
+ struct dump_context *dump_ctx = NULL;
+ struct save_restore_stats stats = {0};
+ struct diritem *item = NULL;
+ struct cli_state *targetcli = NULL;
+ char *targetpath = NULL;
+ NTSTATUS status;
+
+ ctx = talloc_init("cacl_dump");
+ if (ctx == NULL) {
+ DBG_ERR("out of memory\n");
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ dump_ctx = talloc_zero(ctx, struct dump_context);
+ if (dump_ctx == NULL) {
+ DBG_ERR("out of memory\n");
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ dump_ctx->save_fd = open(save_file,
+ O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
+
+ if (dump_ctx->save_fd < 0) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ dump_ctx->creds = creds;
+ dump_ctx->cli = cli;
+ dump_ctx->stats = &stats;
+
+ /* ensure we have a filename that starts with '\' */
+ if (!filename || *filename != DIRSEP_CHAR) {
+ /* illegal or no filename */
+ result = EXIT_FAILED;
+ DBG_ERR("illegal or missing name '%s'\n", filename);
+ goto out;
+ }
+
+ status = cli_resolve_path(dump_ctx, "",
+ dump_ctx->creds,
+ dump_ctx->cli,
+ filename, &targetcli, &targetpath);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed resolve %s\n", filename);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ fileattr = get_fileinfo(targetcli, targetpath);
+ isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ == FILE_ATTRIBUTE_DIRECTORY;
+
+ /*
+ * if we've got as far as here then we have already evaluated
+ * the args.
+ */
+ if (test_args) {
+ result = EXIT_OK;
+ goto out;
+ }
+
+ mask = NULL;
+ /* make sure we have a trailing '\*' for directory */
+ if (!isdirectory) {
+ mask = talloc_strdup(ctx, filename);
+ } else if (strlen(filename) > 1) {
+ mask = sanitize_dirname(ctx, filename);
+ } else {
+ /* filename is a single '\' */
+ mask = talloc_strdup(ctx, filename);
+ }
+ if (!mask) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ write_dacl(dump_ctx, targetcli, targetpath, filename);
+ if (isdirectory && recurse) {
+ item = talloc_zero(dump_ctx, struct diritem);
+ if (!item) {
+ result = EXIT_FAILED;
+ goto out;
+ }
+ item->dirname = mask;
+ DLIST_ADD_END(dump_ctx->list, item);
+ dump_dacl_dirtree(dump_ctx);
+ }
+
+ fprintf(stdout, "Successfully processed %d files: "
+ "Failed processing %d files\n",
+ dump_ctx->stats->success, dump_ctx->stats->failure);
+ result = EXIT_OK;
+out:
+ if (dump_ctx && dump_ctx->save_fd > 0) {
+ close(dump_ctx->save_fd);
+ }
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+struct restore_dacl {
+ const char *path;
+ struct security_descriptor *sd;
+};
+
+/*
+ * Restore dacls from 'savefile' produced by
+ * 'icacls name /save' or 'smbcacls --save'
+ */
+static int cacl_restore(struct cli_state *cli,
+ struct cli_credentials *creds,
+ bool numeric, const char *restorefile)
+{
+ int restore_fd;
+ int result;
+ struct save_restore_stats stats = { 0 };
+
+ char **lines = NULL;
+ char *content = NULL;
+ char *convert_content = NULL;
+ size_t content_size;
+ struct restore_dacl *entries = NULL;
+ int numlines, i = 0;
+ bool ok;
+ struct dom_sid *sid = NULL;
+
+ if (restorefile == NULL) {
+ DBG_ERR("No restore file specified\n");
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ if (test_args) {
+ result = EXIT_OK;
+ goto out;
+ }
+
+ restore_fd = open(restorefile, O_RDONLY, S_IRUSR | S_IWUSR);
+ if (restore_fd < 0) {
+ DBG_ERR("Failed to open %s.\n", restorefile);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ content = fd_load(restore_fd, &content_size, 0, talloc_tos());
+
+ close(restore_fd);
+
+ if (content == NULL) {
+ DBG_ERR("Failed to load content from %s.\n", restorefile);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ ok = convert_string_talloc(talloc_tos(),
+ CH_UTF16,
+ CH_UNIX,
+ content,
+ utf16_len_n(content, content_size),
+ (void **)(void *)&convert_content,
+ &content_size);
+
+ TALLOC_FREE(content);
+
+ if (!ok) {
+ DBG_ERR("Failed to convert content from %s "
+ "to CH_UNIX.\n", restorefile);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ lines = file_lines_parse(convert_content,
+ content_size, &numlines, talloc_tos());
+
+ if (lines == NULL) {
+ DBG_ERR("Failed to parse lines from content of %s.",
+ restorefile);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ entries = talloc_zero_array(lines, struct restore_dacl, numlines / 2);
+
+ if (entries == NULL) {
+ DBG_ERR("error processing %s, out of memory\n", restorefile);
+ result = EXIT_FAILED;
+ goto out;
+ }
+
+ sid = get_domain_sid(cli);
+
+ while (i < numlines) {
+ int index = i / 2;
+ int first_line = (i % 2) == 0;
+
+ if (first_line) {
+ char *tmp = NULL;
+ tmp = lines[i];
+ /* line can be blank if root of share */
+ if (strlen(tmp) == 0) {
+ entries[index].path = talloc_strdup(lines,
+ "\\");
+ } else {
+ entries[index].path = lines[i];
+ }
+ } else {
+ const char *msg = NULL;
+ size_t msg_offset = 0;
+ enum ace_condition_flags flags =
+ ACE_CONDITION_FLAG_ALLOW_DEVICE;
+ entries[index].sd = sddl_decode_err_msg(lines,
+ lines[i],
+ sid,
+ flags,
+ &msg,
+ &msg_offset);
+ if(entries[index].sd == NULL) {
+ DBG_ERR("could not decode '%s'\n", lines[i]);
+ if (msg != NULL) {
+ DBG_ERR(" %*c\n",
+ (int)msg_offset, '^');
+ DBG_ERR("error '%s'\n", msg);
+ }
+ result = EXIT_FAILED;
+ goto out;
+ }
+ entries[index].sd->type |=
+ SEC_DESC_DACL_AUTO_INHERIT_REQ;
+ entries[index].sd->type |= SEC_DESC_SACL_AUTO_INHERITED;
+ }
+ i++;
+ }
+ for (i = 0; i < (numlines / 2); i++) {
+ int mode = SMB_ACL_SET;
+ int set_result;
+ struct cli_state *targetcli = NULL;
+ char *targetpath = NULL;
+ NTSTATUS status;
+
+ /* check for dfs */
+ status = cli_resolve_path(talloc_tos(),
+ "",
+ creds,
+ cli,
+ entries[i].path,
+ &targetcli, &targetpath);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ printf("Error failed to process file: %s\n",
+ entries[i].path);
+ stats.failure += 1;
+ continue;
+ }
+
+ set_result = cacl_set_from_sd(targetcli,
+ targetpath,
+ entries[i].sd, mode, numeric);
+
+ if (set_result == EXIT_OK) {
+ printf("Successfully processed file: %s\n",
+ entries[i].path);
+ stats.success += 1;
+ } else {
+ printf("Error failed to process file: %s\n",
+ entries[i].path);
+ stats.failure += 1;
+ }
+ }
+
+ result = EXIT_OK;
+out:
+ TALLOC_FREE(lines);
+ fprintf(stdout, "Successfully processed %d files: "
+ "Failed processing %d files\n", stats.success, stats.failure);
+ return result;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc, char *argv[])
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ char *share;
+ int opt;
+ enum acl_mode mode = SMB_ACL_SET;
+ static char *the_acl = NULL;
+ enum chown_mode change_mode = REQUEST_NONE;
+ int result;
+ char *path;
+ char *filename = NULL;
+ poptContext pc;
+ /* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+ int numeric = 0;
+ struct cli_state *targetcli = NULL;
+ struct cli_credentials *creds = NULL;
+ char *targetfile = NULL;
+ NTSTATUS status;
+ bool ok;
+ struct loadparm_context *lp_ctx = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "delete",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'D',
+ .descrip = "Delete an acl",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "modify",
+ .shortName = 'M',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'M',
+ .descrip = "Modify an acl",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "add",
+ .shortName = 'a',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'a',
+ .descrip = "Add an acl",
+ .argDescrip = "ACL",
+ },
+ {
+ .longName = "set",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'S',
+ .descrip = "Set acls",
+ .argDescrip = "ACLS",
+ },
+ {
+ .longName = "chown",
+ .shortName = 'C',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'C',
+ .descrip = "Change ownership of a file",
+ .argDescrip = "USERNAME",
+ },
+ {
+ .longName = "chgrp",
+ .shortName = 'G',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'G',
+ .descrip = "Change group ownership of a file",
+ .argDescrip = "GROUPNAME",
+ },
+ {
+ .longName = "inherit",
+ .shortName = 'I',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'I',
+ .descrip = "Inherit allow|remove|copy",
+ },
+ {
+ .longName = "propagate-inheritance",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &inheritance,
+ .val = 1,
+ .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify",
+ },
+ {
+ .longName = "save",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &save_file,
+ .val = 1,
+ .descrip = "stores the DACLs in sddl format of the "
+ "specified file or folder for later use "
+ "with restore. SACLS, owner or integrity"
+ " labels are not stored",
+ },
+ {
+ .longName = "restore",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &restore_file,
+ .val = 1,
+ .descrip = "applies the stored DACLS to files in "
+ "directory.",
+ },
+ {
+ .longName = "recurse",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &recurse,
+ .val = 1,
+ .descrip = "indicates the operation is performed "
+ "on directory and all files/directories"
+ " below. (only applies to save option)",
+ },
+ {
+ .longName = "numeric",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &numeric,
+ .val = 1,
+ .descrip = "Don't resolve sids or masks to names",
+ },
+ {
+ .longName = "sddl",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &sddl,
+ .val = 1,
+ .descrip = "Output and input acls in sddl format",
+ },
+ {
+ .longName = "query-security-info",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &query_sec_info,
+ .val = 1,
+ .descrip = "The security-info flags for queries"
+ },
+ {
+ .longName = "set-security-info",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &set_sec_info,
+ .val = 1,
+ .descrip = "The security-info flags for modifications"
+ },
+ {
+ .longName = "test-args",
+ .shortName = 't',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &test_args,
+ .val = 1,
+ .descrip = "Test arguments"
+ },
+ {
+ .longName = "domain-sid",
+ .shortName = 0,
+ .argInfo = POPT_ARG_STRING,
+ .arg = &domain_sid,
+ .val = 0,
+ .descrip = "Domain SID for sddl",
+ .argDescrip = "SID"},
+ {
+ .longName = "maximum-access",
+ .shortName = 'x',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'x',
+ .descrip = "Query maximum permissions",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S3
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ struct cli_state *cli;
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *owner_username = "";
+ char *server;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ setlinebuf(stdout);
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: "
+ "'ACL:user:[ALLOWED|DENIED]/flags/permissions'");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'S':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_SET;
+ break;
+
+ case 'D':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_DELETE;
+ break;
+
+ case 'M':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_MODIFY;
+ break;
+
+ case 'a':
+ the_acl = smb_xstrdup(poptGetOptArg(pc));
+ mode = SMB_ACL_ADD;
+ break;
+
+ case 'C':
+ owner_username = poptGetOptArg(pc);
+ change_mode = REQUEST_CHOWN;
+ break;
+
+ case 'G':
+ owner_username = poptGetOptArg(pc);
+ change_mode = REQUEST_CHGRP;
+ break;
+
+ case 'I':
+ owner_username = poptGetOptArg(pc);
+ change_mode = REQUEST_INHERIT;
+ break;
+ case 'm':
+ lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc));
+ break;
+ case 'x':
+ want_mxac = true;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+ if (inheritance && !the_acl) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ path = talloc_strdup(frame, poptGetArg(pc));
+ if (!path) {
+ return -1;
+ }
+
+ if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
+ printf("Invalid argument: %s\n", path);
+ return -1;
+ }
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ filename = talloc_strdup(frame, poptGetArg(pc));
+ if (!filename) {
+ return -1;
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ string_replace(path,'/','\\');
+
+ server = talloc_strdup(frame, path+2);
+ if (!server) {
+ return -1;
+ }
+ share = strchr_m(server,'\\');
+ if (share == NULL) {
+ printf("Invalid argument\n");
+ return -1;
+ }
+
+ *share = 0;
+ share++;
+
+ creds = samba_cmdline_get_creds();
+
+ /* Make connection to server */
+ if (!test_args) {
+ cli = connect_one(creds, server, share);
+ if (!cli) {
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(0);
+ }
+
+ string_replace(filename, '/', '\\');
+ if (filename[0] != '\\') {
+ filename = talloc_asprintf(frame,
+ "\\%s",
+ filename);
+ if (!filename) {
+ return -1;
+ }
+ }
+
+ status = cli_resolve_path(frame,
+ "",
+ creds,
+ cli,
+ filename,
+ &targetcli,
+ &targetfile);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status)));
+ return -1;
+ }
+
+ /* Perform requested action */
+
+ if (change_mode == REQUEST_INHERIT) {
+ result = inherit(targetcli, targetfile, owner_username);
+ } else if (change_mode != REQUEST_NONE) {
+ result = owner_set(targetcli, change_mode, targetfile, owner_username);
+ } else if (the_acl) {
+ if (inheritance) {
+ struct cacl_callback_state cbstate = {
+ .creds = creds,
+ .cli = targetcli,
+ .mode = mode,
+ .the_acl = the_acl,
+ .numeric = numeric,
+ };
+ result = inheritance_cacl_set(targetfile, &cbstate);
+ } else {
+ result = cacl_set(targetcli,
+ targetfile,
+ the_acl,
+ mode,
+ numeric);
+ }
+ } else {
+ if (save_file || restore_file) {
+ sddl = 1;
+ if (save_file) {
+ result = cacl_dump_dacl(cli, creds, filename);
+ } else {
+ result = cacl_restore(targetcli,
+ creds,
+ numeric, restore_file);
+ }
+ } else {
+ result = cacl_dump(targetcli, targetfile, numeric);
+ }
+ }
+
+ gfree_all();
+ TALLOC_FREE(frame);
+
+ return result;
+}
diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c
new file mode 100644
index 0000000..b318f55
--- /dev/null
+++ b/source3/utils/smbcontrol.c
@@ -0,0 +1,1870 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Send messages to other Samba daemons
+
+ Copyright (C) Tim Potter 2003
+ Copyright (C) Andrew Tridgell 1994-1998
+ Copyright (C) Martin Pool 2001-2002
+ Copyright (C) Simo Sorce 2002
+ Copyright (C) James Peach 2006
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/util/server_id.h"
+#include "lib/cmdline/cmdline.h"
+#include "librpc/gen_ndr/spoolss.h"
+#include "nt_printing.h"
+#include "printing/notify.h"
+#include "libsmb/nmblib.h"
+#include "messages.h"
+#include "util_tdb.h"
+#include "../lib/util/pidfile.h"
+#include "serverid.h"
+#include "lib/util/server_id_db.h"
+#include "cmdline_contexts.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/global_contexts.h"
+#include "lib/param/param.h"
+
+#ifdef HAVE_LIBUNWIND_H
+#include <libunwind.h>
+#endif
+
+#ifdef HAVE_LIBUNWIND_PTRACE_H
+#include <libunwind-ptrace.h>
+#endif
+
+#ifdef HAVE_SYS_PTRACE_H
+#include <sys/ptrace.h>
+#endif
+
+/* Default timeout value when waiting for replies (in seconds) */
+
+#define DEFAULT_TIMEOUT 10
+
+static int timeout = DEFAULT_TIMEOUT;
+static int num_replies; /* Used by message callback fns */
+
+/* Send a message to a destination pid. Zero means broadcast smbd. */
+
+static bool send_message(struct messaging_context *msg_ctx,
+ struct server_id pid, int msg_type,
+ const void *buf, int len)
+{
+ if (procid_to_pid(&pid) != 0)
+ return NT_STATUS_IS_OK(
+ messaging_send_buf(msg_ctx, pid, msg_type,
+ (const uint8_t *)buf, len));
+
+ messaging_send_all(msg_ctx, msg_type, buf, len);
+
+ return true;
+}
+
+static void smbcontrol_timeout(struct tevent_context *event_ctx,
+ struct tevent_timer *te,
+ struct timeval now,
+ void *private_data)
+{
+ bool *timed_out = (bool *)private_data;
+ TALLOC_FREE(te);
+ *timed_out = True;
+}
+
+/* Wait for one or more reply messages */
+
+static void wait_replies(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ bool multiple_replies)
+{
+ struct tevent_timer *te;
+ bool timed_out = False;
+
+ te = tevent_add_timer(ev_ctx, NULL,
+ timeval_current_ofs(timeout, 0),
+ smbcontrol_timeout, (void *)&timed_out);
+ if (te == NULL) {
+ DEBUG(0, ("tevent_add_timer failed\n"));
+ return;
+ }
+
+ while (!timed_out) {
+ int ret;
+ if (num_replies > 0 && !multiple_replies)
+ break;
+ ret = tevent_loop_once(ev_ctx);
+ if (ret != 0) {
+ break;
+ }
+ }
+}
+
+/* Message handler callback that displays the PID and a string on stdout */
+
+static void print_pid_string_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ struct server_id_buf pidstr;
+
+ printf("PID %s: %.*s", server_id_str_buf(pid, &pidstr),
+ (int)data->length, (const char *)data->data);
+ num_replies++;
+}
+
+/* Send no message. Useful for testing. */
+
+static bool do_noop(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> noop\n");
+ return False;
+ }
+
+ /* Move along, nothing to see here */
+
+ return True;
+}
+
+/* Send a debug string */
+
+static bool do_debug(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debug "
+ "<debug-string>\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_DEBUG, argv[1],
+ strlen(argv[1]) + 1);
+}
+
+
+static bool do_idmap(struct tevent_context *ev,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ static const char* usage = "Usage: "
+ "smbcontrol <dest> idmap <cmd> [arg]\n"
+ "\tcmd:"
+ "\tdelete \"UID <uid>\"|\"GID <gid>\"|<sid>\n"
+ "\t\tkill \"UID <uid>\"|\"GID <gid>\"|<sid>\n";
+ const char* arg = NULL;
+ int arglen = 0;
+ int msg_type;
+
+ switch (argc) {
+ case 2:
+ break;
+ case 3:
+ arg = argv[2];
+ arglen = strlen(arg) + 1;
+ break;
+ default:
+ fprintf(stderr, "%s", usage);
+ return false;
+ }
+
+ if (strcmp(argv[1], "delete") == 0) {
+ msg_type = ID_CACHE_DELETE;
+ }
+ else if (strcmp(argv[1], "kill") == 0) {
+ msg_type = ID_CACHE_KILL;
+ }
+ else if (strcmp(argv[1], "help") == 0) {
+ fprintf(stdout, "%s", usage);
+ return true;
+ }
+ else {
+ fprintf(stderr, "%s", usage);
+ return false;
+ }
+
+ return send_message(msg_ctx, pid, msg_type, arg, arglen);
+}
+
+
+#if defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE)
+
+/* Return the name of a process given it's PID. This will only work on Linux,
+ * but that's probably moot since this whole stack tracing implementation is
+ * Linux-specific anyway.
+ */
+static const char * procname(pid_t pid, char * buf, size_t bufsz)
+{
+ char path[64];
+ FILE * fp;
+
+ snprintf(path, sizeof(path), "/proc/%llu/cmdline",
+ (unsigned long long)pid);
+ if ((fp = fopen(path, "r")) == NULL) {
+ return NULL;
+ }
+
+ fgets(buf, bufsz, fp);
+
+ fclose(fp);
+ return buf;
+}
+
+static void print_stack_trace(pid_t pid, int * count)
+{
+ void * pinfo = NULL;
+ unw_addr_space_t aspace = NULL;
+ unw_cursor_t cursor;
+ unw_word_t ip, sp;
+
+ char nbuf[256];
+ unw_word_t off;
+
+ int ret;
+
+ if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
+ fprintf(stderr,
+ "Failed to attach to process %llu: %s\n",
+ (unsigned long long)pid, strerror(errno));
+ return;
+ }
+
+ /* Wait until the attach is complete. */
+ waitpid(pid, NULL, 0);
+
+ if (((pinfo = _UPT_create(pid)) == NULL) ||
+ ((aspace = unw_create_addr_space(&_UPT_accessors, 0)) == NULL)) {
+ /* Probably out of memory. */
+ fprintf(stderr,
+ "Unable to initialize stack unwind for process %llu\n",
+ (unsigned long long)pid);
+ goto cleanup;
+ }
+
+ if ((ret = unw_init_remote(&cursor, aspace, pinfo))) {
+ fprintf(stderr,
+ "Unable to unwind stack for process %llu: %s\n",
+ (unsigned long long)pid, unw_strerror(ret));
+ goto cleanup;
+ }
+
+ if (*count > 0) {
+ printf("\n");
+ }
+
+ if (procname(pid, nbuf, sizeof(nbuf))) {
+ printf("Stack trace for process %llu (%s):\n",
+ (unsigned long long)pid, nbuf);
+ } else {
+ printf("Stack trace for process %llu:\n",
+ (unsigned long long)pid);
+ }
+
+ while (unw_step(&cursor) > 0) {
+ ip = sp = off = 0;
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ unw_get_reg(&cursor, UNW_REG_SP, &sp);
+
+ ret = unw_get_proc_name(&cursor, nbuf, sizeof(nbuf), &off);
+ if (ret != 0 && ret != -UNW_ENOMEM) {
+ snprintf(nbuf, sizeof(nbuf), "<unknown symbol>");
+ }
+ printf(" %s + %#llx [ip=%#llx] [sp=%#llx]\n",
+ nbuf, (long long)off, (long long)ip,
+ (long long)sp);
+ }
+
+ (*count)++;
+
+cleanup:
+ if (aspace) {
+ unw_destroy_addr_space(aspace);
+ }
+
+ if (pinfo) {
+ _UPT_destroy(pinfo);
+ }
+
+ ptrace(PTRACE_DETACH, pid, NULL, NULL);
+}
+
+static int stack_trace_server(pid_t pid, void *priv)
+{
+ print_stack_trace(pid, (int *)priv);
+ return 0;
+}
+
+static bool do_daemon_stack_trace(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ pid_t dest;
+ int count = 0;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> stacktrace\n");
+ return False;
+ }
+
+ dest = procid_to_pid(&pid);
+
+ if (dest != 0) {
+ /* It would be nice to be able to make sure that this PID is
+ * the PID of a smbd/winbind/nmbd process, not some random PID
+ * the user liked the look of. It doesn't seem like it's worth
+ * the effort at the moment, however.
+ */
+ print_stack_trace(dest, &count);
+ } else {
+ messaging_dgm_forall(stack_trace_server, &count);
+ }
+
+ return True;
+}
+
+#else /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */
+
+static bool do_daemon_stack_trace(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ fprintf(stderr,
+ "Daemon stack tracing is not supported on this platform\n");
+ return False;
+}
+
+#endif /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */
+
+/* Inject a fault (fatal signal) into a running smbd */
+
+static bool do_inject_fault(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> inject "
+ "<bus|hup|term|internal|segv>\n");
+ return False;
+ }
+
+#if !defined(DEVELOPER) && !defined(ENABLE_SELFTEST)
+ fprintf(stderr, "Fault injection is only available in "
+ "developer and self test builds\n");
+ return False;
+#else /* DEVELOPER || ENABLE_SELFTEST */
+ {
+ int sig = 0;
+
+ if (strcmp(argv[1], "bus") == 0) {
+ sig = SIGBUS;
+ } else if (strcmp(argv[1], "hup") == 0) {
+ sig = SIGHUP;
+ } else if (strcmp(argv[1], "term") == 0) {
+ sig = SIGTERM;
+ } else if (strcmp(argv[1], "segv") == 0) {
+ sig = SIGSEGV;
+ } else if (strcmp(argv[1], "internal") == 0) {
+ /* Force an internal error, ie. an unclean exit. */
+ sig = -1;
+ } else {
+ fprintf(stderr, "Unknown signal name '%s'\n", argv[1]);
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_INJECT_FAULT,
+ &sig, sizeof(int));
+ }
+#endif /* DEVELOPER || ENABLE_SELFTEST */
+}
+
+static bool do_sleep(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+#if defined(DEVELOPER) || defined(ENABLE_SELFTEST)
+ unsigned int seconds;
+ long input;
+ const long MAX_SLEEP = 60 * 60; /* One hour maximum sleep */
+#endif
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> sleep seconds\n");
+ return False;
+ }
+
+#if !defined(DEVELOPER) && !defined(ENABLE_SELFTEST)
+ fprintf(stderr, "Sleep is only available in "
+ "developer and self test builds\n");
+ return False;
+#else /* DEVELOPER || ENABLE_SELFTEST */
+
+ input = atol(argv[1]);
+ if (input < 1 || input > MAX_SLEEP) {
+ fprintf(stderr,
+ "Invalid duration for sleep '%s'\n"
+ "It should be at least 1 second and no more than %ld\n",
+ argv[1],
+ MAX_SLEEP);
+ return False;
+ }
+ seconds = input;
+ return send_message(msg_ctx, pid,
+ MSG_SMB_SLEEP,
+ &seconds,
+ sizeof(unsigned int));
+#endif /* DEVELOPER || ENABLE_SELFTEST */
+}
+
+/* Force a browser election */
+
+static bool do_election(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> force-election\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_FORCE_ELECTION, NULL, 0);
+}
+
+/* Ping a samba daemon process */
+
+static void pong_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ struct server_id_buf src_string;
+ printf("PONG from pid %s\n", server_id_str_buf(pid, &src_string));
+ num_replies++;
+}
+
+static bool do_ping(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> ping\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_PING, NULL, 0))
+ return False;
+
+ messaging_register(msg_ctx, NULL, MSG_PONG, pong_cb);
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ messaging_deregister(msg_ctx, MSG_PONG, NULL);
+
+ return num_replies;
+}
+
+/* Set profiling options */
+
+static bool do_profile(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ int v;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profile "
+ "<off|count|on|flush>\n");
+ return False;
+ }
+
+ if (strcmp(argv[1], "off") == 0) {
+ v = 0;
+ } else if (strcmp(argv[1], "count") == 0) {
+ v = 1;
+ } else if (strcmp(argv[1], "on") == 0) {
+ v = 2;
+ } else if (strcmp(argv[1], "flush") == 0) {
+ v = 3;
+ } else {
+ fprintf(stderr, "Unknown profile command '%s'\n", argv[1]);
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_PROFILE, &v, sizeof(int));
+}
+
+/* Return the profiling level */
+
+static void profilelevel_cb(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ int level;
+ const char *s;
+
+ num_replies++;
+
+ if (data->length != sizeof(int)) {
+ fprintf(stderr, "invalid message length %ld returned\n",
+ (unsigned long)data->length);
+ return;
+ }
+
+ memcpy(&level, data->data, sizeof(int));
+
+ switch (level) {
+ case 0:
+ s = "not enabled";
+ break;
+ case 1:
+ s = "off";
+ break;
+ case 3:
+ s = "count only";
+ break;
+ case 7:
+ s = "count and time";
+ break;
+ default:
+ s = "BOGUS";
+ break;
+ }
+
+ printf("Profiling %s on pid %u\n",s,(unsigned int)procid_to_pid(&pid));
+}
+
+static void profilelevel_rqst(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ int v = 0;
+
+ /* Send back a dummy reply */
+
+ send_message(msg_ctx, pid, MSG_PROFILELEVEL, &v, sizeof(int));
+}
+
+static bool do_profilelevel(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> profilelevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_REQ_PROFILELEVEL, NULL, 0))
+ return False;
+
+ messaging_register(msg_ctx, NULL, MSG_PROFILELEVEL, profilelevel_cb);
+ messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL,
+ profilelevel_rqst);
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ messaging_deregister(msg_ctx, MSG_PROFILE, NULL);
+
+ return num_replies;
+}
+
+/* Display debug level settings */
+
+static bool do_debuglevel(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> debuglevel\n");
+ return False;
+ }
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_REQ_DEBUGLEVEL, NULL, 0))
+ return False;
+
+ messaging_register(msg_ctx, NULL, MSG_DEBUGLEVEL, print_pid_string_cb);
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ messaging_deregister(msg_ctx, MSG_DEBUGLEVEL, NULL);
+
+ return num_replies;
+}
+
+/* Send a print notify message */
+
+static bool do_printnotify(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ const char *cmd;
+
+ /* Check for subcommand */
+
+ if (argc == 1) {
+ fprintf(stderr, "Must specify subcommand:\n");
+ fprintf(stderr, "\tqueuepause <printername>\n");
+ fprintf(stderr, "\tqueueresume <printername>\n");
+ fprintf(stderr, "\tjobpause <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobresume <printername> <unix jobid>\n");
+ fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n");
+ fprintf(stderr, "\tprinter <printername> <comment|port|"
+ "driver> <value>\n");
+
+ return False;
+ }
+
+ cmd = argv[1];
+
+ if (strcmp(cmd, "queuepause") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuepause <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(ev_ctx, msg_ctx, argv[2],
+ PRINTER_STATUS_PAUSED);
+
+ goto send;
+
+ } else if (strcmp(cmd, "queueresume") == 0) {
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " queuereume <printername>\n");
+ return False;
+ }
+
+ notify_printer_status_byname(ev_ctx, msg_ctx, argv[2],
+ PRINTER_STATUS_OK);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobpause") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ ev_ctx, msg_ctx,
+ argv[2], jobid, JOB_STATUS_PAUSED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobresume") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ ev_ctx, msg_ctx,
+ argv[2], jobid, JOB_STATUS_QUEUED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "jobdelete") == 0) {
+ int jobid;
+
+ if (argc != 4) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify"
+ " jobpause <printername> <unix-jobid>\n");
+ return False;
+ }
+
+ jobid = atoi(argv[3]);
+
+ notify_job_status_byname(
+ ev_ctx, msg_ctx,
+ argv[2], jobid, JOB_STATUS_DELETING,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ notify_job_status_byname(
+ ev_ctx, msg_ctx,
+ argv[2], jobid, JOB_STATUS_DELETING|
+ JOB_STATUS_DELETED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "printer") == 0) {
+ uint32_t attribute;
+
+ if (argc != 5) {
+ fprintf(stderr, "Usage: smbcontrol <dest> printnotify "
+ "printer <printername> <comment|port|driver> "
+ "<value>\n");
+ return False;
+ }
+
+ if (strcmp(argv[3], "comment") == 0) {
+ attribute = PRINTER_NOTIFY_FIELD_COMMENT;
+ } else if (strcmp(argv[3], "port") == 0) {
+ attribute = PRINTER_NOTIFY_FIELD_PORT_NAME;
+ } else if (strcmp(argv[3], "driver") == 0) {
+ attribute = PRINTER_NOTIFY_FIELD_DRIVER_NAME;
+ } else {
+ fprintf(stderr, "Invalid printer command '%s'\n",
+ argv[3]);
+ return False;
+ }
+
+ notify_printer_byname(ev_ctx, msg_ctx, argv[2], attribute,
+ discard_const_p(char, argv[4]));
+
+ goto send;
+ }
+
+ fprintf(stderr, "Invalid subcommand '%s'\n", cmd);
+ return False;
+
+send:
+ print_notify_send_messages(msg_ctx, 0);
+ return True;
+}
+
+/* Close a share */
+
+static bool do_closeshare(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> close-share "
+ "<sharename>\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_FORCE_TDIS, argv[1],
+ strlen(argv[1]) + 1);
+}
+
+/*
+ * Close a share if access denied by now
+ **/
+
+static bool do_close_denied_share(
+ struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> close-denied-share "
+ "<sharename>\n");
+ return False;
+ }
+
+ return send_message(
+ msg_ctx,
+ pid,
+ MSG_SMB_FORCE_TDIS_DENIED,
+ argv[1],
+ strlen(argv[1]) + 1);
+}
+
+/* Kill a client by IP address */
+static bool do_kill_client_by_ip(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> kill-client-ip "
+ "<IP address>\n");
+ return false;
+ }
+
+ if (!is_ipaddress_v4(argv[1]) && !is_ipaddress_v6(argv[1])) {
+ fprintf(stderr, "%s is not a valid IP address!\n", argv[1]);
+ return false;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_KILL_CLIENT_IP,
+ argv[1], strlen(argv[1]) + 1);
+}
+
+/* Tell winbindd an IP got dropped */
+
+static bool do_ip_dropped(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> ip-dropped "
+ "<ip-address>\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_WINBIND_IP_DROPPED, argv[1],
+ strlen(argv[1]) + 1);
+}
+
+/* Display talloc pool usage */
+
+static bool do_poolusage(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id dst,
+ const int argc, const char **argv)
+{
+ pid_t pid = procid_to_pid(&dst);
+ int stdout_fd = 1;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n");
+ return False;
+ }
+
+ if (pid == 0) {
+ fprintf(stderr, "Can only send to a specific PID\n");
+ return false;
+ }
+
+ messaging_send_iov(
+ msg_ctx,
+ dst,
+ MSG_REQ_POOL_USAGE,
+ NULL,
+ 0,
+ &stdout_fd,
+ 1);
+
+ return true;
+}
+
+static bool do_rpc_dump_status(
+ struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id dst,
+ const int argc,
+ const char **argv)
+{
+ pid_t pid = procid_to_pid(&dst);
+ int stdout_fd = 1;
+
+ if (argc != 1) {
+ fprintf(stderr,
+ "Usage: smbcontrol <dest> rpc-dump-status\n");
+ return False;
+ }
+
+ if (pid == 0) {
+ fprintf(stderr, "Can only send to a specific PID\n");
+ return false;
+ }
+
+ messaging_send_iov(
+ msg_ctx,
+ dst,
+ MSG_RPC_DUMP_STATUS,
+ NULL,
+ 0,
+ &stdout_fd,
+ 1);
+
+ return true;
+}
+
+/* Fetch and print the ringbuf log */
+
+static void print_ringbuf_log_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ printf("%s", (const char *)data->data);
+ num_replies++;
+}
+
+static bool do_ringbuflog(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> ringbuf-log\n");
+ return false;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_RINGBUF_LOG,
+ print_ringbuf_log_cb);
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_REQ_RINGBUF_LOG, NULL, 0)) {
+ return false;
+ }
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0) {
+ printf("No replies received\n");
+ }
+
+ messaging_deregister(msg_ctx, MSG_RINGBUF_LOG, NULL);
+
+ return num_replies != 0;
+}
+
+/* Perform a dmalloc mark */
+
+static bool do_dmalloc_mark(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> dmalloc-mark\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_REQ_DMALLOC_MARK, NULL, 0);
+}
+
+/* Perform a dmalloc changed */
+
+static bool do_dmalloc_changed(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> "
+ "dmalloc-log-changed\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_REQ_DMALLOC_LOG_CHANGED,
+ NULL, 0);
+}
+
+static void print_uint32_cb(struct messaging_context *msg, void *private_data,
+ uint32_t msg_type, struct server_id pid,
+ DATA_BLOB *data)
+{
+ uint32_t num_children;
+
+ if (data->length != sizeof(uint32_t)) {
+ printf("Invalid response: %d bytes long\n",
+ (int)data->length);
+ goto done;
+ }
+ num_children = IVAL(data->data, 0);
+ printf("%u children\n", (unsigned)num_children);
+done:
+ num_replies++;
+}
+
+static bool do_num_children(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> num-children\n");
+ return False;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_SMB_NUM_CHILDREN,
+ print_uint32_cb);
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_SMB_TELL_NUM_CHILDREN, NULL, 0))
+ return false;
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ messaging_deregister(msg_ctx, MSG_SMB_NUM_CHILDREN, NULL);
+
+ return num_replies;
+}
+
+static bool do_msg_cleanup(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ int ret;
+
+ ret = messaging_cleanup(msg_ctx, pid.pid);
+
+ printf("cleanup(%u) returned %s\n", (unsigned)pid.pid,
+ ret ? strerror(ret) : "ok");
+
+ return (ret == 0);
+}
+
+/* Shutdown a server process */
+
+static bool do_shutdown(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> shutdown\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SHUTDOWN, NULL, 0);
+}
+
+/* Notify a driver upgrade */
+
+static bool do_drvupgrade(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> drvupgrade "
+ "<driver-name>\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_PRINTER_DRVUPGRADE, argv[1],
+ strlen(argv[1]) + 1);
+}
+
+static bool do_winbind_online(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ char *db_path;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd online\n");
+ return False;
+ }
+
+ db_path = state_path(talloc_tos(), "winbindd_cache.tdb");
+ if (db_path == NULL) {
+ return false;
+ }
+
+ /* Remove the entry in the winbindd_cache tdb to tell a later
+ starting winbindd that we're online. */
+
+ tdb = tdb_open_log(db_path, 0, TDB_DEFAULT, O_RDWR, 0600);
+ if (!tdb) {
+ fprintf(stderr, "Cannot open the tdb %s for writing.\n",
+ db_path);
+ TALLOC_FREE(db_path);
+ return False;
+ }
+
+ TALLOC_FREE(db_path);
+ tdb_delete_bystring(tdb, "WINBINDD_OFFLINE");
+ tdb_close(tdb);
+
+ return send_message(msg_ctx, pid, MSG_WINBIND_ONLINE, NULL, 0);
+}
+
+static bool do_winbind_offline(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ bool ret = False;
+ int retry = 0;
+ char *db_path;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd offline\n");
+ return False;
+ }
+
+ db_path = state_path(talloc_tos(), "winbindd_cache.tdb");
+ if (db_path == NULL) {
+ return false;
+ }
+
+ /* Create an entry in the winbindd_cache tdb to tell a later
+ starting winbindd that we're offline. We may actually create
+ it here... */
+
+ tdb = tdb_open_log(db_path,
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_DEFAULT|TDB_INCOMPATIBLE_HASH /* TDB_CLEAR_IF_FIRST */,
+ O_RDWR|O_CREAT, 0600);
+
+ if (!tdb) {
+ fprintf(stderr, "Cannot open the tdb %s for writing.\n",
+ db_path);
+ TALLOC_FREE(db_path);
+ return False;
+ }
+ TALLOC_FREE(db_path);
+
+ /* There's a potential race condition that if a child
+ winbindd detects a domain is online at the same time
+ we're trying to tell it to go offline that it might
+ delete the record we add between us adding it and
+ sending the message. Minimize this by retrying up to
+ 5 times. */
+
+ for (retry = 0; retry < 5; retry++) {
+ uint8_t buf[4];
+ TDB_DATA d = { .dptr = buf, .dsize = sizeof(buf) };
+
+ SIVAL(buf, 0, time(NULL));
+
+ tdb_store_bystring(tdb, "WINBINDD_OFFLINE", d, TDB_INSERT);
+
+ ret = send_message(msg_ctx, pid, MSG_WINBIND_OFFLINE,
+ NULL, 0);
+
+ /* Check that the entry "WINBINDD_OFFLINE" still exists. */
+ d = tdb_fetch_bystring( tdb, "WINBINDD_OFFLINE" );
+ if (d.dptr != NULL && d.dsize == 4) {
+ SAFE_FREE(d.dptr);
+ break;
+ }
+
+ SAFE_FREE(d.dptr);
+ DEBUG(10,("do_winbind_offline: offline state not set - retrying.\n"));
+ }
+
+ tdb_close(tdb);
+ return ret;
+}
+
+static bool do_winbind_onlinestatus(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd onlinestatus\n");
+ return False;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_WINBIND_ONLINESTATUS,
+ print_pid_string_cb);
+
+ if (!send_message(msg_ctx, pid, MSG_WINBIND_ONLINESTATUS, NULL, 0)) {
+ return False;
+ }
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0)
+ printf("No replies received\n");
+
+ messaging_deregister(msg_ctx, MSG_WINBIND_ONLINESTATUS, NULL);
+
+ return num_replies;
+}
+
+static bool do_winbind_dump_domain_list(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ const char *domain = NULL;
+ int domain_len = 0;
+
+ if (argc < 1 || argc > 2) {
+ fprintf(stderr, "Usage: smbcontrol <dest> dump-domain-list "
+ "<domain>\n");
+ return false;
+ }
+
+ if (argc == 2) {
+ domain = argv[1];
+ domain_len = strlen(argv[1]) + 1;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_WINBIND_DUMP_DOMAIN_LIST,
+ print_pid_string_cb);
+
+ if (!send_message(msg_ctx, pid, MSG_WINBIND_DUMP_DOMAIN_LIST,
+ domain, domain_len))
+ {
+ return false;
+ }
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ if (num_replies == 0) {
+ printf("No replies received\n");
+ }
+
+ messaging_deregister(msg_ctx, MSG_WINBIND_DUMP_DOMAIN_LIST, NULL);
+
+ return num_replies;
+}
+
+static bool do_msg_disconnect_dc(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> disconnect-dc\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_WINBIND_DISCONNECT_DC, NULL, 0);
+}
+
+static void winbind_validate_cache_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ struct server_id_buf src_string;
+ printf("Winbindd cache is %svalid. (answer from pid %s)\n",
+ (*(data->data) == 0 ? "" : "NOT "),
+ server_id_str_buf(pid, &src_string));
+ num_replies++;
+}
+
+static bool do_winbind_validate_cache(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ struct server_id myid;
+
+ myid = messaging_server_id(msg_ctx);
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd validate-cache\n");
+ return False;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_WINBIND_VALIDATE_CACHE,
+ winbind_validate_cache_cb);
+
+ if (!send_message(msg_ctx, pid, MSG_WINBIND_VALIDATE_CACHE, &myid,
+ sizeof(myid))) {
+ return False;
+ }
+
+ wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0);
+
+ if (num_replies == 0) {
+ printf("No replies received\n");
+ }
+
+ messaging_deregister(msg_ctx, MSG_WINBIND_VALIDATE_CACHE, NULL);
+
+ return num_replies;
+}
+
+static bool do_reload_certs(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol ldap_server reload-certs \n");
+ return false;
+ }
+
+ return send_message(msg_ctx, pid, MSG_RELOAD_TLS_CERTIFICATES, NULL, 0);
+}
+static bool do_reload_config(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> reload-config\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_CONF_UPDATED, NULL, 0);
+}
+
+static bool do_reload_printers(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> reload-printers\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_PRINTER_PCAP, NULL, 0);
+}
+
+static void my_make_nmb_name( struct nmb_name *n, const char *name, int type)
+{
+ fstring unix_name;
+ memset( (char *)n, '\0', sizeof(struct nmb_name) );
+ fstrcpy(unix_name, name);
+ (void)strupper_m(unix_name);
+ push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE);
+ n->name_type = (unsigned int)type & 0xFF;
+ push_ascii(n->scope, lp_netbios_scope(), 64, STR_TERMINATE);
+}
+
+static bool do_nodestatus(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ struct packet_struct p;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: smbcontrol nmbd nodestatus <ip>\n");
+ return False;
+ }
+
+ ZERO_STRUCT(p);
+
+ p.ip = interpret_addr2(argv[1]);
+ p.port = 137;
+ p.packet_type = NMB_PACKET;
+
+ p.packet.nmb.header.name_trn_id = 10;
+ p.packet.nmb.header.opcode = 0;
+ p.packet.nmb.header.response = False;
+ p.packet.nmb.header.nm_flags.bcast = False;
+ p.packet.nmb.header.nm_flags.recursion_available = False;
+ p.packet.nmb.header.nm_flags.recursion_desired = False;
+ p.packet.nmb.header.nm_flags.trunc = False;
+ p.packet.nmb.header.nm_flags.authoritative = False;
+ p.packet.nmb.header.rcode = 0;
+ p.packet.nmb.header.qdcount = 1;
+ p.packet.nmb.header.ancount = 0;
+ p.packet.nmb.header.nscount = 0;
+ p.packet.nmb.header.arcount = 0;
+ my_make_nmb_name(&p.packet.nmb.question.question_name, "*", 0x00);
+ p.packet.nmb.question.question_type = 0x21;
+ p.packet.nmb.question.question_class = 0x1;
+
+ return send_message(msg_ctx, pid, MSG_SEND_PACKET, &p, sizeof(p));
+}
+
+static bool do_notify_cleanup(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol smbd notify-cleanup\n");
+ return false;
+ }
+ return send_message(msg_ctx, pid, MSG_SMB_NOTIFY_CLEANUP, NULL, 0);
+}
+
+/* A list of message type supported */
+
+static const struct {
+ const char *name; /* Option name */
+ bool (*fn)(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv);
+ const char *help; /* Short help text */
+} msg_types[] = {
+ {
+ .name = "debug",
+ .fn = do_debug,
+ .help = "Set debuglevel",
+ },
+ {
+ .name = "idmap",
+ .fn = do_idmap,
+ .help = "Manipulate idmap cache",
+ },
+ {
+ .name = "force-election",
+ .fn = do_election,
+ .help = "Force a browse election",
+ },
+ {
+ .name = "ping",
+ .fn = do_ping,
+ .help = "Elicit a response",
+ },
+ {
+ .name = "profile",
+ .fn = do_profile,
+ .help = "",
+ },
+ {
+ .name = "inject",
+ .fn = do_inject_fault,
+ .help = "Inject a fatal signal into a running smbd"},
+ {
+ .name = "stacktrace",
+ .fn = do_daemon_stack_trace,
+ .help = "Display a stack trace of a daemon",
+ },
+ {
+ .name = "profilelevel",
+ .fn = do_profilelevel,
+ .help = "",
+ },
+ {
+ .name = "debuglevel",
+ .fn = do_debuglevel,
+ .help = "Display current debuglevels",
+ },
+ {
+ .name = "printnotify",
+ .fn = do_printnotify,
+ .help = "Send a print notify message",
+ },
+ {
+ .name = "close-share",
+ .fn = do_closeshare,
+ .help = "Forcibly disconnect a share",
+ },
+ {
+ .name = "close-denied-share",
+ .fn = do_close_denied_share,
+ .help = "Forcibly disconnect users from shares disallowed now",
+ },
+ {
+ .name = "kill-client-ip",
+ .fn = do_kill_client_by_ip,
+ .help = "Forcibly disconnect a client with a specific IP address",
+ },
+ {
+ .name = "ip-dropped",
+ .fn = do_ip_dropped,
+ .help = "Tell winbind that an IP got dropped",
+ },
+ {
+ .name = "pool-usage",
+ .fn = do_poolusage,
+ .help = "Display talloc memory usage",
+ },
+ {
+ .name = "rpc-dump-status",
+ .fn = do_rpc_dump_status,
+ .help = "Display rpc status",
+ },
+ {
+ .name = "ringbuf-log",
+ .fn = do_ringbuflog,
+ .help = "Display ringbuf log",
+ },
+ {
+ .name = "dmalloc-mark",
+ .fn = do_dmalloc_mark,
+ .help = "",
+ },
+ {
+ .name = "dmalloc-log-changed",
+ .fn = do_dmalloc_changed,
+ .help = "",
+ },
+ {
+ .name = "shutdown",
+ .fn = do_shutdown,
+ .help = "Shut down daemon",
+ },
+ {
+ .name = "drvupgrade",
+ .fn = do_drvupgrade,
+ .help = "Notify a printer driver has changed",
+ },
+ {
+ .name = "reload-certs",
+ .fn = do_reload_certs,
+ .help = "Reload TLS certificates"
+ },
+ {
+ .name = "reload-config",
+ .fn = do_reload_config,
+ .help = "Force smbd or winbindd to reload config file"},
+ {
+ .name = "reload-printers",
+ .fn = do_reload_printers,
+ .help = "Force smbd to reload printers"},
+ {
+ .name = "nodestatus",
+ .fn = do_nodestatus,
+ .help = "Ask nmbd to do a node status request"},
+ {
+ .name = "online",
+ .fn = do_winbind_online,
+ .help = "Ask winbind to go into online state"},
+ {
+ .name = "offline",
+ .fn = do_winbind_offline,
+ .help = "Ask winbind to go into offline state"},
+ {
+ .name = "onlinestatus",
+ .fn = do_winbind_onlinestatus,
+ .help = "Request winbind online status"},
+ {
+ .name = "validate-cache" ,
+ .fn = do_winbind_validate_cache,
+ .help = "Validate winbind's credential cache",
+ },
+ {
+ .name = "dump-domain-list",
+ .fn = do_winbind_dump_domain_list,
+ .help = "Dump winbind domain list"},
+ {
+ .name = "disconnect-dc",
+ .fn = do_msg_disconnect_dc,
+ },
+ {
+ .name = "notify-cleanup",
+ .fn = do_notify_cleanup,
+ },
+ {
+ .name = "num-children",
+ .fn = do_num_children,
+ .help = "Print number of smbd child processes",
+ },
+ {
+ .name = "msg-cleanup",
+ .fn = do_msg_cleanup,
+ },
+ {
+ .name = "noop",
+ .fn = do_noop,
+ .help = "Do nothing",
+ },
+ {
+ .name = "sleep",
+ .fn = do_sleep,
+ .help = "Cause the target process to sleep",
+ },
+ { .name = NULL, },
+};
+
+/* Display usage information */
+
+static void usage(poptContext pc)
+{
+ int i;
+
+ poptPrintHelp(pc, stderr, 0);
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<destination> is one of \"nmbd\", \"smbd\", \"winbindd\", "
+ "\"ldap_server\" or a process ID\n");
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<message-type> is one of:\n");
+
+ for (i = 0; msg_types[i].name; i++) {
+ const char *help = msg_types[i].help;
+ if (help == NULL) {
+ help = "";
+ }
+ fprintf(stderr, "\t%-30s%s\n", msg_types[i].name, help);
+ }
+
+ fprintf(stderr, "\n");
+
+ exit(1);
+}
+
+/* Return the pid number for a string destination */
+
+static struct server_id parse_dest(struct messaging_context *msg,
+ const char *dest)
+{
+ struct server_id result = {
+ .pid = (uint64_t)-1,
+ };
+ pid_t pid;
+ struct server_id_db *names_db = NULL;
+ bool ok;
+
+ /* Zero is a special return value for broadcast to all processes */
+
+ if (strequal(dest, "all")) {
+ return interpret_pid(MSG_BROADCAST_PID_STR);
+ }
+
+ /* Try self - useful for testing */
+
+ if (strequal(dest, "self")) {
+ return messaging_server_id(msg);
+ }
+
+ /* Fix winbind typo. */
+ if (strequal(dest, "winbind")) {
+ dest = "winbindd";
+ }
+
+ /* Check for numeric pid number */
+ result = interpret_pid(dest);
+
+ /* Zero isn't valid if not "all". */
+ if (result.pid && procid_valid(&result)) {
+ return result;
+ }
+
+ /* Look up other destinations in pidfile directory */
+
+ if ((pid = pidfile_pid(lp_pid_directory(), dest)) != 0) {
+ return pid_to_procid(pid);
+ }
+
+ names_db = messaging_names_db(msg);
+ if (names_db == NULL) {
+ goto fail;
+ }
+ ok = server_id_db_lookup_one(names_db, dest, &result);
+ if (ok) {
+ return result;
+ }
+
+fail:
+ fprintf(stderr,"Can't find pid for destination '%s'\n", dest);
+
+ return result;
+}
+
+/* Execute smbcontrol command */
+
+static bool do_command(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ int argc, const char **argv)
+{
+ const char *dest = argv[0], *command = argv[1];
+ struct server_id pid;
+ int i;
+
+ /* Check destination */
+
+ pid = parse_dest(msg_ctx, dest);
+ if (!procid_valid(&pid)) {
+ return False;
+ }
+
+ /* Check command */
+
+ for (i = 0; msg_types[i].name; i++) {
+ if (strequal(command, msg_types[i].name))
+ return msg_types[i].fn(ev_ctx, msg_ctx, pid,
+ argc - 1, argv + 1);
+ }
+
+ fprintf(stderr, "smbcontrol: unknown command '%s'\n", command);
+
+ return False;
+}
+
+static void smbcontrol_help(poptContext pc,
+ enum poptCallbackReason preason,
+ struct poptOption * poption,
+ const char * parg,
+ void * pdata)
+{
+ if (poption->shortName != '?') {
+ poptPrintUsage(pc, stdout, 0);
+ } else {
+ usage(pc);
+ }
+
+ exit(0);
+}
+
+struct poptOption help_options[] = {
+ { NULL, '\0', POPT_ARG_CALLBACK, (void *)&smbcontrol_help, '\0',
+ NULL, NULL },
+ { "help", '?', 0, NULL, '?', "Show this help message", NULL },
+ { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL },
+ {0}
+} ;
+
+/* Main program */
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ int opt;
+ struct tevent_context *evt_ctx;
+ struct messaging_context *msg_ctx;
+
+ struct poptOption long_options[] = {
+ /* POPT_AUTOHELP */
+ { NULL, '\0', POPT_ARG_INCLUDE_TABLE, help_options,
+ 0, "Help options:", NULL },
+ { "timeout", 't', POPT_ARG_INT, &timeout, 't',
+ "Set timeout value in seconds", "TIMEOUT" },
+
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+ int ret = 0;
+ bool ok;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+ /* Parse command line arguments using popt */
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "[OPTION...] <destination> <message-type> "
+ "<parameters>");
+
+ if (argc == 1)
+ usage(pc);
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case 't': /* --timeout */
+ break;
+ default:
+ fprintf(stderr, "Invalid option\n");
+ poptPrintHelp(pc, stderr, 0);
+ break;
+ }
+ }
+
+ /* We should now have the remaining command line arguments in
+ argv. The argc parameter should have been decremented to the
+ correct value in the above switch statement. */
+
+ argv = (const char **)poptGetArgs(pc);
+ argc = 0;
+ if (argv != NULL) {
+ while (argv[argc] != NULL) {
+ argc++;
+ }
+ }
+
+ if (argc <= 1)
+ usage(pc);
+
+ msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
+ if (msg_ctx == NULL) {
+ fprintf(stderr,
+ "Could not init messaging context, not root?\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ evt_ctx = global_event_context();
+
+ /* Need to invert sense of return code -- samba
+ * routines mostly return True==1 for success, but
+ * shell needs 0. */
+
+ ret = !do_command(evt_ctx, msg_ctx, argc, argv);
+
+ cmdline_messaging_context_free();
+ gfree_all();
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
new file mode 100644
index 0000000..655da7d
--- /dev/null
+++ b/source3/utils/smbcquotas.c
@@ -0,0 +1,832 @@
+/*
+ Unix SMB/CIFS implementation.
+ QUOTA get/set utility
+
+ Copyright (C) Andrew Tridgell 2000
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jeremy Allison 2000
+ Copyright (C) Stefan (metze) Metzmacher 2003
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_lsa.h"
+#include "rpc_client/cli_lsarpc.h"
+#include "fake_file.h"
+#include "../libcli/security/security.h"
+#include "libsmb/libsmb.h"
+#include "lib/param/param.h"
+
+static char *server;
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static bool numeric;
+static bool verbose;
+
+enum todo_values {NOOP_QUOTA=0,FS_QUOTA,USER_QUOTA,LIST_QUOTA,SET_QUOTA};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+static struct cli_state *cli_ipc;
+static struct rpc_pipe_client *global_pipe_hnd;
+static struct policy_handle pol;
+static bool got_policy_hnd;
+
+static struct cli_state *connect_one(const char *share);
+
+/* Open cli connection and policy handle */
+
+static bool cli_open_policy_hnd(void)
+{
+ /* Initialise cli LSA connection */
+
+ if (!cli_ipc) {
+ NTSTATUS ret;
+ cli_ipc = connect_one("IPC$");
+ ret = cli_rpc_pipe_open_noauth(cli_ipc,
+ &ndr_table_lsarpc,
+ &global_pipe_hnd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_FLAG_MAXIMUM_ALLOWED,
+ but NT sends 0x2000000 so we might as well do it too. */
+
+ if (!NT_STATUS_IS_OK(rpccli_lsa_open_policy(global_pipe_hnd, talloc_tos(), True,
+ GENERIC_EXECUTE_ACCESS, &pol))) {
+ return False;
+ }
+
+ got_policy_hnd = True;
+ }
+
+ return True;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(fstring str, struct dom_sid *sid, bool _numeric)
+{
+ char **domains = NULL;
+ char **names = NULL;
+ enum lsa_SidType *types = NULL;
+
+ sid_to_fstring(str, sid);
+
+ if (_numeric) return;
+
+ /* Ask LSA to convert the sid to a name */
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(global_pipe_hnd, talloc_tos(),
+ &pol, 1, sid, &domains,
+ &names, &types)) ||
+ !domains || !domains[0] || !names || !names[0]) {
+ return;
+ }
+
+ /* Converted OK */
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domains[0], lp_winbind_separator(),
+ names[0]);
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static bool StringToSid(struct dom_sid *sid, const char *str)
+{
+ enum lsa_SidType *types = NULL;
+ struct dom_sid *sids = NULL;
+ bool result = True;
+
+ if (string_to_sid(sid, str)) {
+ return true;
+ }
+
+ if (!cli_open_policy_hnd() ||
+ !NT_STATUS_IS_OK(rpccli_lsa_lookup_names(global_pipe_hnd, talloc_tos(),
+ &pol, 1, &str, NULL, 1, &sids,
+ &types))) {
+ result = False;
+ goto done;
+ }
+
+ sid_copy(sid, &sids[0]);
+ done:
+
+ return result;
+}
+
+#define QUOTA_GET 1
+#define QUOTA_SETLIM 2
+#define QUOTA_SETFLAGS 3
+#define QUOTA_LIST 4
+
+enum {PARSE_FLAGS,PARSE_LIM};
+
+static int parse_quota_set(TALLOC_CTX *ctx,
+ char *set_str,
+ char **pp_username_str,
+ enum SMB_QUOTA_TYPE *qtype,
+ int *cmd,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ char *p = set_str,*p2;
+ int todo;
+ bool stop = False;
+ bool enable = False;
+ bool deny = False;
+
+ *pp_username_str = NULL;
+ if (strnequal(set_str,"UQLIM:",6)) {
+ p += 6;
+ *qtype = SMB_USER_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ if ((p2=strstr(p,":"))==NULL) {
+ return -1;
+ }
+
+ *p2 = '\0';
+ p2++;
+
+ *pp_username_str = talloc_strdup(ctx, p);
+ p = p2;
+ } else if (strnequal(set_str,"FSQLIM:",7)) {
+ p +=7;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETLIM;
+ todo = PARSE_LIM;
+ } else if (strnequal(set_str,"FSQFLAGS:",9)) {
+ p +=9;
+ todo = PARSE_FLAGS;
+ *qtype = SMB_USER_FS_QUOTA_TYPE;
+ *cmd = QUOTA_SETFLAGS;
+ } else {
+ return -1;
+ }
+
+ switch (todo) {
+ case PARSE_LIM:
+ if (sscanf(p,"%"SCNu64"/%"SCNu64,&pqt->softlim,
+ &pqt->hardlim) != 2)
+ {
+ return -1;
+ }
+
+ break;
+ case PARSE_FLAGS:
+ while (!stop) {
+
+ if ((p2=strstr(p,"/"))==NULL) {
+ stop = True;
+ } else {
+ *p2 = '\0';
+ p2++;
+ }
+
+ if (strnequal(p,"QUOTA_ENABLED",13)) {
+ enable = True;
+ } else if (strnequal(p,"DENY_DISK",9)) {
+ deny = True;
+ } else if (strnequal(p,"LOG_SOFTLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_THRESHOLD;
+ } else if (strnequal(p,"LOG_HARDLIMIT",13)) {
+ pqt->qflags |= QUOTAS_LOG_LIMIT;
+ } else {
+ return -1;
+ }
+
+ p=p2;
+ }
+
+ if (deny) {
+ pqt->qflags |= QUOTAS_DENY_DISK;
+ } else if (enable) {
+ pqt->qflags |= QUOTAS_ENABLED;
+ }
+
+ break;
+ }
+
+ return 0;
+}
+
+
+static const char *quota_str_static(uint64_t val, bool special, bool _numeric)
+{
+ const char *result;
+
+ if (!_numeric && special && val == 0) {
+ return "NO LIMIT";
+ }
+ result = talloc_asprintf(talloc_tos(), "%"PRIu64, val);
+ SMB_ASSERT(result != NULL);
+ return result;
+}
+
+static void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, bool _verbose,
+ bool _numeric,
+ void (*_sidtostring)(fstring str,
+ struct dom_sid *sid,
+ bool _numeric))
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!qt) {
+ smb_panic("dump_ntquota() called with NULL pointer");
+ }
+
+ switch (qt->qtype) {
+ case SMB_USER_FS_QUOTA_TYPE:
+ {
+ d_printf("File System QUOTAS:\n");
+ d_printf("Limits:\n");
+ d_printf(" Default Soft Limit: %15s\n",
+ quota_str_static(qt->softlim,True,_numeric));
+ d_printf(" Default Hard Limit: %15s\n",
+ quota_str_static(qt->hardlim,True,_numeric));
+ d_printf("Quota Flags:\n");
+ d_printf(" Quotas Enabled: %s\n",
+ ((qt->qflags&QUOTAS_ENABLED)
+ ||(qt->qflags&QUOTAS_DENY_DISK))?"On":"Off");
+ d_printf(" Deny Disk: %s\n",
+ (qt->qflags&QUOTAS_DENY_DISK)?"On":"Off");
+ d_printf(" Log Soft Limit: %s\n",
+ (qt->qflags&QUOTAS_LOG_THRESHOLD)?"On":"Off");
+ d_printf(" Log Hard Limit: %s\n",
+ (qt->qflags&QUOTAS_LOG_LIMIT)?"On":"Off");
+ }
+ break;
+ case SMB_USER_QUOTA_TYPE:
+ {
+ fstring username_str = {0};
+
+ if (_sidtostring) {
+ _sidtostring(username_str,&qt->sid,_numeric);
+ } else {
+ sid_to_fstring(username_str, &qt->sid);
+ }
+
+ if (_verbose) {
+ d_printf("Quotas for User: %s\n",username_str);
+ d_printf("Used Space: %15s\n",
+ quota_str_static(qt->usedspace,False,
+ _numeric));
+ d_printf("Soft Limit: %15s\n",
+ quota_str_static(qt->softlim,True,
+ _numeric));
+ d_printf("Hard Limit: %15s\n",
+ quota_str_static(qt->hardlim,True,_numeric));
+ } else {
+ d_printf("%-30s: ",username_str);
+ d_printf("%15s/",quota_str_static(
+ qt->usedspace,False,_numeric));
+ d_printf("%15s/",quota_str_static(
+ qt->softlim,True,_numeric));
+ d_printf("%15s\n",quota_str_static(
+ qt->hardlim,True,_numeric));
+ }
+ }
+ break;
+ default:
+ d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype);
+ }
+ TALLOC_FREE(frame);
+ return;
+}
+
+static void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, bool _verbose,
+ bool _numeric,
+ void (*_sidtostring)(fstring str,
+ struct dom_sid *sid,
+ bool _numeric))
+{
+ SMB_NTQUOTA_LIST *cur;
+
+ for (cur = *qtl;cur;cur = cur->next) {
+ if (cur->quotas)
+ dump_ntquota(cur->quotas,_verbose,_numeric,
+ _sidtostring);
+ }
+}
+
+static int do_quota(struct cli_state *cli,
+ enum SMB_QUOTA_TYPE qtype,
+ uint16_t cmd,
+ const char *username_str,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint32_t fs_attrs = 0;
+ uint16_t quota_fnum = 0;
+ SMB_NTQUOTA_LIST *qtl = NULL;
+ TALLOC_CTX *qtl_ctx = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ NTSTATUS status;
+
+ ZERO_STRUCT(qt);
+
+ status = cli_get_fs_attr_info(cli, &fs_attrs);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Failed to get the filesystem attributes %s.\n",
+ nt_errstr(status));
+ return -1;
+ }
+
+ if (!(fs_attrs & FILE_VOLUME_QUOTAS)) {
+ d_printf("Quotas are not supported by the server.\n");
+ return 0;
+ }
+
+ status = cli_get_quota_handle(cli, &quota_fnum);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("Quotas are not enabled on this share.\n");
+ d_printf("Failed to open %s %s.\n",
+ FAKE_FILE_NAME_QUOTA_WIN32,
+ nt_errstr(status));
+ return -1;
+ }
+
+ switch(qtype) {
+ case SMB_USER_QUOTA_TYPE:
+ if (!StringToSid(&qt.sid, username_str)) {
+ d_printf("StringToSid() failed for [%s]\n",username_str);
+ return -1;
+ }
+
+ switch(cmd) {
+ case QUOTA_GET:
+ status = cli_get_user_quota(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ nt_errstr(status),
+ username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_SETLIM:
+ pqt->sid = qt.sid;
+ if ((qtl_ctx = talloc_init(
+ "SMB_USER_QUOTA_SET")) ==
+ NULL) {
+ return -1;
+ }
+
+ if (!add_record_to_ntquota_list(
+ qtl_ctx, pqt, &qtl)) {
+ TALLOC_FREE(qtl_ctx);
+ return -1;
+ }
+
+ status = cli_set_user_quota(
+ cli, quota_fnum, qtl);
+ free_ntquota_list(&qtl);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ nt_errstr(status),
+ username_str);
+ return -1;
+ }
+ status = cli_get_user_quota(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ nt_errstr(status),
+ username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_LIST:
+ status = cli_list_user_quota(
+ cli, quota_fnum, &qtl);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf(
+ "%s cli_list_user_quota\n",
+ nt_errstr(status));
+ return -1;
+ }
+ dump_ntquota_list(&qtl,verbose,numeric,SidToString);
+ free_ntquota_list(&qtl);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ case SMB_USER_FS_QUOTA_TYPE:
+ switch(cmd) {
+ case QUOTA_GET:
+ status = cli_get_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETLIM:
+ status = cli_get_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ qt.softlim = pqt->softlim;
+ qt.hardlim = pqt->hardlim;
+ status = cli_set_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ status = cli_get_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETFLAGS:
+ status = cli_get_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ qt.qflags = pqt->qflags;
+ status = cli_set_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ status = cli_get_fs_quota_info(
+ cli, quota_fnum, &qt);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ nt_errstr(status));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+ break;
+ default:
+ d_printf("Unknown Error\n");
+ return -1;
+ }
+
+ cli_close(cli, quota_fnum);
+
+ return 0;
+}
+
+/*****************************************************
+ Return a connection to a server.
+*******************************************************/
+
+static struct cli_state *connect_one(const char *share)
+{
+ struct cli_state *c;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+
+ nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server,
+ NULL, 0,
+ share, "?????",
+ samba_cmdline_get_creds(),
+ flags);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ return c;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc, char *argv[])
+{
+ const char **argv_const = discard_const_p(const char *, argv);
+ char *share;
+ int opt;
+ int result;
+ int todo = 0;
+ char *username_str = NULL;
+ char *path = NULL;
+ char *set_str = NULL;
+ enum SMB_QUOTA_TYPE qtype = SMB_INVALID_QUOTA_TYPE;
+ int cmd = 0;
+ static bool test_args = False;
+ struct cli_state *cli;
+ bool fix_user = False;
+ SMB_NTQUOTA_STRUCT qt;
+ TALLOC_CTX *frame = talloc_stackframe();
+ poptContext pc;
+ struct cli_credentials *creds = NULL;
+ bool ok;
+ struct loadparm_context *lp_ctx = NULL;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "quota-user",
+ .shortName = 'u',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'u',
+ .descrip = "Show quotas for user",
+ .argDescrip = "USER",
+ },
+ {
+ .longName = "list",
+ .shortName = 'L',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'L',
+ .descrip = "List user quotas",
+ },
+ {
+ .longName = "fs",
+ .shortName = 'F',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'F',
+ .descrip = "Show filesystem quotas",
+ },
+ {
+ .longName = "set",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'S',
+ .descrip = "Set acls\n"
+ "SETSTRING:\n"
+ "UQLIM:<username>/<softlimit>/<hardlimit> for user quotas\n"
+ "FSQLIM:<softlimit>/<hardlimit> for filesystem defaults\n"
+ "FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT",
+ .argDescrip = "SETSTRING",
+ },
+ {
+ .longName = "numeric",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'n',
+ .descrip = "Don't resolve sids or limits to names",
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'v',
+ .descrip = "be verbose",
+ },
+ {
+ .longName = "test-args",
+ .shortName = 't',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 't',
+ .descrip = "Test arguments"
+ },
+ {
+ .longName = "max-protocol",
+ .shortName = 'm',
+ .argInfo = POPT_ARG_STRING,
+ .arg = NULL,
+ .val = 'm',
+ .descrip = "Set the max protocol level",
+ .argDescrip = "LEVEL"
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S3
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ smb_init_locale();
+
+ ZERO_STRUCT(qt);
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ setlinebuf(stdout);
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options, 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ poptSetOtherOptionHelp(pc, "//server1/share1");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'n':
+ numeric = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 't':
+ test_args = true;
+ break;
+ case 'L':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = LIST_QUOTA;
+ break;
+
+ case 'F':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = FS_QUOTA;
+ break;
+
+ case 'u':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ username_str = talloc_strdup(frame, poptGetOptArg(pc));
+ if (!username_str) {
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = USER_QUOTA;
+ fix_user = True;
+ break;
+
+ case 'S':
+ if (todo != 0) {
+ d_printf("Please specify only one option of <-L|-F|-S|-u>\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ set_str = talloc_strdup(frame, poptGetOptArg(pc));
+ if (!set_str) {
+ exit(EXIT_PARSE_ERROR);
+ }
+ todo = SET_QUOTA;
+ break;
+ case 'm':
+ lpcfg_set_cmdline(lp_ctx,
+ "client max protocol",
+ poptGetOptArg(pc));
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ creds = samba_cmdline_get_creds();
+
+ if (todo == 0)
+ todo = USER_QUOTA;
+
+ if (!fix_user) {
+ const char *user = cli_credentials_get_username(creds);
+ if (user == NULL) {
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ username_str = talloc_strdup(frame, user);
+ if (!username_str) {
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ path = talloc_strdup(frame, poptGetArg(pc));
+ if (!path) {
+ printf("Out of memory\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) {
+ printf("Invalid argument: %s\n", path);
+ return -1;
+ }
+
+ poptFreeContext(pc);
+ samba_cmdline_burn(argc, argv);
+
+ string_replace(path, '/', '\\');
+
+ server = SMB_STRDUP(path+2);
+ if (!server) {
+ printf("Out of memory\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+ share = strchr_m(server,'\\');
+ if (share == NULL) {
+ printf("Invalid argument\n");
+ exit(EXIT_PARSE_ERROR);
+ }
+
+ *share = 0;
+ share++;
+
+ if (todo == SET_QUOTA) {
+ if (parse_quota_set(talloc_tos(), set_str, &username_str, &qtype, &cmd, &qt)) {
+ printf("Invalid argument: -S %s\n", set_str);
+ exit(EXIT_PARSE_ERROR);
+ }
+ }
+
+ if (!test_args) {
+ cli = connect_one(share);
+ if (!cli) {
+ exit(EXIT_FAILED);
+ }
+ } else {
+ exit(EXIT_OK);
+ }
+
+
+ /* Perform requested action */
+
+ switch (todo) {
+ case FS_QUOTA:
+ result = do_quota(cli,SMB_USER_FS_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case LIST_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_LIST, username_str, NULL);
+ break;
+ case USER_QUOTA:
+ result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_GET, username_str, NULL);
+ break;
+ case SET_QUOTA:
+ result = do_quota(cli, qtype, cmd, username_str, &qt);
+ break;
+ default:
+ result = EXIT_FAILED;
+ break;
+ }
+
+ gfree_all();
+ talloc_free(frame);
+
+ return result;
+}
diff --git a/source3/utils/smbfilter.c b/source3/utils/smbfilter.c
new file mode 100644
index 0000000..ebf2809
--- /dev/null
+++ b/source3/utils/smbfilter.c
@@ -0,0 +1,356 @@
+/*
+ Unix SMB/CIFS implementation.
+ SMB filter/socket plugin
+ Copyright (C) Andrew Tridgell 1999
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "system/select.h"
+#include "libsmb/namequery.h"
+#include "../lib/util/select.h"
+#include "libsmb/nmblib.h"
+#include "lib/util/sys_rw_data.h"
+
+#define SECURITY_MASK 0
+#define SECURITY_SET 0
+
+/* this forces non-unicode */
+#define CAPABILITY_MASK 0
+#define CAPABILITY_SET 0
+
+/* and non-unicode for the client too */
+#define CLI_CAPABILITY_MASK 0
+#define CLI_CAPABILITY_SET 0
+
+static char *netbiosname;
+
+static void save_file(const char *fname, void *ppacket, size_t length)
+{
+ int fd;
+ fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ if (fd == -1) {
+ perror(fname);
+ return;
+ }
+ if (write(fd, ppacket, length) != length) {
+ fprintf(stderr,"Failed to write %s\n", fname);
+ close(fd);
+ return;
+ }
+ close(fd);
+ printf("Wrote %ld bytes to %s\n", (unsigned long)length, fname);
+}
+
+static void filter_reply(char *buf)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ unsigned x;
+
+ if (msg_type) return;
+
+ switch (type) {
+
+ case SMBnegprot:
+ /* force the security bits */
+ x = CVAL(buf, smb_vwv1);
+ x = (x | SECURITY_SET) & ~SECURITY_MASK;
+ SCVAL(buf, smb_vwv1, x);
+
+ /* force the capabilities */
+ x = IVAL(buf,smb_vwv9+1);
+ x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv9+1, x);
+ break;
+
+ }
+}
+
+static void filter_request(char *buf, size_t buf_len)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ unsigned x;
+ fstring name1,name2;
+ int name_len1 = 0;
+ int name_len2;
+ int name_type1, name_type2;
+ int ret;
+
+ if (msg_type) {
+ /* it's a netbios special */
+ switch (msg_type)
+ case 0x81:
+ /* session request */
+ /* inbuf_size is guaranteed to be at least 4. */
+ name_len1 = name_len((unsigned char *)(buf+4),
+ buf_len - 4);
+ if (name_len1 <= 0 || name_len1 > buf_len - 4) {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return;
+ }
+ name_len2 = name_len((unsigned char *)(buf+4+name_len1),
+ buf_len - 4 - name_len1);
+ if (name_len2 <= 0 || name_len2 > buf_len - 4 - name_len1) {
+ DEBUG(0,("Invalid name length in session request\n"));
+ return;
+ }
+
+ name_type1 = name_extract((unsigned char *)buf,
+ buf_len,(unsigned int)4,name1);
+ name_type2 = name_extract((unsigned char *)buf,
+ buf_len,(unsigned int)(4 + name_len1),name2);
+
+ if (name_type1 == -1 || name_type2 == -1) {
+ DEBUG(0,("Invalid name type in session request\n"));
+ return;
+ }
+
+ d_printf("session_request: %s -> %s\n",
+ name1, name2);
+ if (netbiosname) {
+ char *mangled = name_mangle(
+ talloc_tos(), netbiosname, 0x20);
+ if (mangled != NULL) {
+ /* replace the destination netbios
+ * name */
+ memcpy(buf+4, mangled,
+ name_len((unsigned char *)mangled,
+ talloc_get_size(mangled)));
+ TALLOC_FREE(mangled);
+ }
+ }
+ return;
+ }
+
+ /* it's an ordinary SMB request */
+ switch (type) {
+ case SMBsesssetupX:
+ /* force the client capabilities */
+ x = IVAL(buf,smb_vwv11);
+ d_printf("SMBsesssetupX cap=0x%08x\n", x);
+ d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8));
+ ret = system("mv sessionsetup.dat sessionsetup1.dat");
+ if (ret == -1) {
+ DBG_ERR("failed to call mv command\n");
+ }
+ save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7));
+ x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK;
+ SIVAL(buf, smb_vwv11, x);
+ break;
+ }
+}
+
+/****************************************************************************
+ Send an smb to a fd.
+****************************************************************************/
+
+static bool send_smb(int fd, char *buffer)
+{
+ size_t len;
+ size_t nwritten=0;
+ ssize_t ret;
+
+ len = smb_len(buffer) + 4;
+
+ while (nwritten < len) {
+ ret = write_data(fd,buffer+nwritten,len - nwritten);
+ if (ret <= 0) {
+ DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n",
+ (int)len,(int)ret, strerror(errno) ));
+ return false;
+ }
+ nwritten += ret;
+ }
+
+ return true;
+}
+
+static void filter_child(int c, struct sockaddr_storage *dest_ss)
+{
+ NTSTATUS status;
+ int s = -1;
+ char packet[128*1024];
+
+ /* we have a connection from a new client, now connect to the server */
+ status = open_socket_out(dest_ss, TCP_SMB_PORT, LONG_CONNECT_TIMEOUT, &s);
+ if (!NT_STATUS_IS_OK(status)) {
+ char addr[INET6_ADDRSTRLEN];
+ if (dest_ss) {
+ print_sockaddr(addr, sizeof(addr), dest_ss);
+ }
+
+ d_printf("Unable to connect to %s (%s)\n",
+ dest_ss?addr:"NULL", nt_errstr(status));
+ exit(1);
+ }
+
+ while (c != -1 || s != -1) {
+ struct pollfd fds[2];
+ int num_fds, ret;
+
+ memset(fds, 0, sizeof(struct pollfd) * 2);
+ fds[0].fd = -1;
+ fds[1].fd = -1;
+ num_fds = 0;
+
+ if (s != -1) {
+ fds[num_fds].fd = s;
+ fds[num_fds].events = POLLIN|POLLHUP;
+ num_fds += 1;
+ }
+ if (c != -1) {
+ fds[num_fds].fd = c;
+ fds[num_fds].events = POLLIN|POLLHUP;
+ num_fds += 1;
+ }
+
+ ret = sys_poll_intr(fds, num_fds, -1);
+ if (ret <= 0) {
+ continue;
+ }
+
+ /*
+ * find c in fds and see if it's readable
+ */
+ if ((c != -1) &&
+ (((fds[0].fd == c)
+ && (fds[0].revents & (POLLIN|POLLHUP|POLLERR))) ||
+ ((fds[1].fd == c)
+ && (fds[1].revents & (POLLIN|POLLHUP|POLLERR))))) {
+ size_t len;
+ if (!NT_STATUS_IS_OK(receive_smb_raw(
+ c, packet, sizeof(packet),
+ 0, 0, &len))) {
+ d_printf("client closed connection\n");
+ exit(0);
+ }
+ filter_request(packet, len);
+ if (!send_smb(s, packet)) {
+ d_printf("server is dead\n");
+ exit(1);
+ }
+ }
+
+ /*
+ * find s in fds and see if it's readable
+ */
+ if ((s != -1) &&
+ (((fds[0].fd == s)
+ && (fds[0].revents & (POLLIN|POLLHUP|POLLERR))) ||
+ ((fds[1].fd == s)
+ && (fds[1].revents & (POLLIN|POLLHUP|POLLERR))))) {
+ size_t len;
+ if (!NT_STATUS_IS_OK(receive_smb_raw(
+ s, packet, sizeof(packet),
+ 0, 0, &len))) {
+ d_printf("server closed connection\n");
+ exit(0);
+ }
+ filter_reply(packet);
+ if (!send_smb(c, packet)) {
+ d_printf("client is dead\n");
+ exit(1);
+ }
+ }
+ }
+ d_printf("Connection closed\n");
+ exit(0);
+}
+
+
+static void start_filter(char *desthost)
+{
+ int s, c;
+ struct sockaddr_storage dest_ss;
+ struct sockaddr_storage my_ss;
+
+ CatchChild();
+
+ /* start listening on port 445 locally */
+
+ zero_sockaddr(&my_ss);
+ s = open_socket_in(SOCK_STREAM, &my_ss, TCP_SMB_PORT, true);
+
+ if (s < 0) {
+ d_printf("bind failed: %s\n", strerror(-s));
+ exit(1);
+ }
+
+ if (listen(s, 5) == -1) {
+ d_printf("listen failed\n");
+ }
+
+ if (!resolve_name(desthost, &dest_ss, 0x20, false)) {
+ d_printf("Unable to resolve host %s\n", desthost);
+ exit(1);
+ }
+
+ while (1) {
+ int num, revents;
+ struct sockaddr_storage ss;
+ socklen_t in_addrlen = sizeof(ss);
+
+ num = poll_intr_one_fd(s, POLLIN|POLLHUP, -1, &revents);
+ if ((num > 0) && (revents & (POLLIN|POLLHUP|POLLERR))) {
+ c = accept(s, (struct sockaddr *)&ss, &in_addrlen);
+ if (c != -1) {
+ smb_set_close_on_exec(c);
+ if (fork() == 0) {
+ close(s);
+ filter_child(c, &dest_ss);
+ exit(0);
+ } else {
+ close(c);
+ }
+ }
+ }
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ char *desthost;
+ const char *configfile;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ smb_init_locale();
+
+ setup_logging(argv[0], DEBUG_STDOUT);
+
+ configfile = get_dyn_CONFIGFILE();
+
+ if (argc < 2) {
+ fprintf(stderr,"smbfilter <desthost> <netbiosname>\n");
+ exit(1);
+ }
+
+ desthost = argv[1];
+ if (argc > 2) {
+ netbiosname = argv[2];
+ }
+
+ if (!lp_load_global(configfile)) {
+ d_printf("Unable to load config file\n");
+ }
+
+ start_filter(desthost);
+ gfree_all();
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/smbget.c b/source3/utils/smbget.c
new file mode 100644
index 0000000..67ea259
--- /dev/null
+++ b/source3/utils/smbget.c
@@ -0,0 +1,1073 @@
+/*
+ smbget: a wget-like utility with support for recursive downloading of
+ smb:// urls
+ Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/param/param.h"
+#include "libsmbclient.h"
+#include "cmdline_contexts.h"
+#include "auth/credentials/credentials.h"
+#include "auth/gensec/gensec.h"
+
+static int columns = 0;
+
+static time_t total_start_time = 0;
+static off_t total_bytes = 0;
+
+#define SMB_MAXPATHLEN MAXPATHLEN
+
+/*
+ * Number of bytes to read when checking whether local and remote file
+ * are really the same file
+ */
+#define RESUME_CHECK_SIZE 512
+#define RESUME_DOWNLOAD_OFFSET 1024
+#define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE)
+/* Number of bytes to read at once */
+#define SMB_DEFAULT_BLOCKSIZE 64000
+
+struct opt {
+ char *outputfile;
+ size_t blocksize;
+
+ int quiet;
+ int dots;
+ int verbose;
+ int send_stdout;
+ int update;
+ unsigned limit_rate;
+};
+static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE };
+
+static bool smb_download_file(const char *base, const char *name,
+ bool recursive, bool resume, bool toplevel,
+ char *outfile);
+
+static int get_num_cols(void)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) {
+ return 0;
+ }
+ return ws.ws_col;
+#else
+#warning No support for TIOCGWINSZ
+ char *cols = getenv("COLUMNS");
+ if (!cols) {
+ return 0;
+ }
+ return atoi(cols);
+#endif
+}
+
+static void change_columns(int sig)
+{
+ columns = get_num_cols();
+}
+
+static void human_readable(off_t s, char *buffer, int l)
+{
+ if (s > 1024 * 1024 * 1024) {
+ snprintf(buffer, l, "%.2fGB", 1.0 * s / (1024 * 1024 * 1024));
+ } else if (s > 1024 * 1024) {
+ snprintf(buffer, l, "%.2fMB", 1.0 * s / (1024 * 1024));
+ } else if (s > 1024) {
+ snprintf(buffer, l, "%.2fkB", 1.0 * s / 1024);
+ } else {
+ snprintf(buffer, l, "%jdb", (intmax_t)s);
+ }
+}
+
+/*
+ * Authentication callback for libsmbclient.
+ *
+ * The command line parser will take care asking for a password interactively!
+ */
+static void get_auth_data_with_context_fn(SMBCCTX *ctx,
+ const char *srv,
+ const char *shr,
+ char *dom,
+ int dom_len,
+ char *usr,
+ int usr_len,
+ char *pwd,
+ int pwd_len)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ const char *username = NULL;
+ const char *password = NULL;
+ const char *domain = NULL;
+ enum credentials_obtained obtained = CRED_UNINITIALISED;
+
+ domain = cli_credentials_get_domain_and_obtained(creds, &obtained);
+ if (domain != NULL) {
+ bool overwrite = false;
+ if (dom[0] == '\0') {
+ overwrite = true;
+ }
+ if (obtained >= CRED_CALLBACK_RESULT) {
+ overwrite = true;
+ }
+ if (overwrite) {
+ strncpy(dom, domain, dom_len - 1);
+ }
+ }
+ cli_credentials_set_domain(creds, dom, obtained);
+
+ username = cli_credentials_get_username_and_obtained(creds, &obtained);
+ if (username != NULL) {
+ bool overwrite = false;
+ if (usr[0] == '\0') {
+ overwrite = true;
+ }
+ if (obtained >= CRED_CALLBACK_RESULT) {
+ overwrite = true;
+ }
+ if (overwrite) {
+ strncpy(usr, username, usr_len - 1);
+ }
+ }
+ cli_credentials_set_username(creds, usr, obtained);
+
+ password = cli_credentials_get_password_and_obtained(creds, &obtained);
+ if (password != NULL) {
+ bool overwrite = false;
+ if (pwd[0] == '\0') {
+ overwrite = true;
+ }
+ if (obtained >= CRED_CALLBACK_RESULT) {
+ overwrite = true;
+ }
+ if (overwrite) {
+ strncpy(pwd, password, pwd_len - 1);
+ }
+ }
+ cli_credentials_set_password(creds, pwd, obtained);
+
+ smbc_set_credentials_with_fallback(ctx, dom, usr, pwd);
+
+ if (!opt.quiet) {
+ if (usr[0] == '\0') {
+ printf("Using guest user\n");
+ } else if (dom[0] == '\0') {
+ printf("Using user: %s\n", usr);
+ } else {
+ printf("Using domain: %s, user: %s\n", dom, usr);
+ }
+ }
+}
+
+static bool smb_download_dir(const char *base, const char *name, int resume)
+{
+ char path[SMB_MAXPATHLEN];
+ int dirhandle;
+ struct smbc_dirent *dirent;
+ const char *relname = name;
+ char *tmpname;
+ bool ok = false;
+
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
+ (base[0] && name[0] && name[0] != '/' &&
+ base[strlen(base)-1] != '/') ? "/" : "",
+ name);
+
+ /* List files in directory and call smb_download_file on them */
+ dirhandle = smbc_opendir(path);
+ if (dirhandle < 1) {
+ if (errno == ENOTDIR) {
+ return smb_download_file(base, name, true, resume,
+ false, NULL);
+ }
+ fprintf(stderr, "Can't open directory %s: %s\n", path,
+ strerror(errno));
+ return false;
+ }
+
+ while (*relname == '/') {
+ relname++;
+ }
+
+ if (strlen(relname) > 0) {
+ int rc = mkdir(relname, 0755);
+ if (rc == -1 && errno != EEXIST) {
+ fprintf(stderr, "Can't create directory %s: %s\n",
+ relname, strerror(errno));
+ return false;
+ }
+ }
+
+ tmpname = SMB_STRDUP(name);
+
+ while ((dirent = smbc_readdir(dirhandle))) {
+ char *newname;
+ if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..")) {
+ ok = true;
+ continue;
+ }
+ if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) {
+ free(tmpname);
+ return false;
+ }
+ switch (dirent->smbc_type) {
+ case SMBC_DIR:
+ ok = smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_WORKGROUP:
+ ok = smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_SERVER:
+ ok = smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_FILE:
+ ok = smb_download_file(base, newname, true, resume,
+ false, NULL);
+ break;
+
+ case SMBC_FILE_SHARE:
+ ok = smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_PRINTER_SHARE:
+ if (!opt.quiet) {
+ printf("Ignoring printer share %s\n",
+ dirent->name);
+ }
+ break;
+
+ case SMBC_COMMS_SHARE:
+ if (!opt.quiet) {
+ printf("Ignoring comms share %s\n",
+ dirent->name);
+ }
+ break;
+
+ case SMBC_IPC_SHARE:
+ if (!opt.quiet) {
+ printf("Ignoring ipc$ share %s\n",
+ dirent->name);
+ }
+ break;
+
+ default:
+ fprintf(stderr, "Ignoring file '%s' of type '%d'\n",
+ newname, dirent->smbc_type);
+ break;
+ }
+
+ if (!ok) {
+ fprintf(stderr, "Failed to download %s: %s\n",
+ newname, strerror(errno));
+ free(newname);
+ free(tmpname);
+ return false;
+ }
+ free(newname);
+ }
+ free(tmpname);
+
+ smbc_closedir(dirhandle);
+ return ok;
+}
+
+static char *print_time(long t)
+{
+ static char buffer[100];
+ int secs, mins, hours;
+ if (t < -1) {
+ strncpy(buffer, "Unknown", sizeof(buffer));
+ return buffer;
+ }
+
+ secs = (int)t % 60;
+ mins = (int)t / 60 % 60;
+ hours = (int)t / (60 * 60);
+ snprintf(buffer, sizeof(buffer) - 1, "%02d:%02d:%02d", hours, mins,
+ secs);
+ return buffer;
+}
+
+static void print_progress(const char *name, time_t start, time_t now,
+ off_t start_pos, off_t pos, off_t total)
+{
+ double avg = 0.0;
+ long eta = -1;
+ double prcnt = 0.0;
+ char hpos[22], htotal[22], havg[22];
+ char *status, *filename;
+ int len;
+ if (now - start) {
+ avg = 1.0 * (pos - start_pos) / (now - start);
+ }
+ eta = (total - pos) / avg;
+ if (total) {
+ prcnt = 100.0 * pos / total;
+ }
+
+ human_readable(pos, hpos, sizeof(hpos));
+ human_readable(total, htotal, sizeof(htotal));
+ human_readable(avg, havg, sizeof(havg));
+
+ len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos,
+ htotal, prcnt, havg, print_time(eta));
+ if (len == -1) {
+ return;
+ }
+
+ if (columns) {
+ int required = strlen(name),
+ available = columns - len - strlen("[] ");
+ if (required > available) {
+ if (asprintf(&filename, "...%s",
+ name + required - available + 3) == -1) {
+ return;
+ }
+ } else {
+ filename = SMB_STRNDUP(name, available);
+ }
+ } else {
+ filename = SMB_STRDUP(name);
+ }
+
+ fprintf(stderr, "\r[%s] %s", filename, status);
+
+ free(filename);
+ free(status);
+}
+
+/* Return false on error, true on success. */
+
+static bool smb_download_file(const char *base, const char *name,
+ bool recursive, bool resume, bool toplevel,
+ char *outfile)
+{
+ int remotehandle, localhandle;
+ time_t start_time = time_mono(NULL);
+ const char *newpath;
+ char path[SMB_MAXPATHLEN];
+ char checkbuf[2][RESUME_CHECK_SIZE];
+ char *readbuf = NULL;
+ off_t offset_download = 0, offset_check = 0, curpos = 0,
+ start_offset = 0;
+ struct stat localstat, remotestat;
+ clock_t start_of_bucket_ticks = 0;
+ size_t bytes_in_bucket = 0;
+ size_t bucket_size = 0;
+ clock_t ticks_to_fill_bucket = 0;
+
+ snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base,
+ (*base && *name && name[0] != '/' &&
+ base[strlen(base)-1] != '/') ? "/" : "",
+ name);
+
+ remotehandle = smbc_open(path, O_RDONLY, 0755);
+
+ if (remotehandle < 0) {
+ switch (errno) {
+ case EISDIR:
+ if (!recursive) {
+ fprintf(stderr,
+ "%s is a directory. Specify -R "
+ "to download recursively\n",
+ path);
+ return false;
+ }
+ return smb_download_dir(base, name, resume);
+
+ case ENOENT:
+ fprintf(stderr,
+ "%s can't be found on the remote server\n",
+ path);
+ return false;
+
+ case ENOMEM:
+ fprintf(stderr, "Not enough memory\n");
+ return false;
+
+ case ENODEV:
+ fprintf(stderr,
+ "The share name used in %s does not exist\n",
+ path);
+ return false;
+
+ case EACCES:
+ fprintf(stderr, "You don't have enough permissions "
+ "to access %s\n",
+ path);
+ return false;
+
+ default:
+ perror("smbc_open");
+ return false;
+ }
+ }
+
+ if (smbc_fstat(remotehandle, &remotestat) < 0) {
+ fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
+ return false;
+ }
+
+ if (outfile) {
+ newpath = outfile;
+ } else if (!name[0]) {
+ newpath = strrchr(base, '/');
+ if (newpath) {
+ newpath++;
+ } else {
+ newpath = base;
+ }
+ } else {
+ newpath = name;
+ }
+
+ if (!toplevel && (newpath[0] == '/')) {
+ newpath++;
+ }
+
+ /* Open local file according to the mode */
+ if (opt.update) {
+ /* if it is up-to-date, skip */
+ if (stat(newpath, &localstat) == 0 &&
+ localstat.st_mtime >= remotestat.st_mtime) {
+ if (opt.verbose) {
+ printf("%s is up-to-date, skipping\n", newpath);
+ }
+ smbc_close(remotehandle);
+ return true;
+ }
+ /* else open it for writing and truncate if it exists */
+ localhandle = open(
+ newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775);
+ if (localhandle < 0) {
+ fprintf(stderr, "Can't open %s : %s\n", newpath,
+ strerror(errno));
+ smbc_close(remotehandle);
+ return false;
+ }
+ /* no offset */
+ } else if (!opt.send_stdout) {
+ localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR |
+ (!resume ? O_EXCL : 0),
+ 0755);
+ if (localhandle < 0) {
+ fprintf(stderr, "Can't open %s: %s\n", newpath,
+ strerror(errno));
+ smbc_close(remotehandle);
+ return false;
+ }
+
+ if (fstat(localhandle, &localstat) != 0) {
+ fprintf(stderr, "Can't fstat %s: %s\n", newpath,
+ strerror(errno));
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ start_offset = localstat.st_size;
+
+ if (localstat.st_size &&
+ localstat.st_size == remotestat.st_size) {
+ if (opt.verbose) {
+ fprintf(stderr, "%s is already downloaded "
+ "completely.\n",
+ path);
+ } else if (!opt.quiet) {
+ fprintf(stderr, "%s\n", path);
+ }
+ smbc_close(remotehandle);
+ close(localhandle);
+ return true;
+ }
+
+ if (localstat.st_size > RESUME_CHECK_OFFSET &&
+ remotestat.st_size > RESUME_CHECK_OFFSET) {
+ offset_download =
+ localstat.st_size - RESUME_DOWNLOAD_OFFSET;
+ offset_check = localstat.st_size - RESUME_CHECK_OFFSET;
+ if (opt.verbose) {
+ printf("Trying to start resume of %s at %jd\n"
+ "At the moment %jd of %jd bytes have "
+ "been retrieved\n",
+ newpath, (intmax_t)offset_check,
+ (intmax_t)localstat.st_size,
+ (intmax_t)remotestat.st_size);
+ }
+ }
+
+ if (offset_check) {
+ off_t off1, off2;
+ /* First, check all bytes from offset_check to
+ * offset_download */
+ off1 = lseek(localhandle, offset_check, SEEK_SET);
+ if (off1 < 0) {
+ fprintf(stderr,
+ "Can't seek to %jd in local file %s\n",
+ (intmax_t)offset_check, newpath);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
+ if (off2 < 0) {
+ fprintf(stderr,
+ "Can't seek to %jd in remote file %s\n",
+ (intmax_t)offset_check, newpath);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ if (off1 != off2) {
+ fprintf(stderr, "Offset in local and remote "
+ "files are different "
+ "(local: %jd, remote: %jd)\n",
+ (intmax_t)off1, (intmax_t)off2);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ if (smbc_read(remotehandle, checkbuf[0],
+ RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from "
+ "remote file %s\n",
+ RESUME_CHECK_SIZE, path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ if (read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) !=
+ RESUME_CHECK_SIZE) {
+ fprintf(stderr, "Can't read %d bytes from "
+ "local file %s\n",
+ RESUME_CHECK_SIZE, name);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+
+ if (memcmp(checkbuf[0], checkbuf[1],
+ RESUME_CHECK_SIZE) == 0) {
+ if (opt.verbose) {
+ printf("Current local and remote file "
+ "appear to be the same. "
+ "Starting download from "
+ "offset %jd\n",
+ (intmax_t)offset_download);
+ }
+ } else {
+ fprintf(stderr, "Local and remote file appear "
+ "to be different, not "
+ "doing resume for %s\n",
+ path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return false;
+ }
+ }
+ } else {
+ localhandle = STDOUT_FILENO;
+ start_offset = 0;
+ offset_download = 0;
+ offset_check = 0;
+ }
+
+ /* We implement rate limiting by filling up a bucket with bytes and
+ * checking, once the bucket is filled, if it was filled too fast.
+ * If so, we sleep for some time to get an average transfer rate that
+ * equals to the one set by the user.
+ *
+ * The bucket size directly affects the traffic characteristics.
+ * The smaller the bucket the more frequent the pause/resume cycle.
+ * A large bucket can result in burst of high speed traffic and large
+ * pauses. A cycle of 100ms looks like a good value. This value (in
+ * ticks) is held in `ticks_to_fill_bucket`. The `bucket_size` is
+ * calculated as:
+ * `limit_rate * 1024 * / (CLOCKS_PER_SEC / ticks_to_fill_bucket)`
+ *
+ * After selecting the bucket size we also need to check the blocksize
+ * of the transfer, since this is the minimum unit of traffic that we
+ * can observe. Achieving a ~10% precision requires a blocksize with a
+ * maximum size of `bucket_size / 10`.
+ */
+ if (opt.limit_rate > 0) {
+ unsigned max_block_size;
+ /* This is the time that the bucket should take to fill. */
+ ticks_to_fill_bucket = 100 /*ms*/ * CLOCKS_PER_SEC / 1000;
+ /* This is the size of the bucket in bytes.
+ * If we fill the bucket too quickly we should pause */
+ bucket_size = opt.limit_rate * 1024 / (CLOCKS_PER_SEC / ticks_to_fill_bucket);
+ max_block_size = bucket_size / 10;
+ max_block_size = max_block_size > 0 ? max_block_size : 1;
+ if (opt.blocksize > max_block_size) {
+ if (opt.blocksize != SMB_DEFAULT_BLOCKSIZE) {
+ fprintf(stderr,
+ "Warning: Overriding block size to %d "
+ "due to limit-rate", max_block_size);
+ }
+ opt.blocksize = max_block_size;
+ }
+ start_of_bucket_ticks = clock();
+ }
+
+ readbuf = (char *)SMB_MALLOC(opt.blocksize);
+ if (!readbuf) {
+ fprintf(stderr, "Failed to allocate %zu bytes for read "
+ "buffer (%s)", opt.blocksize, strerror(errno));
+ if (localhandle != STDOUT_FILENO) {
+ close(localhandle);
+ }
+ return false;
+ }
+
+ /* Now, download all bytes from offset_download to the end */
+ for (curpos = offset_download; curpos < remotestat.st_size;
+ curpos += opt.blocksize) {
+ ssize_t bytesread;
+ ssize_t byteswritten;
+
+ /* Rate limiting. This pauses the transfer to limit traffic. */
+ if (opt.limit_rate > 0) {
+ if (bytes_in_bucket > bucket_size) {
+ clock_t now_ticks = clock();
+ clock_t diff_ticks = now_ticks
+ - start_of_bucket_ticks;
+ /* Check if the bucket filled up too fast. */
+ if (diff_ticks < ticks_to_fill_bucket) {
+ /* Pause until `ticks_to_fill_bucket` */
+ double sleep_us
+ = (ticks_to_fill_bucket - diff_ticks)
+ * 1000000.0 / CLOCKS_PER_SEC;
+ usleep(sleep_us);
+ }
+ /* Reset the byte counter and the ticks. */
+ bytes_in_bucket = 0;
+ start_of_bucket_ticks = clock();
+ }
+ }
+
+ bytesread = smbc_read(remotehandle, readbuf, opt.blocksize);
+ if (opt.limit_rate > 0) {
+ bytes_in_bucket += bytesread;
+ }
+ if(bytesread < 0) {
+ fprintf(stderr,
+ "Can't read %zu bytes at offset %jd, file %s\n",
+ opt.blocksize, (intmax_t)curpos, path);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) {
+ close(localhandle);
+ }
+ free(readbuf);
+ return false;
+ }
+
+ total_bytes += bytesread;
+
+ byteswritten = write(localhandle, readbuf, bytesread);
+ if (byteswritten != bytesread) {
+ fprintf(stderr,
+ "Can't write %zd bytes to local file %s at "
+ "offset %jd\n", bytesread, path,
+ (intmax_t)curpos);
+ free(readbuf);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) {
+ close(localhandle);
+ }
+ return false;
+ }
+
+ if (opt.dots) {
+ fputc('.', stderr);
+ } else if (!opt.quiet) {
+ print_progress(newpath, start_time, time_mono(NULL),
+ start_offset, curpos,
+ remotestat.st_size);
+ }
+ }
+
+ free(readbuf);
+
+ if (opt.dots) {
+ fputc('\n', stderr);
+ printf("%s downloaded\n", path);
+ } else if (!opt.quiet) {
+ int i;
+ fprintf(stderr, "\r%s", path);
+ if (columns) {
+ for (i = strlen(path); i < columns; i++) {
+ fputc(' ', stderr);
+ }
+ }
+ fputc('\n', stderr);
+ }
+
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) {
+ close(localhandle);
+ }
+ return true;
+}
+
+static void clean_exit(void)
+{
+ char bs[100];
+ human_readable(total_bytes, bs, sizeof(bs));
+ if (!opt.quiet) {
+ fprintf(stderr, "Downloaded %s in %lu seconds\n", bs,
+ (unsigned long)(time_mono(NULL) - total_start_time));
+ }
+ exit(0);
+}
+
+static void signal_quit(int v)
+{
+ clean_exit();
+}
+
+int main(int argc, char **argv)
+{
+ int c = 0;
+ const char *file = NULL;
+ int smb_encrypt = false;
+ int resume = 0, recursive = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ bool ok = false;
+ const char **argv_const = discard_const_p(const char *, argv);
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+
+ {
+ .longName = "guest",
+ .shortName = 'a',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'a',
+ .descrip = "Work as user guest"
+ },
+ {
+ .longName = "encrypt",
+ .shortName = 'e',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &smb_encrypt,
+ .val = 1,
+ .descrip = "Encrypt SMB transport"
+ },
+ {
+ .longName = "resume",
+ .shortName = 'r',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &resume,
+ .val = 1,
+ .descrip = "Automatically resume aborted files"
+ },
+ {
+ .longName = "update",
+ .shortName = 'u',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt.update,
+ .val = 1,
+ .descrip = "Download only when remote file is "
+ "newer than local file or local file "
+ "is missing"
+ },
+ {
+ .longName = "recursive",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &recursive,
+ .val = true,
+ .descrip = "Recursively download files"
+ },
+ {
+ .longName = "blocksize",
+ .shortName = 'b',
+ .argInfo = POPT_ARG_INT,
+ .arg = &opt.blocksize,
+ .val = 'b',
+ .descrip = "Change number of bytes in a block"
+ },
+
+ {
+ .longName = "outputfile",
+ .shortName = 'o',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &opt.outputfile,
+ .val = 'o',
+ .descrip = "Write downloaded data to specified file"
+ },
+ {
+ .longName = "stdout",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt.send_stdout,
+ .val = true,
+ .descrip = "Write data to stdout"
+ },
+ {
+ .longName = "dots",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt.dots,
+ .val = 1,
+ .descrip = "Show dots as progress indication"
+ },
+ {
+ .longName = "quiet",
+ .shortName = 'q',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt.quiet,
+ .val = 1,
+ .descrip = "Be quiet"
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &opt.verbose,
+ .val = 1,
+ .descrip = "Be verbose"
+ },
+ {
+ .longName = "limit-rate",
+ .shortName = 0,
+ .argInfo = POPT_ARG_INT,
+ .arg = &opt.limit_rate,
+ .val = 'l',
+ .descrip = "Limit download speed to this many KB/s"
+ },
+
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_LEGACY_S3
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ poptContext pc = NULL;
+ struct cli_credentials *creds = NULL;
+ enum smb_encryption_setting encryption_state = SMB_ENCRYPTION_DEFAULT;
+ enum credentials_use_kerberos use_kerberos = CRED_USE_KERBEROS_DESIRED;
+ smbc_smb_encrypt_level encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
+#if 0
+ enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT;
+ const char *use_signing = "auto";
+#endif
+ bool is_nt_hash = false;
+ uint32_t gensec_features;
+ bool use_wbccache = false;
+ SMBCCTX *smb_ctx = NULL;
+ int dbg_lvl = -1;
+ int rc;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false);
+ if (!ok) {
+ goto done;
+ }
+
+#ifdef SIGWINCH
+ signal(SIGWINCH, change_columns);
+#endif
+ signal(SIGINT, signal_quit);
+ signal(SIGTERM, signal_quit);
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ ok = false;
+ goto done;
+ }
+
+ creds = samba_cmdline_get_creds();
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 'a':
+ cli_credentials_set_anonymous(creds);
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(c));
+ poptPrintUsage(pc, stderr, 0);
+ ok = false;
+ goto done;
+ }
+
+ if (c < -1) {
+ fprintf(stderr, "%s: %s\n",
+ poptBadOption(pc, POPT_BADOPTION_NOALIAS),
+ poptStrerror(c));
+ ok = false;
+ goto done;
+ }
+ }
+
+ if ((opt.send_stdout || resume || opt.outputfile) && opt.update) {
+ fprintf(stderr, "The -o, -R or -O and -U options can not be "
+ "used together.\n");
+ ok = true;
+ goto done;
+ }
+ if ((opt.send_stdout || opt.outputfile) && recursive) {
+ fprintf(stderr, "The -o or -O and -R options can not be "
+ "used together.\n");
+ ok = true;
+ goto done;
+ }
+
+ if (opt.outputfile && opt.send_stdout) {
+ fprintf(stderr, "The -o and -O options can not be "
+ "used together.\n");
+ ok = true;
+ goto done;
+ }
+
+ samba_cmdline_burn(argc, argv);
+
+ /* smbc_new_context() will set the log level to 0 */
+ dbg_lvl = debuglevel_get();
+
+ smb_ctx = smbc_new_context();
+ if (smb_ctx == NULL) {
+ fprintf(stderr, "Unable to initialize libsmbclient\n");
+ ok = false;
+ goto done;
+ }
+ smbc_setDebug(smb_ctx, dbg_lvl);
+
+ rc = smbc_setConfiguration(smb_ctx, lp_default_path());
+ if (rc < 0) {
+ ok = false;
+ goto done;
+ }
+
+ smbc_setFunctionAuthDataWithContext(smb_ctx,
+ get_auth_data_with_context_fn);
+
+ ok = smbc_init_context(smb_ctx);
+ if (!ok) {
+ goto done;
+ }
+ smbc_set_context(smb_ctx);
+
+ encryption_state = cli_credentials_get_smb_encryption(creds);
+ switch (encryption_state) {
+ case SMB_ENCRYPTION_REQUIRED:
+ encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
+ break;
+ case SMB_ENCRYPTION_DESIRED:
+ case SMB_ENCRYPTION_IF_REQUIRED:
+ encrypt_level = SMBC_ENCRYPTLEVEL_REQUEST;
+ break;
+ case SMB_ENCRYPTION_OFF:
+ encrypt_level = SMBC_ENCRYPTLEVEL_NONE;
+ break;
+ case SMB_ENCRYPTION_DEFAULT:
+ encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT;
+ break;
+ }
+ if (smb_encrypt) {
+ encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE;
+ }
+ smbc_setOptionSmbEncryptionLevel(smb_ctx, encrypt_level);
+
+#if 0
+ signing_state = cli_credentials_get_smb_signing(creds);
+ if (encryption_state >= SMB_ENCRYPTION_DESIRED) {
+ signing_state = SMB_SIGNING_REQUIRED;
+ }
+ switch (signing_state) {
+ case SMB_SIGNING_REQUIRED:
+ use_signing = "required";
+ break;
+ case SMB_SIGNING_DEFAULT:
+ case SMB_SIGNING_DESIRED:
+ case SMB_SIGNING_IF_REQUIRED:
+ use_signing = "yes";
+ break;
+ case SMB_SIGNING_OFF:
+ use_signing = "off";
+ break;
+ default:
+ use_signing = "auto";
+ break;
+ }
+ /* FIXME: There is no libsmbclient function to set signing state */
+#endif
+
+ use_kerberos = cli_credentials_get_kerberos_state(creds);
+ switch (use_kerberos) {
+ case CRED_USE_KERBEROS_REQUIRED:
+ smbc_setOptionUseKerberos(smb_ctx, true);
+ smbc_setOptionFallbackAfterKerberos(smb_ctx, false);
+ break;
+ case CRED_USE_KERBEROS_DESIRED:
+ smbc_setOptionUseKerberos(smb_ctx, true);
+ smbc_setOptionFallbackAfterKerberos(smb_ctx, true);
+ break;
+ case CRED_USE_KERBEROS_DISABLED:
+ smbc_setOptionUseKerberos(smb_ctx, false);
+ break;
+ }
+
+ /* Check if the password supplied is an NT hash */
+ is_nt_hash = cli_credentials_is_password_nt_hash(creds);
+ smbc_setOptionUseNTHash(smb_ctx, is_nt_hash);
+
+ /* Check if we should use the winbind ccache */
+ gensec_features = cli_credentials_get_gensec_features(creds);
+ use_wbccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE);
+ smbc_setOptionUseCCache(smb_ctx, use_wbccache);
+
+ columns = get_num_cols();
+
+ total_start_time = time_mono(NULL);
+
+ while ((file = poptGetArg(pc))) {
+ if (!recursive) {
+ ok = smb_download_file(file, "", recursive, resume,
+ true, opt.outputfile);
+ } else {
+ ok = smb_download_dir(file, "", resume);
+ }
+ }
+
+done:
+ gfree_all();
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ if (ok) {
+ clean_exit();
+ }
+ return ok ? 0 : 1;
+}
diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c
new file mode 100644
index 0000000..f2c5dfd
--- /dev/null
+++ b/source3/utils/smbpasswd.c
@@ -0,0 +1,678 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Copyright (C) Jeremy Allison 1995-1998
+ * Copyright (C) Tim Potter 2001
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "includes.h"
+#include "system/passwd.h"
+#include "secrets.h"
+#include "../librpc/gen_ndr/samr.h"
+#include "../lib/util/util_pw.h"
+#include "libsmb/proto.h"
+#include "passdb.h"
+#include "cmdline_contexts.h"
+#include "passwd_proto.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/param/param.h"
+#include "lib/util/memcache.h"
+
+/*
+ * Next two lines needed for SunOS and don't
+ * hurt anything else...
+ */
+extern char *optarg;
+extern int optind;
+
+/* forced running in root-mode */
+static bool got_username = False;
+static bool stdin_passwd_get = False;
+static fstring user_name;
+static char *new_passwd = NULL;
+static const char *remote_machine = NULL;
+
+static fstring ldap_secret;
+
+
+/*********************************************************
+ Print command usage on stderr and die.
+**********************************************************/
+static void usage(void)
+{
+ printf("When run by root:\n");
+ printf(" smbpasswd [options] [username]\n");
+ printf("otherwise:\n");
+ printf(" smbpasswd [options]\n\n");
+
+ printf("options:\n");
+ printf(" -L local mode (must be first option)\n");
+ printf(" -h print this usage message\n");
+ printf(" -s use stdin for password prompt\n");
+ printf(" -c smb.conf file Use the given path to the smb.conf file\n");
+ printf(" -D LEVEL debug level\n");
+ printf(" -r MACHINE remote machine\n");
+ printf(" -U USER remote username (e.g. SAM/user)\n");
+
+ printf("extra options when run by root or in local mode:\n");
+ printf(" -a add user\n");
+ printf(" -d disable user\n");
+ printf(" -e enable user\n");
+ printf(" -i interdomain trust account\n");
+ printf(" -m machine trust account\n");
+ printf(" -n set no password\n");
+ printf(" -W use stdin ldap admin password\n");
+ printf(" -w PASSWORD ldap admin password\n");
+ printf(" -x delete user\n");
+ printf(" -R ORDER name resolve order\n");
+
+ exit(1);
+}
+
+static void set_line_buffering(FILE *f)
+{
+ setvbuf(f, NULL, _IOLBF, 0);
+}
+
+/*******************************************************************
+ Process command line options
+ ******************************************************************/
+
+static int process_options(int argc, char **argv, int local_flags,
+ struct loadparm_context *lp_ctx)
+{
+ int ch;
+ const char *configfile = get_dyn_CONFIGFILE();
+
+ local_flags |= LOCAL_SET_PASSWORD;
+
+ ZERO_STRUCT(user_name);
+
+ user_name[0] = '\0';
+
+ while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:LWS:")) != EOF) {
+ switch(ch) {
+ case 'L':
+ if (getuid() != 0) {
+ fprintf(stderr, "smbpasswd -L can only be used by root.\n");
+ exit(1);
+ }
+ local_flags |= LOCAL_AM_ROOT;
+ break;
+ case 'c':
+ configfile = optarg;
+ set_dyn_CONFIGFILE(optarg);
+ break;
+ case 'a':
+ local_flags |= LOCAL_ADD_USER;
+ break;
+ case 'x':
+ local_flags |= LOCAL_DELETE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'd':
+ local_flags |= LOCAL_DISABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'e':
+ local_flags |= LOCAL_ENABLE_USER;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ break;
+ case 'm':
+ local_flags |= LOCAL_TRUST_ACCOUNT;
+ break;
+ case 'i':
+ local_flags |= LOCAL_INTERDOM_ACCOUNT;
+ break;
+ case 'j':
+ d_printf("See 'net join' for this functionality\n");
+ exit(1);
+ break;
+ case 'n':
+ local_flags |= LOCAL_SET_NO_PASSWORD;
+ local_flags &= ~LOCAL_SET_PASSWORD;
+ SAFE_FREE(new_passwd);
+ new_passwd = smb_xstrdup("NO PASSWORD");
+ break;
+ case 'r':
+ remote_machine = optarg;
+ break;
+ case 's':
+ set_line_buffering(stdin);
+ set_line_buffering(stdout);
+ set_line_buffering(stderr);
+ stdin_passwd_get = True;
+ break;
+ case 'w':
+ local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
+ fstrcpy(ldap_secret, optarg);
+ break;
+ case 'R':
+ lpcfg_set_cmdline(lp_ctx, "name resolve order", optarg);
+ break;
+ case 'D':
+ lpcfg_set_cmdline(lp_ctx, "log level", optarg);
+ break;
+ case 'U': {
+ got_username = True;
+ fstrcpy(user_name, optarg);
+ break;
+ case 'W':
+ local_flags |= LOCAL_SET_LDAP_ADMIN_PW;
+ *ldap_secret = '\0';
+ break;
+ }
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (!got_username)
+ fstrcpy(user_name, "");
+ break;
+ case 1:
+ if (!(local_flags & LOCAL_AM_ROOT)) {
+ usage();
+ } else {
+ if (got_username) {
+ usage();
+ } else {
+ fstrcpy(user_name, argv[0]);
+ }
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if (!lp_load_global(configfile)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ configfile);
+ exit(1);
+ }
+
+ return local_flags;
+}
+
+/*************************************************************
+ Utility function to prompt for new password.
+*************************************************************/
+static char *prompt_for_new_password(bool stdin_get)
+{
+ char *p;
+ fstring new_pw;
+
+ ZERO_ARRAY(new_pw);
+
+ p = get_pass("New SMB password:", stdin_get);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ fstrcpy(new_pw, p);
+ SAFE_FREE(p);
+
+ p = get_pass("Retype new SMB password:", stdin_get);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ if (strcmp(p, new_pw)) {
+ fprintf(stderr, "Mismatch - password unchanged.\n");
+ ZERO_ARRAY(new_pw);
+ SAFE_FREE(p);
+ return NULL;
+ }
+
+ return p;
+}
+
+
+/*************************************************************
+ Change a password either locally or remotely.
+*************************************************************/
+
+static NTSTATUS password_change(const char *remote_mach,
+ const char *domain, const char *username,
+ const char *old_passwd, const char *new_pw,
+ int local_flags)
+{
+ NTSTATUS ret;
+ char *err_str = NULL;
+ char *msg_str = NULL;
+
+ if (remote_mach != NULL) {
+ if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|
+ LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|
+ LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) {
+ /* these things can't be done remotely yet */
+ fprintf(stderr, "Invalid remote operation!\n");
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ret = remote_password_change(remote_mach,
+ domain, username,
+ old_passwd, new_pw, &err_str);
+ } else {
+ ret = local_password_change(username, local_flags, new_pw,
+ &err_str, &msg_str);
+ }
+
+ if (msg_str) {
+ printf("%s", msg_str);
+ }
+ if (err_str) {
+ fprintf(stderr, "%s", err_str);
+ }
+ if (!NT_STATUS_IS_OK(ret) && !err_str) {
+ fprintf(stderr, "Failed to change password!\n");
+ }
+
+ SAFE_FREE(msg_str);
+ SAFE_FREE(err_str);
+ return ret;
+}
+
+/*******************************************************************
+ Store the LDAP admin password in secrets.tdb
+ ******************************************************************/
+static bool store_ldap_admin_pw (char* pw)
+{
+ if (!pw)
+ return False;
+
+ if (!secrets_init())
+ return False;
+
+ return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw);
+}
+
+
+/*************************************************************
+ Handle password changing for root.
+*************************************************************/
+
+static int process_root(int local_flags)
+{
+ struct passwd *pwd;
+ int result = 0;
+ char *old_passwd = NULL;
+
+ if (local_flags & LOCAL_SET_LDAP_ADMIN_PW) {
+ const char *ldap_admin_dn = lp_ldap_admin_dn();
+ if ( ! *ldap_admin_dn ) {
+ DEBUG(0,("ERROR: 'ldap admin dn' not defined! Please check your smb.conf\n"));
+ goto done;
+ }
+
+ printf("Setting stored password for \"%s\" in secrets.tdb\n", ldap_admin_dn);
+ if ( ! *ldap_secret ) {
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+ if (new_passwd == NULL) {
+ fprintf(stderr, "Failed to read new password!\n");
+ exit(1);
+ }
+ fstrcpy(ldap_secret, new_passwd);
+ }
+ if (!store_ldap_admin_pw(ldap_secret)) {
+ DEBUG(0,("ERROR: Failed to store the ldap admin password!\n"));
+ }
+ goto done;
+ }
+
+ /* Ensure passdb startup(). */
+ if(!initialize_password_db(False, NULL)) {
+ DEBUG(0, ("Failed to open passdb!\n"));
+ exit(1);
+ }
+
+ /* Ensure we have a SAM sid. */
+ get_global_sam_sid();
+
+ /*
+ * Ensure both add/delete user are not set
+ * Ensure add/delete user and either remote machine or join domain are
+ * not both set.
+ */
+ if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) ||
+ ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) &&
+ (remote_machine != NULL))) {
+ usage();
+ }
+
+ /* Only load interfaces if we are doing network operations. */
+
+ if (remote_machine) {
+ load_interfaces();
+ }
+
+ if (!user_name[0] && (pwd = getpwuid_alloc(talloc_tos(), geteuid()))) {
+ fstrcpy(user_name, pwd->pw_name);
+ TALLOC_FREE(pwd);
+ }
+
+ if (!user_name[0]) {
+ fprintf(stderr,"You must specify a username\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_TRUST_ACCOUNT) {
+ /* add the $ automatically */
+ size_t user_name_len = strlen(user_name);
+
+ if (user_name[user_name_len - 1] == '$') {
+ user_name_len--;
+ } else {
+ if (user_name_len + 2 > sizeof(user_name)) {
+ fprintf(stderr, "machine name too long\n");
+ exit(1);
+ }
+ user_name[user_name_len] = '$';
+ user_name[user_name_len + 1] = '\0';
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ SAFE_FREE(new_passwd);
+
+ /*
+ * Remove any trailing '$' before we
+ * generate the initial machine password.
+ */
+ new_passwd = smb_xstrndup(user_name, user_name_len);
+ if (!strlower_m(new_passwd)) {
+ fprintf(stderr, "strlower_m %s failed\n",
+ new_passwd);
+ exit(1);
+ }
+ }
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ size_t user_name_len = strlen(user_name);
+
+ if (user_name[user_name_len - 1] != '$') {
+ if (user_name_len + 2 > sizeof(user_name)) {
+ fprintf(stderr, "machine name too long\n");
+ exit(1);
+ }
+ user_name[user_name_len] = '$';
+ user_name[user_name_len + 1] = '\0';
+ }
+
+ if ((local_flags & LOCAL_ADD_USER) && (new_passwd == NULL)) {
+ /*
+ * Prompt for trusting domain's account password
+ */
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get newpassword.\n");
+ exit(1);
+ }
+ }
+ } else {
+
+ if (remote_machine != NULL) {
+ old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+ if(!old_passwd) {
+ fprintf(stderr, "Unable to get old password.\n");
+ exit(1);
+ }
+ }
+
+ if (!(local_flags & LOCAL_SET_PASSWORD)) {
+
+ /*
+ * If we are trying to enable a user, first we need to find out
+ * if they are using a modern version of the smbpasswd file that
+ * disables a user by just writing a flag into the file. If so
+ * then we can re-enable a user without prompting for a new
+ * password. If not (ie. they have a no stored password in the
+ * smbpasswd file) then we need to prompt for a new password.
+ */
+
+ if(local_flags & LOCAL_ENABLE_USER) {
+ struct samu *sampass = NULL;
+
+ sampass = samu_new( NULL );
+ if (!sampass) {
+ fprintf(stderr, "talloc fail for struct samu.\n");
+ exit(1);
+ }
+ if (!pdb_getsampwnam(sampass, user_name)) {
+ fprintf(stderr, "Failed to find user %s in passdb backend.\n",
+ user_name );
+ exit(1);
+ }
+
+ if(pdb_get_nt_passwd(sampass) == NULL) {
+ local_flags |= LOCAL_SET_PASSWORD;
+ }
+ TALLOC_FREE(sampass);
+ }
+ }
+
+ if((local_flags & LOCAL_SET_PASSWORD) && (new_passwd == NULL)) {
+
+ new_passwd = prompt_for_new_password(stdin_passwd_get);
+ if(!new_passwd) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(password_change(remote_machine,
+ NULL, user_name,
+ old_passwd, new_passwd,
+ local_flags))) {
+ result = 1;
+ goto done;
+ }
+
+ if(remote_machine) {
+ printf("Password changed for user %s on %s.\n", user_name, remote_machine );
+ } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) {
+ struct samu *sampass = NULL;
+
+ sampass = samu_new( NULL );
+ if (!sampass) {
+ fprintf(stderr, "talloc fail for struct samu.\n");
+ exit(1);
+ }
+
+ if (!pdb_getsampwnam(sampass, user_name)) {
+ fprintf(stderr, "Failed to find user %s in passdb backend.\n",
+ user_name );
+ exit(1);
+ }
+
+ printf("Password changed for user %s.", user_name );
+ if(pdb_get_acct_ctrl(sampass)&ACB_DISABLED) {
+ printf(" User has disabled flag set.");
+ }
+ if(pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) {
+ printf(" User has no password flag set.");
+ }
+ printf("\n");
+ TALLOC_FREE(sampass);
+ }
+
+ done:
+ SAFE_FREE(old_passwd);
+ SAFE_FREE(new_passwd);
+ return result;
+}
+
+
+/*************************************************************
+ Handle password changing for non-root.
+*************************************************************/
+
+static int process_nonroot(int local_flags)
+{
+ struct passwd *pwd = NULL;
+ int result = 0;
+ char *old_pw = NULL;
+ char *new_pw = NULL;
+ const char *username = user_name;
+ const char *domain = NULL;
+ char *p = NULL;
+
+ if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) {
+ /* Extra flags that we can't honor non-root */
+ usage();
+ }
+
+ if (!user_name[0]) {
+ pwd = getpwuid_alloc(talloc_tos(), getuid());
+ if (pwd) {
+ fstrcpy(user_name,pwd->pw_name);
+ TALLOC_FREE(pwd);
+ } else {
+ fprintf(stderr, "smbpasswd: cannot lookup user name for uid %u\n", (unsigned int)getuid());
+ exit(1);
+ }
+ }
+
+ /* Allow domain as part of the username */
+ if ((p = strchr_m(user_name, '\\')) ||
+ (p = strchr_m(user_name, '/')) ||
+ (p = strchr_m(user_name, *lp_winbind_separator()))) {
+ *p = '\0';
+ username = p + 1;
+ domain = user_name;
+ }
+
+ /*
+ * A non-root user is always setting a password
+ * via a remote machine (even if that machine is
+ * localhost).
+ */
+
+ load_interfaces(); /* Delayed from main() */
+
+ if (remote_machine != NULL) {
+ if (!is_ipaddress(remote_machine)) {
+ domain = remote_machine;
+ }
+ } else {
+ remote_machine = "127.0.0.1";
+
+ /*
+ * If we deal with a local user, change the password for the
+ * user in our SAM.
+ */
+ domain = get_global_sam_name();
+ }
+
+ old_pw = get_pass("Old SMB password:",stdin_passwd_get);
+ if (old_pw == NULL) {
+ fprintf(stderr, "Unable to get old password.\n");
+ exit(1);
+ }
+
+ if (!new_passwd) {
+ new_pw = prompt_for_new_password(stdin_passwd_get);
+ }
+ else
+ new_pw = smb_xstrdup(new_passwd);
+
+ if (!new_pw) {
+ fprintf(stderr, "Unable to get new password.\n");
+ exit(1);
+ }
+
+ if (!NT_STATUS_IS_OK(password_change(remote_machine,
+ domain, username,
+ old_pw, new_pw, 0))) {
+ result = 1;
+ goto done;
+ }
+
+ printf("Password changed for user %s\n", username);
+
+ done:
+ SAFE_FREE(old_pw);
+ SAFE_FREE(new_pw);
+
+ return result;
+}
+
+
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main(int argc, char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+ struct memcache *mcache = NULL;
+ int local_flags = 0;
+ int ret;
+
+ mcache = memcache_init(NULL, 0);
+ if (mcache == NULL) {
+ fprintf(stderr, "%s: memcache_init failed\n", __location__);
+ return 1;
+ }
+ memcache_set_global(mcache);
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ if (getuid() == 0) {
+ local_flags = LOCAL_AM_ROOT;
+ }
+
+ smb_init_locale();
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ fprintf(stderr,
+ "Failed to initialise the global parameter structure.\n");
+ return 1;
+ }
+
+ local_flags = process_options(argc, argv, local_flags, lp_ctx);
+
+ setup_logging("smbpasswd", DEBUG_STDERR);
+
+ /* Check the effective uid - make sure we are not setuid */
+ if (is_setuid_root()) {
+ fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n");
+ exit(1);
+ }
+
+ if (local_flags & LOCAL_AM_ROOT) {
+ bool ok;
+
+ ok = secrets_init();
+ if (!ok) {
+ return 1;
+ }
+ ret = process_root(local_flags);
+ } else {
+ ret = process_nonroot(local_flags);
+ }
+
+ gfree_all();
+
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/smbtree.c b/source3/utils/smbtree.c
new file mode 100644
index 0000000..80dfa0a
--- /dev/null
+++ b/source3/utils/smbtree.c
@@ -0,0 +1,295 @@
+/*
+ Unix SMB/CIFS implementation.
+ Network neighbourhood browser.
+
+ Copyright (C) Tim Potter 2000
+ Copyright (C) Jelmer Vernooij 2003
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/cmdline/cmdline.h"
+#include "rpc_client/cli_pipe.h"
+#include "../librpc/gen_ndr/ndr_srvsvc_c.h"
+#include "libsmb/libsmb.h"
+#include "libsmb/namequery.h"
+#include "libsmb/clirap.h"
+#include "../libcli/smb/smbXcli_base.h"
+#include "nameserv.h"
+#include "libsmbclient.h"
+
+/* How low can we go? */
+
+enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
+static enum tree_level level = LEV_SHARE;
+
+static void get_auth_data_with_context_fn(
+ SMBCCTX *context,
+ const char *server,
+ const char *share,
+ char *domain,
+ int domain_len,
+ char *user,
+ int user_len,
+ char *password,
+ int password_len)
+{
+ struct cli_credentials *creds = samba_cmdline_get_creds();
+ size_t len;
+
+ len = strlcpy(domain, cli_credentials_get_domain(creds), domain_len);
+ if ((int)len >= domain_len) {
+ return;
+ }
+ len = strlcpy(
+ user, cli_credentials_get_username(creds), user_len);
+ if ((int)len >= user_len) {
+ return;
+ }
+ len = strlcpy(
+ password, cli_credentials_get_password(creds), password_len);
+ if ((int)len >= password_len) {
+ /* pointless, but what can you do... */
+ return;
+ }
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc, char *argv[])
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char **argv_const = discard_const_p(const char *, argv);
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "domains",
+ .shortName = 'D',
+ .argInfo = POPT_ARG_VAL,
+ .arg = &level,
+ .val = LEV_WORKGROUP,
+ .descrip = "List only domains (workgroups) of tree" ,
+ },
+ {
+ .longName = "servers",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_VAL,
+ .arg = &level,
+ .val = LEV_SERVER,
+ .descrip = "List domains(workgroups) and servers of tree" ,
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ poptContext pc;
+ SMBCCTX *ctx = NULL;
+ SMBCFILE *workgroups = NULL;
+ struct smbc_dirent *dirent = NULL;
+ bool ok;
+ int ret, result = 1;
+ int opt;
+ int debuglevel;
+
+ /* Initialise samba stuff */
+ smb_init_locale();
+
+ setlinebuf(stdout);
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv_const,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ samba_cmdline_burn(argc, argv);
+
+ debuglevel = DEBUGLEVEL;
+
+ ctx = smbc_new_context();
+ if (ctx == NULL) {
+ perror("smbc_new_context");
+ goto fail;
+ }
+ ret = smbc_setConfiguration(ctx, get_dyn_CONFIGFILE());
+ if (ret == -1) {
+ perror("smbc_setConfiguration");
+ goto fail;
+ }
+ smbc_setDebug(ctx, debuglevel);
+ ok = smbc_setOptionProtocols(ctx, NULL, "NT1");
+ if (!ok) {
+ perror("smbc_setOptionProtocols");
+ goto fail;
+ }
+ smbc_setFunctionAuthDataWithContext(
+ ctx, get_auth_data_with_context_fn);
+
+ ok = smbc_init_context(ctx);
+ if (!ok) {
+ perror("smbc_init_context");
+ goto fail;
+ }
+
+ workgroups = smbc_getFunctionOpendir(ctx)(ctx, "smb://");
+ if (workgroups == NULL) {
+ DBG_ERR("This is utility doesn't work if netbios name "
+ "resolution is not configured.\n"
+ "If you are using SMB2 or SMB3, network browsing uses "
+ "WSD/LLMNR, which is not yet supported by Samba. SMB1 "
+ "is disabled by default on the latest Windows versions "
+ "for security reasons. It is still possible to access "
+ "the Samba resources directly via \\name or "
+ "\\ip.address.\n");
+ goto fail;
+ }
+
+ while ((dirent = smbc_getFunctionReaddir(ctx)(ctx, workgroups))
+ != NULL) {
+ char *url = NULL;
+ SMBCFILE *servers = NULL;
+
+ if (dirent->smbc_type != SMBC_WORKGROUP) {
+ continue;
+ }
+
+ printf("%s\n", dirent->name);
+
+ if (level == LEV_WORKGROUP) {
+ continue;
+ }
+
+ url = talloc_asprintf(
+ talloc_tos(), "smb://%s/", dirent->name);
+ if (url == NULL) {
+ perror("talloc_asprintf");
+ goto fail;
+ }
+
+ servers = smbc_getFunctionOpendir(ctx)(ctx, url);
+ if (servers == NULL) {
+ perror("smbc_opendir");
+ goto fail;
+ }
+ TALLOC_FREE(url);
+
+ while ((dirent = smbc_getFunctionReaddir(ctx)(ctx, servers))
+ != NULL) {
+ SMBCFILE *shares = NULL;
+ char *servername = NULL;
+
+ if (dirent->smbc_type != SMBC_SERVER) {
+ continue;
+ }
+
+ printf("\t\\\\%-15s\t\t%s\n",
+ dirent->name,
+ dirent->comment);
+
+ if (level == LEV_SERVER) {
+ continue;
+ }
+
+ /*
+ * The subsequent readdir for shares will
+ * overwrite the "server" readdir
+ */
+ servername = talloc_strdup(talloc_tos(), dirent->name);
+ if (servername == NULL) {
+ continue;
+ }
+
+ url = talloc_asprintf(
+ talloc_tos(), "smb://%s/", servername);
+ if (url == NULL) {
+ perror("talloc_asprintf");
+ goto fail;
+ }
+
+ shares = smbc_getFunctionOpendir(ctx)(ctx, url);
+ if (shares == NULL) {
+ perror("smbc_opendir");
+ goto fail;
+ }
+
+ while ((dirent = smbc_getFunctionReaddir(
+ ctx)(ctx, shares))
+ != NULL) {
+ printf("\t\t\\\\%s\\%-15s\t%s\n",
+ servername,
+ dirent->name,
+ dirent->comment);
+ }
+
+ ret = smbc_getFunctionClosedir(ctx)(ctx, shares);
+ if (ret == -1) {
+ perror("smbc_closedir");
+ goto fail;
+ }
+
+ TALLOC_FREE(servername);
+ TALLOC_FREE(url);
+ }
+
+ ret = smbc_getFunctionClosedir(ctx)(ctx, servers);
+ if (ret == -1) {
+ perror("smbc_closedir");
+ goto fail;
+ }
+ }
+
+ ret = smbc_getFunctionClosedir(ctx)(ctx, workgroups);
+ if (ret == -1) {
+ perror("smbc_closedir");
+ goto fail;
+ }
+
+ result = 0;
+fail:
+ if (ctx != NULL) {
+ smbc_free_context(ctx, 0);
+ ctx = NULL;
+ }
+ gfree_all();
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return result;
+}
diff --git a/source3/utils/status.c b/source3/utils/status.c
new file mode 100644
index 0000000..4102b41
--- /dev/null
+++ b/source3/utils/status.c
@@ -0,0 +1,1241 @@
+/*
+ Unix SMB/CIFS implementation.
+ status reporting
+ Copyright (C) Andrew Tridgell 1994-1998
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+
+ Revision History:
+
+ 12 aug 96: Erik.Devriendt@te6.siemens.be
+ added support for shared memory implementation of share mode locking
+
+ 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe)
+ Added -L (locks only) -S (shares only) flags and code
+
+*/
+
+/*
+ * This program reports current SMB connections
+ */
+
+#include "includes.h"
+#include "lib/util/server_id.h"
+#include "smbd/globals.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "dbwrap/dbwrap.h"
+#include "dbwrap/dbwrap_open.h"
+#include "../libcli/security/security.h"
+#include "session.h"
+#include "locking/share_mode_lock.h"
+#include "locking/proto.h"
+#include "messages.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "smbd/smbd.h"
+#include "librpc/gen_ndr/notify.h"
+#include "conn_tdb.h"
+#include "serverid.h"
+#include "status_profile.h"
+#include "status.h"
+#include "status_json.h"
+#include "smbd/notifyd/notifyd_db.h"
+#include "cmdline_contexts.h"
+#include "locking/leases_db.h"
+#include "lib/util/string_wrappers.h"
+#include "lib/param/param.h"
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#include "auth/common_auth.h"
+#endif /* HAVE_JANSSON */
+
+#define SMB_MAXPIDS 2048
+static uid_t Ucrit_uid = 0; /* added by OH */
+static struct server_id Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */
+static int Ucrit_MaxPid=0; /* added by OH */
+static unsigned int Ucrit_IsActive = 0; /* added by OH */
+
+static bool verbose, brief;
+static bool shares_only; /* Added by RJS */
+static bool locks_only; /* Added by RJS */
+static bool processes_only;
+static bool show_brl;
+static bool numeric_only;
+static bool do_checks = true;
+
+const char *username = NULL;
+
+/* added by OH */
+static void Ucrit_addUid(uid_t uid)
+{
+ Ucrit_uid = uid;
+ Ucrit_IsActive = 1;
+}
+
+static unsigned int Ucrit_checkUid(uid_t uid)
+{
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ if ( uid == Ucrit_uid )
+ return 1;
+
+ return 0;
+}
+
+static unsigned int Ucrit_checkPid(struct server_id pid)
+{
+ int i;
+
+ if ( !Ucrit_IsActive )
+ return 1;
+
+ for (i=0;i<Ucrit_MaxPid;i++) {
+ if (server_id_equal(&pid, &Ucrit_pid[i])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static bool Ucrit_addPid( struct server_id pid )
+{
+ if ( !Ucrit_IsActive )
+ return True;
+
+ if ( Ucrit_MaxPid >= SMB_MAXPIDS ) {
+ fprintf(stderr, "ERROR: More than %d pids for user %s!\n",
+ SMB_MAXPIDS, uidtoname(Ucrit_uid));
+
+ return False;
+ }
+
+ Ucrit_pid[Ucrit_MaxPid++] = pid;
+
+ return True;
+}
+
+static int print_share_mode_stdout(struct traverse_state *state,
+ const char *pid,
+ const char *user_name,
+ const char *denymode,
+ int access_mask,
+ const char *rw,
+ const char *oplock,
+ const char *servicepath,
+ const char *filename,
+ const char *timestr)
+{
+ if (state->first) {
+ d_printf("\nLocked files:\n");
+ d_printf("Pid User(ID) DenyMode Access R/W Oplock SharePath Name Time\n");
+ d_printf("--------------------------------------------------------------------------------------------------\n");
+
+ state->first = false;
+ }
+
+ d_printf("%-11s %-9s %-10s 0x%-8x %-10s %-14s %s %s %s",
+ pid, user_name, denymode, access_mask, rw, oplock,
+ servicepath, filename, timestr);
+ return 0;
+}
+
+static int prepare_share_mode(struct traverse_state *state)
+{
+ if (!state->json_output) {
+ /* only print header line if there are open files */
+ state->first = true;
+ } else {
+ add_section_to_json(state, "open_files");
+ }
+ return 0;
+}
+
+static uint32_t map_share_mode_to_deny_mode(
+ uint32_t share_access, uint32_t private_options)
+{
+ switch (share_access & ~FILE_SHARE_DELETE) {
+ case FILE_SHARE_NONE:
+ return DENY_ALL;
+ case FILE_SHARE_READ:
+ return DENY_WRITE;
+ case FILE_SHARE_WRITE:
+ return DENY_READ;
+ case FILE_SHARE_READ|FILE_SHARE_WRITE:
+ return DENY_NONE;
+ }
+ if (private_options & NTCREATEX_FLAG_DENY_DOS) {
+ return DENY_DOS;
+ } else if (private_options & NTCREATEX_FLAG_DENY_FCB) {
+ return DENY_FCB;
+ }
+
+ return (uint32_t)-1;
+}
+
+static int print_share_mode(struct file_id fid,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ void *private_data)
+{
+ const char *denymode = NULL;
+ uint denymode_int;
+ const char *oplock = NULL;
+ const char *pid = NULL;
+ const char *rw = NULL;
+ const char *filename = NULL;
+ const char *timestr = NULL;
+ const char *user_str = NULL;
+ uint32_t lstate;
+ struct traverse_state *state = (struct traverse_state *)private_data;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ if (do_checks && !is_valid_share_mode_entry(e)) {
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+ }
+
+ if (do_checks && !serverid_exists(&e->pid)) {
+ /* the process for this entry does not exist any more */
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+ }
+
+ if (Ucrit_checkPid(e->pid)) {
+ struct server_id_buf tmp;
+ pid = server_id_str_buf(e->pid, &tmp);
+ if (state->resolve_uids) {
+ user_str = talloc_asprintf(tmp_ctx, "%s", uidtoname(e->uid));
+ } else {
+ user_str = talloc_asprintf(tmp_ctx, "%u", (unsigned int)e->uid);
+ }
+ if (user_str == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ denymode_int = map_share_mode_to_deny_mode(e->share_access,
+ e->private_options);
+ switch (denymode_int) {
+ case DENY_NONE:
+ denymode = "DENY_NONE";
+ break;
+ case DENY_ALL:
+ denymode = "DENY_ALL";
+ break;
+ case DENY_DOS:
+ denymode = "DENY_DOS";
+ break;
+ case DENY_READ:
+ denymode = "DENY_READ";
+ break;
+ case DENY_WRITE:
+ denymode = "DENY_WRITE";
+ break;
+ case DENY_FCB:
+ denymode = "DENY_FCB";
+ break;
+ default: {
+ denymode = talloc_asprintf(tmp_ctx,
+ "UNKNOWN(0x%08x)",
+ denymode_int);
+ if (denymode == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+ fprintf(stderr,
+ "unknown-please report ! "
+ "e->share_access = 0x%x, "
+ "e->private_options = 0x%x\n",
+ (unsigned int)e->share_access,
+ (unsigned int)e->private_options);
+ break;
+ }
+ }
+ filename = talloc_asprintf(tmp_ctx,
+ "%s%s",
+ d->base_name,
+ (d->stream_name != NULL) ? d->stream_name : "");
+ if (filename == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+ if ((e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))==
+ (FILE_READ_DATA|FILE_WRITE_DATA)) {
+ rw = "RDWR";
+ } else if (e->access_mask & FILE_WRITE_DATA) {
+ rw = "WRONLY";
+ } else {
+ rw = "RDONLY";
+ }
+
+ if (e->op_type & BATCH_OPLOCK) {
+ oplock = "BATCH";
+ } else if (e->op_type & EXCLUSIVE_OPLOCK) {
+ oplock = "EXCLUSIVE";
+ } else if (e->op_type & LEVEL_II_OPLOCK) {
+ oplock = "LEVEL_II";
+ } else if (e->op_type == LEASE_OPLOCK) {
+ NTSTATUS status;
+
+ status = leases_db_get(
+ &e->client_guid,
+ &e->lease_key,
+ &d->id,
+ &lstate, /* current_state */
+ NULL, /* breaking */
+ NULL, /* breaking_to_requested */
+ NULL, /* breaking_to_required */
+ NULL, /* lease_version */
+ NULL); /* epoch */
+
+ if (NT_STATUS_IS_OK(status)) {
+ oplock = talloc_asprintf(tmp_ctx, "LEASE(%s%s%s)%s%s%s",
+ (lstate & SMB2_LEASE_READ)?"R":"",
+ (lstate & SMB2_LEASE_WRITE)?"W":"",
+ (lstate & SMB2_LEASE_HANDLE)?"H":"",
+ (lstate & SMB2_LEASE_READ)?"":" ",
+ (lstate & SMB2_LEASE_WRITE)?"":" ",
+ (lstate & SMB2_LEASE_HANDLE)?"":" ");
+ } else {
+ oplock = "LEASE STATE UNKNOWN";
+ }
+ } else {
+ oplock = "NONE";
+ }
+
+ timestr = time_to_asc((time_t)e->time.tv_sec);
+
+ if (!state->json_output) {
+ print_share_mode_stdout(state,
+ pid,
+ user_str,
+ denymode,
+ (unsigned int)e->access_mask,
+ rw,
+ oplock,
+ d->servicepath,
+ filename,
+ timestr);
+ } else {
+ print_share_mode_json(state,
+ d,
+ e,
+ fid,
+ user_str,
+ oplock,
+ lstate,
+ filename);
+ }
+ }
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+}
+
+static void print_brl_stdout(struct traverse_state *state,
+ char *pid,
+ char *id,
+ const char *desc,
+ intmax_t start,
+ intmax_t size,
+ const char *sharepath,
+ char *fname)
+{
+ if (state->first) {
+ d_printf("Byte range locks:\n");
+ d_printf("Pid dev:inode R/W start size SharePath Name\n");
+ d_printf("--------------------------------------------------------------------------------\n");
+
+ state->first = false;
+ }
+ d_printf("%-10s %-15s %-4s %-9jd %-9jd %-24s %-24s\n",
+ pid, id, desc, start, size, sharepath, fname);
+}
+
+static int prepare_brl(struct traverse_state *state)
+{
+ if (!state->json_output) {
+ /* only print header line if there are locked files */
+ state->first = true;
+ } else {
+ add_section_to_json(state, "byte_range_locks");
+ }
+ return 0;
+}
+
+static void print_brl(struct file_id id,
+ struct server_id pid,
+ enum brl_type lock_type,
+ enum brl_flavour lock_flav,
+ br_off start,
+ br_off size,
+ void *private_data)
+{
+ unsigned int i;
+ static const struct {
+ enum brl_type lock_type;
+ const char *desc;
+ } lock_types[] = {
+ { READ_LOCK, "R" },
+ { WRITE_LOCK, "W" },
+ { UNLOCK_LOCK, "U" }
+ };
+ const char *desc="X";
+ const char *sharepath = "";
+ char *fname = NULL;
+ struct share_mode_lock *share_mode;
+ struct server_id_buf tmp;
+ struct file_id_buf ftmp;
+ struct traverse_state *state = (struct traverse_state *)private_data;
+
+ share_mode = fetch_share_mode_unlocked(NULL, id);
+ if (share_mode) {
+ fname = share_mode_filename(NULL, share_mode);
+ sharepath = share_mode_servicepath(share_mode);
+ } else {
+ fname = talloc_strdup(NULL, "");
+ if (fname == NULL) {
+ return;
+ }
+ }
+
+ for (i=0;i<ARRAY_SIZE(lock_types);i++) {
+ if (lock_type == lock_types[i].lock_type) {
+ desc = lock_types[i].desc;
+ }
+ }
+
+ if (!state->json_output) {
+ print_brl_stdout(state,
+ server_id_str_buf(pid, &tmp),
+ file_id_str_buf(id, &ftmp),
+ desc,
+ (intmax_t)start,
+ (intmax_t)size,
+ sharepath,
+ fname);
+ } else {
+ print_brl_json(state,
+ pid,
+ id,
+ desc,
+ lock_flav,
+ (intmax_t)start,
+ (intmax_t)size,
+ sharepath,
+ fname);
+
+ }
+
+ TALLOC_FREE(fname);
+ TALLOC_FREE(share_mode);
+}
+
+static const char *session_dialect_str(uint16_t dialect)
+{
+ static fstring unknown_dialect;
+
+ switch(dialect){
+ case SMB2_DIALECT_REVISION_000:
+ return "NT1";
+ case SMB2_DIALECT_REVISION_202:
+ return "SMB2_02";
+ case SMB2_DIALECT_REVISION_210:
+ return "SMB2_10";
+ case SMB2_DIALECT_REVISION_222:
+ return "SMB2_22";
+ case SMB2_DIALECT_REVISION_224:
+ return "SMB2_24";
+ case SMB3_DIALECT_REVISION_300:
+ return "SMB3_00";
+ case SMB3_DIALECT_REVISION_302:
+ return "SMB3_02";
+ case SMB3_DIALECT_REVISION_310:
+ return "SMB3_10";
+ case SMB3_DIALECT_REVISION_311:
+ return "SMB3_11";
+ }
+
+ fstr_sprintf(unknown_dialect, "Unknown (0x%04x)", dialect);
+ return unknown_dialect;
+}
+
+static int traverse_connections_stdout(struct traverse_state *state,
+ const char *servicename,
+ char *server_id,
+ const char *machine,
+ const char *timestr,
+ const char *encryption,
+ const char *signing)
+{
+ d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n",
+ servicename, server_id, machine, timestr, encryption, signing);
+
+ return 0;
+}
+
+static int prepare_connections(struct traverse_state *state)
+{
+ if (!state->json_output) {
+ /* always print header line */
+ d_printf("\n%-12s %-7s %-13s %-32s %-12s %-12s\n", "Service", "pid", "Machine", "Connected at", "Encryption", "Signing");
+ d_printf("---------------------------------------------------------------------------------------------\n");
+ } else {
+ add_section_to_json(state, "tcons");
+ }
+ return 0;
+}
+
+static int traverse_connections(const struct connections_data *crec,
+ void *private_data)
+{
+ struct server_id_buf tmp;
+ char *timestr = NULL;
+ int result = 0;
+ const char *encryption = "-";
+ enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
+ const char *signing = "-";
+ enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
+ struct traverse_state *state = (struct traverse_state *)private_data;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ if (crec->cnum == TID_FIELD_INVALID) {
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+ }
+
+ if (do_checks &&
+ (!process_exists(crec->pid) || !Ucrit_checkUid(crec->uid))) {
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+ }
+
+ timestr = timestring(tmp_ctx, nt_time_to_unix(crec->start));
+ if (timestr == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (smbXsrv_is_encrypted(crec->encryption_flags)) {
+ switch (crec->cipher) {
+ case SMB_ENCRYPTION_GSSAPI:
+ encryption = "GSSAPI";
+ break;
+ case SMB2_ENCRYPTION_AES128_CCM:
+ encryption = "AES-128-CCM";
+ break;
+ case SMB2_ENCRYPTION_AES128_GCM:
+ encryption = "AES-128-GCM";
+ break;
+ default:
+ encryption = "???";
+ break;
+ }
+ encryption_degree = CRYPTO_DEGREE_FULL;
+ }
+
+ if (smbXsrv_is_signed(crec->signing_flags)) {
+ switch (crec->signing) {
+ case SMB2_SIGNING_MD5_SMB1:
+ signing = "HMAC-MD5";
+ break;
+ case SMB2_SIGNING_HMAC_SHA256:
+ signing = "HMAC-SHA256";
+ break;
+ case SMB2_SIGNING_AES128_CMAC:
+ signing = "AES-128-CMAC";
+ break;
+ case SMB2_SIGNING_AES128_GMAC:
+ signing = "AES-128-GMAC";
+ break;
+ default:
+ signing = "???";
+ break;
+ }
+ signing_degree = CRYPTO_DEGREE_FULL;
+ }
+
+ if (!state->json_output) {
+ result = traverse_connections_stdout(state,
+ crec->servicename,
+ server_id_str_buf(crec->pid, &tmp),
+ crec->machine,
+ timestr,
+ encryption,
+ signing);
+ } else {
+ result = traverse_connections_json(state,
+ crec,
+ encryption,
+ encryption_degree,
+ signing,
+ signing_degree);
+ }
+
+ TALLOC_FREE(timestr);
+ TALLOC_FREE(tmp_ctx);
+
+ return result;
+}
+
+static int traverse_sessionid_stdout(struct traverse_state *state,
+ char *server_id,
+ char *uid_gid_str,
+ char *machine_hostname,
+ const char *dialect,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree)
+{
+ fstring encryption;
+ fstring signing;
+
+ if (encryption_degree == CRYPTO_DEGREE_FULL) {
+ fstr_sprintf(encryption, "%s", encryption_cipher);
+ } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) {
+ fstr_sprintf(encryption, "partial(%s)", encryption_cipher);
+ } else {
+ fstr_sprintf(encryption, "-");
+ }
+ if (signing_degree == CRYPTO_DEGREE_FULL) {
+ fstr_sprintf(signing, "%s", signing_cipher);
+ } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) {
+ fstr_sprintf(signing, "partial(%s)", signing_cipher);
+ } else {
+ fstr_sprintf(signing, "-");
+ }
+
+ d_printf("%-7s %-25s %-41s %-17s %-20s %-21s\n",
+ server_id, uid_gid_str, machine_hostname, dialect, encryption,
+ signing);
+
+ return 0;
+}
+
+static int prepare_sessionid(struct traverse_state *state)
+{
+ if (!state->json_output) {
+ /* always print header line */
+ d_printf("\nSamba version %s\n",samba_version_string());
+ d_printf("%-7s %-12s %-12s %-41s %-17s %-20s %-21s\n", "PID", "Username", "Group", "Machine", "Protocol Version", "Encryption", "Signing");
+ d_printf("----------------------------------------------------------------------------------------------------------------------------------------\n");
+ } else {
+ add_section_to_json(state, "sessions");
+ }
+ return 0;
+
+}
+
+static int traverse_sessionid(const char *key, struct sessionid *session,
+ void *private_data)
+{
+ fstring uid_gid_str;
+ fstring uid_str;
+ fstring gid_str;
+ struct server_id_buf tmp;
+ char *machine_hostname = NULL;
+ int result = 0;
+ const char *encryption = "-";
+ enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE;
+ const char *signing = "-";
+ enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE;
+ struct traverse_state *state = (struct traverse_state *)private_data;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ if (do_checks &&
+ (!process_exists(session->pid) ||
+ !Ucrit_checkUid(session->uid))) {
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+ }
+
+ Ucrit_addPid(session->pid);
+
+ if (numeric_only) {
+ fstr_sprintf(gid_str, "%u", (unsigned int)session->gid);
+ fstr_sprintf(uid_str, "%u", (unsigned int)session->uid);
+ fstr_sprintf(uid_gid_str, "%-12u %-12u",
+ (unsigned int)session->uid,
+ (unsigned int)session->gid);
+ } else {
+ if (session->uid == -1 && session->gid == -1) {
+ /*
+ * The session is not fully authenticated yet.
+ */
+ fstrcpy(uid_gid_str, "(auth in progress)");
+ fstrcpy(gid_str, "(auth in progress)");
+ fstrcpy(uid_str, "(auth in progress)");
+ } else {
+ /*
+ * In theory it should not happen that one of
+ * session->uid and session->gid is valid (ie != -1)
+ * while the other is not (ie = -1), so we a check for
+ * that case that bails out would be reasonable.
+ */
+ const char *uid_name = "-1";
+ const char *gid_name = "-1";
+
+ if (session->uid != -1) {
+ uid_name = uidtoname(session->uid);
+ if (uid_name == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+ }
+ if (session->gid != -1) {
+ gid_name = gidtoname(session->gid);
+ if (gid_name == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+ }
+ fstr_sprintf(gid_str, "%s", gid_name);
+ fstr_sprintf(uid_str, "%s", uid_name);
+ fstr_sprintf(uid_gid_str, "%-12s %-12s",
+ uid_name, gid_name);
+ }
+ }
+
+ machine_hostname = talloc_asprintf(tmp_ctx, "%s (%s)",
+ session->remote_machine,
+ session->hostname);
+ if (machine_hostname == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+ }
+
+ if (smbXsrv_is_encrypted(session->encryption_flags) ||
+ smbXsrv_is_partially_encrypted(session->encryption_flags)) {
+ switch (session->cipher) {
+ case SMB2_ENCRYPTION_AES128_CCM:
+ encryption = "AES-128-CCM";
+ break;
+ case SMB2_ENCRYPTION_AES128_GCM:
+ encryption = "AES-128-GCM";
+ break;
+ case SMB2_ENCRYPTION_AES256_CCM:
+ encryption = "AES-256-CCM";
+ break;
+ case SMB2_ENCRYPTION_AES256_GCM:
+ encryption = "AES-256-GCM";
+ break;
+ default:
+ encryption = "???";
+ result = -1;
+ break;
+ }
+ if (smbXsrv_is_encrypted(session->encryption_flags)) {
+ encryption_degree = CRYPTO_DEGREE_FULL;
+ } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) {
+ encryption_degree = CRYPTO_DEGREE_PARTIAL;
+ }
+ }
+
+ if (smbXsrv_is_signed(session->signing_flags) ||
+ smbXsrv_is_partially_signed(session->signing_flags)) {
+ switch (session->signing) {
+ case SMB2_SIGNING_MD5_SMB1:
+ signing = "HMAC-MD5";
+ break;
+ case SMB2_SIGNING_HMAC_SHA256:
+ signing = "HMAC-SHA256";
+ break;
+ case SMB2_SIGNING_AES128_CMAC:
+ signing = "AES-128-CMAC";
+ break;
+ case SMB2_SIGNING_AES128_GMAC:
+ signing = "AES-128-GMAC";
+ break;
+ default:
+ signing = "???";
+ result = -1;
+ break;
+ }
+ if (smbXsrv_is_signed(session->signing_flags)) {
+ signing_degree = CRYPTO_DEGREE_FULL;
+ } else if (smbXsrv_is_partially_signed(session->signing_flags)) {
+ signing_degree = CRYPTO_DEGREE_PARTIAL;
+ }
+ }
+
+
+ if (!state->json_output) {
+ traverse_sessionid_stdout(state,
+ server_id_str_buf(session->pid, &tmp),
+ uid_gid_str,
+ machine_hostname,
+ session_dialect_str(session->connection_dialect),
+ encryption,
+ encryption_degree,
+ signing,
+ signing_degree);
+ } else {
+ result = traverse_sessionid_json(state,
+ session,
+ uid_str,
+ gid_str,
+ encryption,
+ encryption_degree,
+ signing,
+ signing_degree,
+ session_dialect_str(session->connection_dialect));
+ }
+
+ TALLOC_FREE(machine_hostname);
+ TALLOC_FREE(tmp_ctx);
+
+ return result;
+}
+
+
+static bool print_notify_rec_stdout(struct traverse_state *state,
+ const char *path,
+ char *server_id_str,
+ unsigned filter,
+ unsigned subdir_filter)
+{
+ d_printf("%s\\%s\\%x\\%x\n", path, server_id_str,
+ filter, subdir_filter);
+
+ return true;
+}
+
+static int prepare_notify(struct traverse_state *state)
+{
+ if (!state->json_output) {
+ /* don't print header line */
+ } else {
+ add_section_to_json(state, "notifies");
+ }
+ return 0;
+}
+
+static bool print_notify_rec(const char *path, struct server_id server,
+ const struct notify_instance *instance,
+ void *private_data)
+{
+ struct server_id_buf idbuf;
+ struct traverse_state *state = (struct traverse_state *)private_data;
+ bool result;
+
+ if (!state->json_output) {
+ result = print_notify_rec_stdout(state,
+ path,
+ server_id_str_buf(server, &idbuf),
+ (unsigned)instance->filter,
+ (unsigned)instance->subdir_filter);
+
+ } else {
+ result = print_notify_rec_json(state,
+ instance,
+ server,
+ path);
+ }
+
+ return result;
+}
+
+enum {
+ OPT_RESOLVE_UIDS = 1000,
+};
+
+int main(int argc, const char *argv[])
+{
+ int c;
+ int profile_only = 0;
+ bool show_processes, show_locks, show_shares;
+ bool show_notify = false;
+ poptContext pc = NULL;
+ struct traverse_state state = {0};
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "processes",
+ .shortName = 'p',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'p',
+ .descrip = "Show processes only",
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'v',
+ .descrip = "Be verbose",
+ },
+ {
+ .longName = "locks",
+ .shortName = 'L',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'L',
+ .descrip = "Show locks only",
+ },
+ {
+ .longName = "shares",
+ .shortName = 'S',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'S',
+ .descrip = "Show shares only",
+ },
+ {
+ .longName = "notify",
+ .shortName = 'N',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'N',
+ .descrip = "Show notifies",
+ },
+ {
+ .longName = "user",
+ .shortName = 'u',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &username,
+ .val = 'u',
+ .descrip = "Switch to user",
+ },
+ {
+ .longName = "brief",
+ .shortName = 'b',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'b',
+ .descrip = "Be brief",
+ },
+ {
+ .longName = "profile",
+ .shortName = 'P',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'P',
+ .descrip = "Do profiling",
+ },
+ {
+ .longName = "profile-rates",
+ .shortName = 'R',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'R',
+ .descrip = "Show call rates",
+ },
+ {
+ .longName = "byterange",
+ .shortName = 'B',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'B',
+ .descrip = "Include byte range locks"
+ },
+ {
+ .longName = "numeric",
+ .shortName = 'n',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'n',
+ .descrip = "Numeric uid/gid"
+ },
+ {
+ .longName = "json",
+ .shortName = 'j',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'j',
+ .descrip = "JSON output"
+ },
+ {
+ .longName = "fast",
+ .shortName = 'f',
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = 'f',
+ .descrip = "Skip checks if processes still exist"
+ },
+ {
+ .longName = "resolve-uids",
+ .shortName = 0,
+ .argInfo = POPT_ARG_NONE,
+ .arg = NULL,
+ .val = OPT_RESOLVE_UIDS,
+ .descrip = "Try to resolve UIDs to usernames"
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ int ret = 0;
+ struct messaging_context *msg_ctx = NULL;
+ char *db_path;
+ bool ok;
+ struct loadparm_context *lp_ctx = NULL;
+
+ state.first = true;
+ state.json_output = false;
+ state.resolve_uids = false;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+ lpcfg_set_cmdline(lp_ctx, "log level", "0");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ while ((c = poptGetNextOpt(pc)) != -1) {
+ switch (c) {
+ case 'p':
+ processes_only = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'L':
+ locks_only = true;
+ break;
+ case 'S':
+ shares_only = true;
+ break;
+ case 'N':
+ show_notify = true;
+ break;
+ case 'b':
+ brief = true;
+ break;
+ case 'u':
+ Ucrit_addUid(nametouid(poptGetOptArg(pc)));
+ break;
+ case 'P':
+ case 'R':
+ profile_only = c;
+ break;
+ case 'B':
+ show_brl = true;
+ break;
+ case 'n':
+ numeric_only = true;
+ break;
+ case 'j':
+ state.json_output = true;
+ break;
+ case 'f':
+ do_checks = false;
+ break;
+ case OPT_RESOLVE_UIDS:
+ state.resolve_uids = true;
+ break;
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(c));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ sec_init();
+
+#ifdef HAVE_JANSSON
+ state.root_json = json_new_object();
+ if (!json_is_invalid(&state.root_json)) {
+ add_general_information_to_json(&state);
+ }
+#else /* HAVE_JANSSON */
+ if (state.json_output) {
+ fprintf(stderr, "JSON support not available, please install lib Jansson\n");
+ goto done;
+ }
+#endif /* HAVE_JANSSON */
+
+ if (getuid() != geteuid()) {
+ fprintf(stderr, "smbstatus should not be run setuid\n");
+ ret = 1;
+ goto done;
+ }
+
+ if (getuid() != 0) {
+ fprintf(stderr, "smbstatus only works as root!\n");
+ ret = 1;
+ goto done;
+ }
+
+ /* setup the flags based on the possible combincations */
+
+ show_processes = !(shares_only || locks_only || profile_only) || processes_only;
+ show_locks = !(shares_only || processes_only || profile_only) || locks_only;
+ show_shares = !(processes_only || locks_only || profile_only) || shares_only;
+
+ if ( username )
+ Ucrit_addUid( nametouid(username) );
+
+ if (verbose && !state.json_output) {
+ d_printf("using configfile = %s\n", get_dyn_CONFIGFILE());
+ }
+
+ msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE());
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "Could not initialize messaging, not root?\n");
+ ret = -1;
+ goto done;
+ }
+
+ switch (profile_only) {
+ case 'P':
+ /* Dump profile data */
+ ok = status_profile_dump(verbose, &state);
+ ret = ok ? 0 : 1;
+ goto done;
+ case 'R':
+ /* Continuously display rate-converted data */
+ if (!state.json_output) {
+ ok = status_profile_rates(verbose);
+ ret = ok ? 0 : 1;
+ } else {
+ fprintf(stderr, "Call rates not available in a json output.\n");
+ ret = 1;
+ }
+ goto done;
+ default:
+ break;
+ }
+
+ if ( show_processes ) {
+ prepare_sessionid(&state);
+ sessionid_traverse_read(traverse_sessionid, &state);
+
+ if (processes_only) {
+ goto done;
+ }
+ }
+
+ if ( show_shares ) {
+ if (brief) {
+ goto done;
+ }
+ prepare_connections(&state);
+ connections_forall_read(traverse_connections, &state);
+
+ if (!state.json_output) {
+ d_printf("\n");
+ }
+
+ if ( shares_only ) {
+ goto done;
+ }
+ }
+
+ if ( show_locks ) {
+ int result;
+ struct db_context *db;
+
+ db_path = lock_path(talloc_tos(), "locking.tdb");
+ if (db_path == NULL) {
+ fprintf(stderr, "Out of memory - exiting\n");
+ ret = -1;
+ goto done;
+ }
+
+ db = db_open(NULL, db_path, 0,
+ TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDONLY, 0,
+ DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE);
+
+ if (!db) {
+ fprintf(stderr, "%s not initialised\n", db_path);
+ fprintf(stderr, "This is normal if an SMB client has never "
+ "connected to your server.\n");
+ TALLOC_FREE(db_path);
+ ret = 0;
+ goto done;
+ } else {
+ TALLOC_FREE(db);
+ TALLOC_FREE(db_path);
+ }
+
+ if (!locking_init_readonly()) {
+ fprintf(stderr, "Can't initialise locking module - exiting\n");
+ ret = 1;
+ goto done;
+ }
+
+ prepare_share_mode(&state);
+ result = share_entry_forall(print_share_mode, &state);
+
+ if (result == 0 && !state.json_output) {
+ fprintf(stderr, "No locked files\n");
+ } else if (result < 0 && !state.json_output) {
+ fprintf(stderr, "locked file list truncated\n");
+ }
+
+ if (!state.json_output) {
+ d_printf("\n");
+ }
+
+ if (show_brl) {
+ prepare_brl(&state);
+ brl_forall(print_brl, &state);
+ }
+
+ locking_end();
+ }
+
+ if (show_notify) {
+ prepare_notify(&state);
+ notify_walk(msg_ctx, print_notify_rec, &state);
+ }
+
+done:
+ cmdline_messaging_context_free();
+ poptFreeContext(pc);
+#ifdef HAVE_JANSSON
+ if (state.json_output) {
+ d_printf("%s\n", json_to_string(frame, &state.root_json));
+ }
+ json_free(&state.root_json);
+#endif /* HAVE_JANSSON */
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/status.h b/source3/utils/status.h
new file mode 100644
index 0000000..c08aba4
--- /dev/null
+++ b/source3/utils/status.h
@@ -0,0 +1,44 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * State struct
+ * Copyright (C) Jule Anger 2022
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_JANSSON
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#include "auth/common_auth.h"
+#endif /* HAVE_JANSSON */
+
+#ifndef STATUS_H
+#define STATUS_H
+
+struct traverse_state {
+ bool json_output;
+ bool first;
+ bool resolve_uids;
+#ifdef HAVE_JANSSON
+ struct json_object root_json;
+#endif /* HAVE_JANSSON */
+};
+
+enum crypto_degree {
+ CRYPTO_DEGREE_NONE,
+ CRYPTO_DEGREE_PARTIAL,
+ CRYPTO_DEGREE_FULL
+};
+
+#endif
diff --git a/source3/utils/status_json.c b/source3/utils/status_json.c
new file mode 100644
index 0000000..ee24a3b
--- /dev/null
+++ b/source3/utils/status_json.c
@@ -0,0 +1,1386 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Json output
+ * Copyright (C) Jule Anger 2022
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbprofile.h"
+#include "lib/util/time_basic.h"
+#include "conn_tdb.h"
+#include "session.h"
+#include "librpc/gen_ndr/smbXsrv.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "status_json.h"
+#include "../libcli/security/security.h"
+#include "status.h"
+#include "lib/util/server_id.h"
+#include "lib/util/string_wrappers.h"
+
+#include <jansson.h>
+#include "audit_logging.h" /* various JSON helpers */
+#include "auth/common_auth.h"
+
+int add_general_information_to_json(struct traverse_state *state)
+{
+ int result;
+
+ result = json_add_timestamp(&state->root_json);
+ if (result < 0) {
+ return -1;
+ }
+
+ result = json_add_string(&state->root_json, "version", samba_version_string());
+ if (result < 0) {
+ return -1;
+ }
+
+ result = json_add_string(&state->root_json, "smb_conf", get_dyn_CONFIGFILE());
+ if (result < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_server_id_to_json(struct json_object *parent_json,
+ const struct server_id server_id)
+{
+ struct json_object sub_json;
+ char *pid_str = NULL;
+ char *task_id_str = NULL;
+ char *vnn_str = NULL;
+ char *unique_id_str = NULL;
+ int result;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ pid_str = talloc_asprintf(
+ tmp_ctx, "%lu", (unsigned long)server_id.pid);
+ result = json_add_string(&sub_json, "pid", pid_str);
+ if (result < 0) {
+ goto failure;
+ }
+ task_id_str = talloc_asprintf(tmp_ctx, "%u", server_id.task_id);
+ result = json_add_string(&sub_json, "task_id", task_id_str);
+ if (result < 0) {
+ goto failure;
+ }
+ vnn_str = talloc_asprintf(tmp_ctx, "%u", server_id.vnn);
+ result = json_add_string(&sub_json, "vnn", vnn_str);
+ if (result < 0) {
+ goto failure;
+ }
+ unique_id_str = talloc_asprintf(
+ tmp_ctx, "%"PRIu64, server_id.unique_id);
+ result = json_add_string(&sub_json, "unique_id", unique_id_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, "server_id", &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+struct mask2txt {
+ uint32_t mask;
+ const char *string_desc;
+};
+
+/*
+ * Convert a mask of some sort (access, oplock, leases),
+ * to key/value pairs in a JSON object.
+ */
+static int map_mask_to_json(struct json_object *root_json,
+ uint32_t tomap,
+ const struct mask2txt *table)
+{
+ const struct mask2txt *a = NULL;
+ int result = 0;
+
+ for (a = table; a->string_desc != 0; a++) {
+ result = json_add_bool(root_json, a->string_desc,
+ (tomap & a->mask) ? true : false);
+
+ if (result < 0) {
+ return result;
+ }
+ tomap &= ~a->mask;
+ }
+
+ /* Assert we know about all requested "tomap" values */
+ SMB_ASSERT(tomap == 0);
+
+ return 0;
+}
+
+static const struct mask2txt access_mask[] = {
+ {FILE_READ_DATA, "READ_DATA"},
+ {FILE_WRITE_DATA, "WRITE_DATA"},
+ {FILE_APPEND_DATA, "APPEND_DATA"},
+ {FILE_READ_EA, "READ_EA"},
+ {FILE_WRITE_EA, "WRITE_EA"},
+ {FILE_EXECUTE, "EXECUTE"},
+ {FILE_READ_ATTRIBUTES, "READ_ATTRIBUTES"},
+ {FILE_WRITE_ATTRIBUTES, "WRITE_ATTRIBUTES"},
+ {FILE_DELETE_CHILD, "DELETE_CHILD"},
+ {SEC_STD_DELETE, "DELETE"},
+ {SEC_STD_READ_CONTROL, "READ_CONTROL"},
+ {SEC_STD_WRITE_DAC, "WRITE_DAC"},
+ {SEC_STD_SYNCHRONIZE, "SYNCHRONIZE"},
+ {SEC_FLAG_SYSTEM_SECURITY, "ACCESS_SYSTEM_SECURITY"},
+ {0, NULL}
+};
+
+static const struct mask2txt oplock_mask[] = {
+ {EXCLUSIVE_OPLOCK, "EXCLUSIVE"},
+ {BATCH_OPLOCK, "BATCH"},
+ {LEVEL_II_OPLOCK, "LEVEL_II"},
+ {LEASE_OPLOCK, "LEASE"},
+ {0, NULL}
+};
+
+static const struct mask2txt sharemode_mask[] = {
+ {FILE_SHARE_READ, "READ"},
+ {FILE_SHARE_WRITE, "WRITE"},
+ {FILE_SHARE_DELETE, "DELETE"},
+ {0, NULL}
+};
+
+static const struct mask2txt lease_mask[] = {
+ {SMB2_LEASE_READ, "READ"},
+ {SMB2_LEASE_WRITE, "WRITE"},
+ {SMB2_LEASE_HANDLE, "HANDLE"},
+ {0, NULL}
+};
+
+int add_profile_item_to_json(struct traverse_state *state,
+ const char *section,
+ const char *subsection,
+ const char *key,
+ uintmax_t value)
+{
+ struct json_object section_json = {
+ .valid = false,
+ };
+ struct json_object subsection_json = {
+ .valid = false,
+ };
+ int result = 0;
+
+ section_json = json_get_object(&state->root_json, section);
+ if (json_is_invalid(&section_json)) {
+ goto failure;
+ }
+ subsection_json = json_get_object(&section_json, subsection);
+ if (json_is_invalid(&subsection_json)) {
+ goto failure;
+ }
+
+ result = json_add_int(&subsection_json, key, value);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_update_object(&section_json, subsection, &subsection_json);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_update_object(&state->root_json, section, &section_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ return 0;
+failure:
+ json_free(&section_json);
+ json_free(&subsection_json);
+ return -1;
+}
+
+int add_section_to_json(struct traverse_state *state,
+ const char *key)
+{
+ struct json_object empty_json;
+ int result;
+
+ empty_json = json_new_object();
+ if (json_is_invalid(&empty_json)) {
+ return -1;
+ }
+
+ result = json_add_object(&state->root_json, key, &empty_json);
+ if (result < 0) {
+ return -1;
+ }
+
+ return result;
+}
+
+static int add_crypto_to_json(struct json_object *parent_json,
+ const char *key,
+ const char *cipher,
+ enum crypto_degree degree)
+{
+ struct json_object sub_json;
+ const char *degree_str;
+ int result;
+
+ if (degree == CRYPTO_DEGREE_NONE) {
+ degree_str = "none";
+ } else if (degree == CRYPTO_DEGREE_PARTIAL) {
+ degree_str = "partial";
+ } else {
+ degree_str = "full";
+ }
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ result = json_add_string(&sub_json, "cipher", cipher);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "degree", degree_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_object(parent_json, key, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ return 0;
+failure:
+ json_free(&sub_json);
+ return -1;
+}
+
+static int add_channel_to_json(struct json_object *parent_json,
+ const struct smbXsrv_channel_global0 *channel)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct json_object sub_json;
+ char *id_str = NULL;
+ struct timeval tv;
+ struct timeval_buf tv_buf;
+ char *time_str = NULL;
+ int result;
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ id_str = talloc_asprintf(frame, "%"PRIu64"", channel->channel_id);
+ if (id_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "channel_id", id_str);
+ if (result < 0) {
+ goto failure;
+ }
+ nttime_to_timeval(&tv, channel->creation_time);
+ time_str = timeval_str_buf(&tv, true, true, &tv_buf);
+ if (time_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "creation_time", time_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "local_address", channel->local_address);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "remote_address", channel->remote_address);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, id_str, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+failure:
+ json_free(&sub_json);
+ TALLOC_FREE(frame);
+ return -1;
+}
+
+static int add_channels_to_json(struct json_object *parent_json,
+ const struct smbXsrv_session_global0 *global)
+{
+ struct json_object sub_json;
+ uint32_t i;
+ int result;
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ for (i = 0; i < global->num_channels; i++) {
+ const struct smbXsrv_channel_global0 *c = &global->channels[i];
+
+ result = add_channel_to_json(&sub_json, c);
+ if (result < 0) {
+ goto failure;
+ }
+ }
+
+ result = json_add_object(parent_json, "channels", &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ return 0;
+failure:
+ json_free(&sub_json);
+ return -1;
+}
+
+int traverse_connections_json(struct traverse_state *state,
+ const struct connections_data *crec,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree)
+{
+ struct json_object sub_json;
+ struct json_object connections_json;
+ struct timeval tv;
+ struct timeval_buf tv_buf;
+ char *time = NULL;
+ int result = 0;
+ char *sess_id_str = NULL;
+ char *tcon_id_str = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+ connections_json = json_get_object(&state->root_json, "tcons");
+ if (json_is_invalid(&connections_json)) {
+ goto failure;
+ }
+
+ result = json_add_string(&sub_json, "service", crec->servicename);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_server_id_to_json(&sub_json, crec->pid);
+ if (result < 0) {
+ goto failure;
+ }
+ tcon_id_str = talloc_asprintf(tmp_ctx, "%u", crec->cnum);
+ if (tcon_id_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "tcon_id", tcon_id_str);
+ if (result < 0) {
+ goto failure;
+ }
+ sess_id_str = talloc_asprintf(tmp_ctx, "%u", crec->sess_id);
+ if (sess_id_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "session_id", sess_id_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "machine", crec->machine);
+ if (result < 0) {
+ goto failure;
+ }
+ nttime_to_timeval(&tv, crec->start);
+ time = timeval_str_buf(&tv, true, true, &tv_buf);
+ if (time == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "connected_at", time);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_crypto_to_json(&sub_json, "encryption",
+ encryption_cipher, encryption_degree);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_crypto_to_json(&sub_json, "signing",
+ signing_cipher, signing_degree);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(&connections_json, tcon_id_str, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_update_object(&state->root_json, "tcons", &connections_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+int traverse_sessionid_json(struct traverse_state *state,
+ struct sessionid *session,
+ char *uid_str,
+ char *gid_str,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree,
+ const char *connection_dialect)
+{
+ struct json_object sub_json;
+ struct json_object session_json;
+ int result = 0;
+ char *id_str = NULL;
+ struct timeval tv;
+ struct timeval_buf tv_buf;
+ char *time_str = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ session_json = json_get_object(&state->root_json, "sessions");
+ if (json_is_invalid(&session_json)) {
+ goto failure;
+ }
+
+ id_str = talloc_asprintf(tmp_ctx, "%u", session->id_num);
+ result = json_add_string(&sub_json, "session_id", id_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_server_id_to_json(&sub_json, session->pid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&sub_json, "uid", session->uid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&sub_json, "gid", session->gid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "username", uid_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "groupname", gid_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ nttime_to_timeval(&tv, session->global->creation_time);
+ time_str = timeval_str_buf(&tv, true, true, &tv_buf);
+ if (time_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "creation_time", time_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ nttime_to_timeval(&tv, session->global->expiration_time);
+ time_str = timeval_str_buf(&tv, true, true, &tv_buf);
+ if (time_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "expiration_time", time_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ nttime_to_timeval(&tv, session->global->auth_time);
+ time_str = timeval_str_buf(&tv, true, true, &tv_buf);
+ if (time_str == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "auth_time", time_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_string(&sub_json, "remote_machine", session->remote_machine);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "hostname", session->hostname);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "session_dialect", connection_dialect);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_guid(&sub_json,
+ "client_guid",
+ &session->global->client_guid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_crypto_to_json(&sub_json, "encryption",
+ encryption_cipher, encryption_degree);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_crypto_to_json(&sub_json, "signing",
+ signing_cipher, signing_degree);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = add_channels_to_json(&sub_json, session->global);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(&session_json, id_str, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_update_object(&state->root_json, "sessions", &session_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_access_mode_to_json(struct json_object *parent_json,
+ int access_int)
+{
+ struct json_object access_json;
+ char *access_hex = NULL;
+ const char *access_str = NULL;
+ int result;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ access_json = json_new_object();
+ if (json_is_invalid(&access_json)) {
+ goto failure;
+ }
+
+ access_hex = talloc_asprintf(tmp_ctx, "0x%08x", access_int);
+ result = json_add_string(&access_json, "hex", access_hex);
+ if (result < 0) {
+ goto failure;
+ }
+ result = map_mask_to_json(&access_json, access_int, access_mask);
+ if (result < 0) {
+ goto failure;
+ }
+
+ access_str = talloc_asprintf(tmp_ctx, "%s%s",
+ (access_int & FILE_READ_DATA)?"R":"",
+ (access_int & (FILE_WRITE_DATA|FILE_APPEND_DATA))?"W":"");
+ result = json_add_string(&access_json, "text", access_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, "access_mask", &access_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&access_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_caching_to_json(struct json_object *parent_json,
+ int op_type,
+ int lease_type)
+{
+ struct json_object caching_json;
+ char *hex = NULL;
+ char *caching_text = NULL;
+ int caching_type = 0;
+ int result;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ caching_json = json_new_object();
+ if (json_is_invalid(&caching_json)) {
+ goto failure;
+ }
+
+ if (op_type & LEASE_OPLOCK) {
+ caching_type = lease_type;
+ } else {
+ if (op_type & LEVEL_II_OPLOCK) {
+ caching_type = SMB2_LEASE_READ;
+ } else if (op_type & EXCLUSIVE_OPLOCK) {
+ caching_type = SMB2_LEASE_READ + SMB2_LEASE_WRITE;
+ } else if (op_type & BATCH_OPLOCK) {
+ caching_type = SMB2_LEASE_READ + SMB2_LEASE_WRITE + SMB2_LEASE_HANDLE;
+ }
+ }
+ result = map_mask_to_json(&caching_json, caching_type, lease_mask);
+ if (result < 0) {
+ goto failure;
+ }
+
+ hex = talloc_asprintf(tmp_ctx, "0x%08x", caching_type);
+ if (hex == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&caching_json, "hex", hex);
+ if (result < 0) {
+ goto failure;
+ }
+
+ caching_text = talloc_asprintf(tmp_ctx, "%s%s%s",
+ (caching_type & SMB2_LEASE_READ)?"R":"",
+ (caching_type & SMB2_LEASE_WRITE)?"W":"",
+ (caching_type & SMB2_LEASE_HANDLE)?"H":"");
+ if (caching_text == NULL) {
+ return -1;
+ }
+
+ result = json_add_string(&caching_json, "text", caching_text);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, "caching", &caching_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&caching_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_oplock_to_json(struct json_object *parent_json,
+ uint16_t op_type,
+ const char *op_str)
+{
+ struct json_object oplock_json;
+ int result;
+
+ oplock_json = json_new_object();
+ if (json_is_invalid(&oplock_json)) {
+ goto failure;
+ }
+
+ if (op_type != 0) {
+ result = map_mask_to_json(&oplock_json, op_type, oplock_mask);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&oplock_json, "text", op_str);
+ if (result < 0) {
+ goto failure;
+ }
+ }
+
+ result = json_add_object(parent_json, "oplock", &oplock_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ return 0;
+failure:
+ json_free(&oplock_json);
+ return -1;
+}
+
+static int lease_key_to_str(struct smb2_lease_key lease_key,
+ char *lease_str)
+{
+ uint8_t _buf[16] = {0};
+ DATA_BLOB blob = data_blob_const(_buf, sizeof(_buf));
+ struct GUID guid;
+ NTSTATUS status;
+ char *tmp = NULL;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ PUSH_LE_U64(_buf, 0, lease_key.data[0]);
+ PUSH_LE_U64(_buf, 8, lease_key.data[1]);
+
+ status = GUID_from_ndr_blob(&blob, &guid);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failure;
+ }
+ tmp = GUID_string(tmp_ctx, &guid);
+ if (tmp == NULL) {
+ goto failure;
+ }
+ fstrcpy(lease_str, tmp);
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_lease_to_json(struct json_object *parent_json,
+ int lease_type,
+ struct smb2_lease_key lease_key,
+ bool add_lease)
+{
+ struct json_object lease_json;
+ char *lease_hex = NULL;
+ char *lease_text = NULL;
+ fstring lease_key_str;
+ int result;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ lease_json = json_new_object();
+ if (json_is_invalid(&lease_json)) {
+ goto failure;
+ }
+
+
+ if (add_lease) {
+ result = lease_key_to_str(lease_key, lease_key_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&lease_json, "lease_key", lease_key_str);
+ if (result < 0) {
+ goto failure;
+ }
+ lease_hex = talloc_asprintf(tmp_ctx, "0x%08x", lease_type);
+ result = json_add_string(&lease_json, "hex", lease_hex);
+ if (result < 0) {
+ goto failure;
+ }
+ if (lease_type > (SMB2_LEASE_WRITE + SMB2_LEASE_HANDLE + SMB2_LEASE_READ)) {
+ result = json_add_bool(&lease_json, "UNKNOWN", true);
+ if (result < 0) {
+ goto failure;
+ }
+ } else {
+ result = map_mask_to_json(&lease_json, lease_type, lease_mask);
+ if (result < 0) {
+ goto failure;
+ }
+ }
+ lease_text = talloc_asprintf(tmp_ctx, "%s%s%s",
+ (lease_type & SMB2_LEASE_READ)?"R":"",
+ (lease_type & SMB2_LEASE_WRITE)?"W":"",
+ (lease_type & SMB2_LEASE_HANDLE)?"H":"");
+
+ result = json_add_string(&lease_json, "text", lease_text);
+ if (result < 0) {
+ goto failure;
+ }
+ }
+
+ result = json_add_object(parent_json, "lease", &lease_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&lease_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_sharemode_to_json(struct json_object *parent_json,
+ int sharemode)
+{
+ struct json_object sharemode_json;
+ char *hex = NULL;
+ char *text = NULL;
+ int result;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ sharemode_json = json_new_object();
+ if (json_is_invalid(&sharemode_json)) {
+ goto failure;
+ }
+
+ hex = talloc_asprintf(tmp_ctx, "0x%08x", sharemode);
+ if (hex == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sharemode_json, "hex", hex);
+ if (result < 0) {
+ goto failure;
+ }
+ result = map_mask_to_json(&sharemode_json, sharemode, sharemode_mask);
+ if (result < 0) {
+ goto failure;
+ }
+
+ text = talloc_asprintf(tmp_ctx, "%s%s%s",
+ (sharemode & FILE_SHARE_READ)?"R":"",
+ (sharemode & FILE_SHARE_WRITE)?"W":"",
+ (sharemode & FILE_SHARE_DELETE)?"D":"");
+ if (text == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sharemode_json, "text", text);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, "sharemode", &sharemode_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&sharemode_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_open_to_json(struct json_object *parent_json,
+ const struct share_mode_entry *e,
+ bool resolve_uids,
+ const char *op_str,
+ uint32_t lease_type,
+ const char *uid_str)
+{
+ struct json_object sub_json = {
+ .valid = false,
+ };
+ struct json_object opens_json = {
+ .valid = false,
+ };
+ struct timeval_buf tv_buf;
+ int result = 0;
+ char *timestr;
+ bool add_lease = false;
+ char *key = NULL;
+ char *share_file_id = NULL;
+ char *pid = NULL;
+ struct server_id_buf tmp;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ opens_json = json_get_object(parent_json, "opens");
+ if (json_is_invalid(&opens_json)) {
+ goto failure;
+ }
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+
+ result = add_server_id_to_json(&sub_json, e->pid);
+ if (result < 0) {
+ goto failure;
+ }
+ if (resolve_uids) {
+ result = json_add_string(&sub_json, "username", uid_str);
+ if (result < 0) {
+ goto failure;
+ }
+ }
+ result = json_add_int(&sub_json, "uid", e->uid);
+ if (result < 0) {
+ goto failure;
+ }
+ share_file_id = talloc_asprintf(tmp_ctx, "%"PRIu64, e->share_file_id);
+ result = json_add_string(&sub_json, "share_file_id", share_file_id);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_sharemode_to_json(&sub_json, e->share_access);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_access_mode_to_json(&sub_json, e->access_mask);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_caching_to_json(&sub_json, e->op_type, lease_type);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_oplock_to_json(&sub_json, e->op_type, op_str);
+ if (result < 0) {
+ goto failure;
+ }
+ add_lease = e->op_type & LEASE_OPLOCK;
+ result = add_lease_to_json(&sub_json, lease_type, e->lease_key, add_lease);
+ if (result < 0) {
+ goto failure;
+ }
+
+ timestr = timeval_str_buf(&e->time, true, true, &tv_buf);
+ if (timestr == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "opened_at", timestr);
+ if (result < 0) {
+ goto failure;
+ }
+
+ pid = server_id_str_buf(e->pid, &tmp);
+ key = talloc_asprintf(tmp_ctx, "%s/%"PRIu64, pid, e->share_file_id);
+ result = json_add_object(&opens_json, key, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_update_object(parent_json, "opens", &opens_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&opens_json);
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_fileid_to_json(struct json_object *parent_json,
+ struct file_id fid)
+{
+ struct json_object fid_json;
+ int result;
+
+ fid_json = json_new_object();
+ if (json_is_invalid(&fid_json)) {
+ goto failure;
+ }
+
+ result = json_add_int(&fid_json, "devid", fid.devid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&fid_json, "inode", fid.inode);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&fid_json, "extid", fid.extid);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(parent_json, "fileid", &fid_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ return 0;
+failure:
+ json_free(&fid_json);
+ return -1;
+}
+
+int print_share_mode_json(struct traverse_state *state,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ struct file_id fid,
+ const char *uid_str,
+ const char *op_str,
+ uint32_t lease_type,
+ const char *filename)
+{
+ struct json_object locks_json = {
+ .valid = false,
+ };
+ struct json_object file_json = {
+ .valid = false,
+ };
+ char *key = NULL;
+ int result = 0;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ if (d->servicepath[strlen(d->servicepath)-1] == '/') {
+ key = talloc_asprintf(tmp_ctx, "%s%s", d->servicepath, filename);
+ } else {
+ key = talloc_asprintf(tmp_ctx, "%s/%s", d->servicepath, filename);
+ }
+
+ locks_json = json_get_object(&state->root_json, "open_files");
+ if (json_is_invalid(&locks_json)) {
+ goto failure;
+ }
+ file_json = json_get_object(&locks_json, key);
+ if (json_is_invalid(&file_json)) {
+ goto failure;
+ }
+
+ result = json_add_string(&file_json, "service_path", d->servicepath);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&file_json, "filename", filename);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_fileid_to_json(&file_json, fid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&file_json, "num_pending_deletes", d->num_delete_tokens);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = add_open_to_json(&file_json,
+ e,
+ state->resolve_uids,
+ op_str,
+ lease_type,
+ uid_str);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_update_object(&locks_json, key, &file_json);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_update_object(&state->root_json, "open_files", &locks_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&file_json);
+ json_free(&locks_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+static int add_lock_to_json(struct json_object *parent_json,
+ struct server_id server_id,
+ const char *type,
+ enum brl_flavour flavour,
+ intmax_t start,
+ intmax_t size)
+{
+ struct json_object sub_json = {
+ .valid = false,
+ };
+ struct json_object locks_json = {
+ .valid = false,
+ };
+ const char *flavour_str;
+ int result = 0;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ locks_json = json_get_array(parent_json, "locks");
+ if (json_is_invalid(&locks_json)) {
+ goto failure;
+ }
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ goto failure;
+ }
+
+ result = add_server_id_to_json(&sub_json, server_id);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "type", type);
+ if (result < 0) {
+ goto failure;
+ }
+ flavour_str = talloc_asprintf(tmp_ctx, "%s%s",
+ (flavour == WINDOWS_LOCK)?"Windows":"",
+ (flavour == POSIX_LOCK)?"Posix":"");
+ result = json_add_string(&sub_json, "flavour", flavour_str);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&sub_json, "start", start);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_int(&sub_json, "size", size);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(&locks_json, NULL, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_update_object(parent_json, "locks", &locks_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&locks_json);
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+int print_brl_json(struct traverse_state *state,
+ const struct server_id server_id,
+ struct file_id fid,
+ const char *type,
+ enum brl_flavour flavour,
+ intmax_t start,
+ intmax_t size,
+ const char *sharepath,
+ const char *filename)
+{
+ struct json_object file_json = {
+ .valid = false,
+ };
+ struct json_object brl_json = {
+ .valid = false,
+ };
+ int result = 0;
+ char *key;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ if (sharepath[strlen(sharepath)-1] == '/') {
+ key = talloc_asprintf(tmp_ctx, "%s%s", sharepath, filename);
+ } else {
+ key = talloc_asprintf(tmp_ctx, "%s/%s", sharepath, filename);
+ }
+ if (key == NULL) {
+ goto failure;
+ }
+
+ brl_json = json_get_object(&state->root_json, "byte_range_locks");
+ if (json_is_invalid(&brl_json)) {
+ goto failure;
+ }
+ file_json = json_get_object(&brl_json, key);
+ if (json_is_invalid(&file_json)) {
+ goto failure;
+ }
+
+ result = add_fileid_to_json(&file_json, fid);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&file_json, "file_name", filename);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&file_json, "share_path", sharepath);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_server_id_to_json(&file_json, server_id);
+ if (result < 0) {
+ goto failure;
+ }
+ result = add_lock_to_json(&file_json, server_id, type, flavour, start, size);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_add_object(&brl_json, key, &file_json);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_update_object(&state->root_json, "byte_range_locks", &brl_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return 0;
+failure:
+ json_free(&file_json);
+ json_free(&brl_json);
+ TALLOC_FREE(tmp_ctx);
+ return -1;
+}
+
+bool print_notify_rec_json(struct traverse_state *state,
+ const struct notify_instance *instance,
+ const struct server_id server_id,
+ const char *path)
+{
+ struct json_object sub_json;
+ struct json_object notify_json;
+ char *filter = NULL;
+ char *subdir_filter = NULL;
+ struct timeval_buf tv_buf;
+ struct timeval val;
+ char *time = NULL;
+ char *pid = NULL;
+ struct server_id_buf tmp;
+ int result = 0;
+
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+ if (tmp_ctx == NULL) {
+ return -1;
+ }
+
+ sub_json = json_new_object();
+ if (json_is_invalid(&sub_json)) {
+ return false;
+ }
+ notify_json = json_get_object(&state->root_json, "notifies");
+ if (json_is_invalid(&notify_json)) {
+ goto failure;
+ }
+
+ result = add_server_id_to_json(&sub_json, server_id);
+ if (result < 0) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "path", path);
+ if (result < 0) {
+ goto failure;
+ }
+ filter = talloc_asprintf(tmp_ctx, "%u", instance->filter);
+ if (filter == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "filter", filter);
+ if (result < 0) {
+ goto failure;
+ }
+ subdir_filter = talloc_asprintf(tmp_ctx, "%u", instance->subdir_filter);
+ if (subdir_filter == NULL) {
+ goto failure;
+ }
+ result = json_add_string(&sub_json, "subdir_filter", subdir_filter);
+ if (result < 0) {
+ goto failure;
+ }
+ val = convert_timespec_to_timeval(instance->creation_time);
+ time = timeval_str_buf(&val, true, true, &tv_buf);
+ result = json_add_string(&sub_json, "creation_time", time);
+ if (result < 0) {
+ goto failure;
+ }
+
+ pid = server_id_str_buf(server_id, &tmp);
+ result = json_add_object(&notify_json, pid, &sub_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ result = json_update_object(&state->root_json, "notifies", &notify_json);
+ if (result < 0) {
+ goto failure;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return true;
+failure:
+ json_free(&sub_json);
+ TALLOC_FREE(tmp_ctx);
+ return false;
+}
diff --git a/source3/utils/status_json.h b/source3/utils/status_json.h
new file mode 100644
index 0000000..ef5d181
--- /dev/null
+++ b/source3/utils/status_json.h
@@ -0,0 +1,77 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Json output
+ * Copyright (C) Jule Anger 2022
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "status.h"
+#include "smbd/notifyd/notifyd_db.h"
+
+#ifndef STATUS_JSON_H
+#define STATUS_JSON_H
+
+int add_section_to_json(struct traverse_state *state,
+ const char *key);
+
+int add_general_information_to_json(struct traverse_state *state);
+
+int add_profile_item_to_json(struct traverse_state *state,
+ const char *section,
+ const char *subsection,
+ const char *key,
+ uintmax_t value);
+
+int traverse_connections_json(struct traverse_state *state,
+ const struct connections_data *crec,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree);
+
+int traverse_sessionid_json(struct traverse_state *state,
+ struct sessionid *session,
+ char *uid_str,
+ char *gid_str,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree,
+ const char *connection_dialect);
+
+int print_share_mode_json(struct traverse_state *state,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ struct file_id fid,
+ const char *uid_str,
+ const char *op_str,
+ uint32_t lease_type,
+ const char *filename);
+
+int print_brl_json(struct traverse_state *state,
+ const struct server_id server_id,
+ struct file_id fid,
+ const char *type,
+ enum brl_flavour flavour,
+ intmax_t start,
+ intmax_t size,
+ const char *sharepath,
+ const char *filename);
+
+bool print_notify_rec_json(struct traverse_state *state,
+ const struct notify_instance *instance,
+ const struct server_id server_id,
+ const char *path);
+#endif
diff --git a/source3/utils/status_json_dummy.c b/source3/utils/status_json_dummy.c
new file mode 100644
index 0000000..3cd8531
--- /dev/null
+++ b/source3/utils/status_json_dummy.c
@@ -0,0 +1,101 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Json output
+ * Copyright (C) Jule Anger 2022
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbprofile.h"
+#include "../libcli/security/security.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "conn_tdb.h"
+#include "status_json.h"
+
+int add_section_to_json(struct traverse_state *state,
+ const char *key)
+{
+ return 0;
+}
+
+int add_general_information_to_json(struct traverse_state *state)
+{
+ return 0;
+}
+
+int add_profile_item_to_json(struct traverse_state *state,
+ const char *section,
+ const char *subsection,
+ const char *key,
+ uintmax_t value)
+{
+ return 0;
+}
+
+int traverse_connections_json(struct traverse_state *state,
+ const struct connections_data *crec,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree)
+{
+ return 0;
+}
+
+int traverse_sessionid_json(struct traverse_state *state,
+ struct sessionid *session,
+ char *uid_str,
+ char *gid_str,
+ const char *encryption_cipher,
+ enum crypto_degree encryption_degree,
+ const char *signing_cipher,
+ enum crypto_degree signing_degree,
+ const char *connection_dialect)
+{
+ return 0;
+}
+
+int print_share_mode_json(struct traverse_state *state,
+ const struct share_mode_data *d,
+ const struct share_mode_entry *e,
+ struct file_id fid,
+ const char *uid_str,
+ const char *op_str,
+ uint32_t lease_type,
+ const char *filename)
+{
+ return 0;
+}
+
+int print_brl_json(struct traverse_state *state,
+ const struct server_id server_id,
+ struct file_id fid,
+ const char *type,
+ enum brl_flavour flavour,
+ intmax_t start,
+ intmax_t size,
+ const char *sharepath,
+ const char *filename)
+{
+ return 0;
+}
+
+bool print_notify_rec_json(struct traverse_state *state,
+ const struct notify_instance *instance,
+ const struct server_id server_id,
+ const char *path)
+{
+ return 0;
+}
diff --git a/source3/utils/status_profile.c b/source3/utils/status_profile.c
new file mode 100644
index 0000000..6e0916e
--- /dev/null
+++ b/source3/utils/status_profile.c
@@ -0,0 +1,381 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * status reporting
+ * Copyright (C) Andrew Tridgell 1994-1998
+ * Copyright (C) James Peach 2005-2006
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbprofile.h"
+#include "status_profile.h"
+#include "conn_tdb.h"
+#include "librpc/gen_ndr/open_files.h"
+#include "status_json.h"
+
+static void profile_separator(const char * title,
+ struct traverse_state *state)
+{
+ char line[79 + 1];
+ char * end;
+
+ if (state->json_output) {
+ return;
+ }
+
+ snprintf(line, sizeof(line), "**** %s ", title);
+
+ for (end = line + strlen(line); end < &line[sizeof(line) -1]; ++end) {
+ *end = '*';
+ }
+
+ line[sizeof(line) - 1] = '\0';
+ d_printf("%s\n", line);
+}
+
+/*******************************************************************
+ dump the elements of the profile structure
+ ******************************************************************/
+bool status_profile_dump(bool verbose,
+ struct traverse_state *state)
+{
+ struct profile_stats stats = {};
+ const char* latest_section = NULL;
+
+ if (!profile_setup(NULL, True)) {
+ fprintf(stderr,"Failed to initialise profile memory\n");
+ return False;
+ }
+
+ smbprofile_collect(&stats);
+
+#define __PRINT_FIELD_LINE(name, _stats, field) do { \
+ uintmax_t val = (uintmax_t)stats.values._stats.field; \
+ if (!state->json_output) { \
+ d_printf("%-59s%20ju\n", \
+ name "_" #field ":", \
+ val); \
+ } else { \
+ add_profile_item_to_json(state, latest_section, name, #field, val); \
+ } \
+} while(0);
+#define SMBPROFILE_STATS_START
+#define SMBPROFILE_STATS_SECTION_START(name, display) do { \
+ latest_section = display; \
+ profile_separator(display, state);\
+} while(0);
+#define SMBPROFILE_STATS_COUNT(name) do { \
+ __PRINT_FIELD_LINE(#name, name##_stats, count); \
+} while(0);
+#define SMBPROFILE_STATS_TIME(name) do { \
+ __PRINT_FIELD_LINE(#name, name##_stats, time); \
+} while(0);
+#define SMBPROFILE_STATS_BASIC(name) do { \
+ __PRINT_FIELD_LINE(#name, name##_stats, count); \
+ __PRINT_FIELD_LINE(#name, name##_stats, time); \
+} while(0);
+#define SMBPROFILE_STATS_BYTES(name) do { \
+ __PRINT_FIELD_LINE(#name, name##_stats, count); \
+ __PRINT_FIELD_LINE(#name, name##_stats, time); \
+ __PRINT_FIELD_LINE(#name, name##_stats, idle); \
+ __PRINT_FIELD_LINE(#name, name##_stats, bytes); \
+} while(0);
+#define SMBPROFILE_STATS_IOBYTES(name) do { \
+ __PRINT_FIELD_LINE(#name, name##_stats, count); \
+ __PRINT_FIELD_LINE(#name, name##_stats, time); \
+ __PRINT_FIELD_LINE(#name, name##_stats, idle); \
+ __PRINT_FIELD_LINE(#name, name##_stats, inbytes); \
+ __PRINT_FIELD_LINE(#name, name##_stats, outbytes); \
+} while(0);
+#define SMBPROFILE_STATS_SECTION_END
+#define SMBPROFILE_STATS_END
+ SMBPROFILE_STATS_ALL_SECTIONS
+#undef __PRINT_FIELD_LINE
+#undef SMBPROFILE_STATS_START
+#undef SMBPROFILE_STATS_SECTION_START
+#undef SMBPROFILE_STATS_COUNT
+#undef SMBPROFILE_STATS_TIME
+#undef SMBPROFILE_STATS_BASIC
+#undef SMBPROFILE_STATS_BYTES
+#undef SMBPROFILE_STATS_IOBYTES
+#undef SMBPROFILE_STATS_SECTION_END
+#undef SMBPROFILE_STATS_END
+
+ return True;
+}
+
+/* Convert microseconds to milliseconds. */
+#define usec_to_msec(s) ((s) / 1000)
+/* Convert microseconds to seconds. */
+#define usec_to_sec(s) ((s) / 1000000)
+/* One second in microseconds. */
+#define one_second_usec (1000000)
+
+#define sample_interval_usec one_second_usec
+
+#define percent_time(used, period) ((double)(used) / (double)(period) * 100.0 )
+
+static uint64_t print_count_count_samples(
+ char *buf, const size_t buflen,
+ const char *name,
+ const struct smbprofile_stats_count * const current,
+ const struct smbprofile_stats_count * const last,
+ uint64_t delta_usec)
+{
+ uint64_t step = current->count - last->count;
+ uint64_t count = 0;
+
+ if (step != 0) {
+ uint64_t delta_sec = usec_to_sec(delta_usec);
+
+ count++;
+
+ if (buf[0] == '\0') {
+ snprintf(buf, buflen,
+ "%-40s %ju/sec",
+ name, (uintmax_t)(step / delta_sec));
+ } else {
+ printf("%-40s %s %ju/sec\n",
+ buf, name, (uintmax_t)(step / delta_sec));
+ buf[0] = '\0';
+ }
+ }
+
+ return count;
+}
+
+static uint64_t print_basic_count_samples(
+ char *buf, const size_t buflen,
+ const char *name,
+ const struct smbprofile_stats_basic * const current,
+ const struct smbprofile_stats_basic * const last,
+ uint64_t delta_usec)
+{
+ uint64_t step = current->count - last->count;
+ uint64_t spent = current->time - last->time;
+ uint64_t count = 0;
+
+ if (step != 0) {
+ uint64_t delta_sec = usec_to_sec(delta_usec);
+
+ count++;
+
+ if (buf[0] == '\0') {
+ snprintf(buf, buflen,
+ "%s %ju/sec (%.2f%%)",
+ name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ } else {
+ printf("%-40s %s %ju/sec (%.2f%%)\n",
+ buf, name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ buf[0] = '\0';
+ }
+ }
+
+ return count;
+}
+
+static uint64_t print_bytes_count_samples(
+ char *buf, const size_t buflen,
+ const char *name,
+ const struct smbprofile_stats_bytes * const current,
+ const struct smbprofile_stats_bytes * const last,
+ uint64_t delta_usec)
+{
+ uint64_t step = current->count - last->count;
+ uint64_t spent = current->time - last->time;
+ uint64_t count = 0;
+
+ if (step != 0) {
+ uint64_t delta_sec = usec_to_sec(delta_usec);
+
+ count++;
+
+ if (buf[0] == '\0') {
+ snprintf(buf, buflen,
+ "%s %ju/sec (%.2f%%)",
+ name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ } else {
+ printf("%-40s %s %ju/sec (%.2f%%)\n",
+ buf, name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ buf[0] = '\0';
+ }
+ }
+
+ return count;
+}
+
+static uint64_t print_iobytes_count_samples(
+ char *buf, const size_t buflen,
+ const char *name,
+ const struct smbprofile_stats_iobytes * const current,
+ const struct smbprofile_stats_iobytes * const last,
+ uint64_t delta_usec)
+{
+ uint64_t step = current->count - last->count;
+ uint64_t spent = current->time - last->time;
+ uint64_t count = 0;
+
+ if (step != 0) {
+ uint64_t delta_sec = usec_to_sec(delta_usec);
+
+ count++;
+
+ if (buf[0] == '\0') {
+ snprintf(buf, buflen,
+ "%s %ju/sec (%.2f%%)",
+ name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ } else {
+ printf("%-40s %s %ju/sec (%.2f%%)\n",
+ buf, name, (uintmax_t)(step / delta_sec),
+ percent_time(spent, delta_usec));
+ buf[0] = '\0';
+ }
+ }
+
+ return count;
+}
+
+static uint64_t print_count_samples(
+ const struct profile_stats * const current,
+ const struct profile_stats * const last,
+ uint64_t delta_usec)
+{
+ uint64_t count = 0;
+ char buf[60] = { '\0', };
+
+ if (delta_usec == 0) {
+ return 0;
+ }
+
+#define SMBPROFILE_STATS_START
+#define SMBPROFILE_STATS_SECTION_START(name, display)
+#define SMBPROFILE_STATS_COUNT(name) do { \
+ count += print_count_count_samples(buf, sizeof(buf), \
+ #name, \
+ &current->values.name##_stats, \
+ &last->values.name##_stats, \
+ delta_usec); \
+} while(0);
+#define SMBPROFILE_STATS_TIME(name) do { \
+} while(0);
+#define SMBPROFILE_STATS_BASIC(name) do { \
+ count += print_basic_count_samples(buf, sizeof(buf), \
+ #name, \
+ &current->values.name##_stats, \
+ &last->values.name##_stats, \
+ delta_usec); \
+} while(0);
+#define SMBPROFILE_STATS_BYTES(name) do { \
+ count += print_bytes_count_samples(buf, sizeof(buf), \
+ #name, \
+ &current->values.name##_stats, \
+ &last->values.name##_stats, \
+ delta_usec); \
+} while(0);
+#define SMBPROFILE_STATS_IOBYTES(name) do { \
+ count += print_iobytes_count_samples(buf, sizeof(buf), \
+ #name, \
+ &current->values.name##_stats, \
+ &last->values.name##_stats, \
+ delta_usec); \
+} while(0);
+#define SMBPROFILE_STATS_SECTION_END
+#define SMBPROFILE_STATS_END
+ SMBPROFILE_STATS_ALL_SECTIONS
+#undef SMBPROFILE_STATS_START
+#undef SMBPROFILE_STATS_SECTION_START
+#undef SMBPROFILE_STATS_COUNT
+#undef SMBPROFILE_STATS_TIME
+#undef SMBPROFILE_STATS_BASIC
+#undef SMBPROFILE_STATS_BYTES
+#undef SMBPROFILE_STATS_IOBYTES
+#undef SMBPROFILE_STATS_SECTION_END
+#undef SMBPROFILE_STATS_END
+
+ if (buf[0] != '\0') {
+ printf("%-40s\n", buf);
+ buf[0] = '\0';
+ }
+
+ return count;
+}
+
+static struct profile_stats sample_data[2];
+static uint64_t sample_time[2];
+
+bool status_profile_rates(bool verbose)
+{
+ uint64_t remain_usec;
+ uint64_t next_usec;
+ uint64_t delta_usec;
+
+ int last = 0;
+ int current = 1;
+ int tmp;
+
+ if (verbose) {
+ fprintf(stderr, "Sampling stats at %d sec intervals\n",
+ usec_to_sec(sample_interval_usec));
+ }
+
+ if (!profile_setup(NULL, True)) {
+ fprintf(stderr,"Failed to initialise profile memory\n");
+ return False;
+ }
+
+ smbprofile_collect(&sample_data[last]);
+ for (;;) {
+ sample_time[current] = profile_timestamp();
+ next_usec = sample_time[current] + sample_interval_usec;
+
+ /* Take a sample. */
+ smbprofile_collect(&sample_data[current]);
+
+ /* Rate convert some values and print results. */
+ delta_usec = sample_time[current] - sample_time[last];
+
+ if (print_count_samples(&sample_data[current],
+ &sample_data[last], delta_usec)) {
+ printf("\n");
+ }
+
+ /* Swap sampling buffers. */
+ tmp = last;
+ last = current;
+ current = tmp;
+
+ /* Delay until next sample time. */
+ remain_usec = next_usec - profile_timestamp();
+ if (remain_usec > sample_interval_usec) {
+ fprintf(stderr, "eek! falling behind sampling rate!\n");
+ } else {
+ if (verbose) {
+ fprintf(stderr,
+ "delaying for %lu msec\n",
+ (unsigned long )usec_to_msec(remain_usec));
+ }
+
+ usleep(remain_usec);
+ }
+
+ }
+
+ return True;
+}
diff --git a/source3/utils/status_profile.h b/source3/utils/status_profile.h
new file mode 100644
index 0000000..eed54e0
--- /dev/null
+++ b/source3/utils/status_profile.h
@@ -0,0 +1,30 @@
+/*
+ * Samba Unix/Linux SMB client library
+ * Dump profiles
+ * Copyright (C) Volker Lendecke 2014
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __STATUS_PROFILE_H__
+#define __STATUS_PROFILE_H__
+
+#include "replace.h"
+#include "status.h"
+
+bool status_profile_dump(bool be_verbose,
+ struct traverse_state *state);
+bool status_profile_rates(bool be_verbose);
+
+#endif
diff --git a/source3/utils/status_profile_dummy.c b/source3/utils/status_profile_dummy.c
new file mode 100644
index 0000000..9083abf
--- /dev/null
+++ b/source3/utils/status_profile_dummy.c
@@ -0,0 +1,35 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Samba internal messaging functions
+ * Copyright (C) 2013 by Volker Lendecke
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "includes.h"
+#include "smbprofile.h"
+#include "status_profile.h"
+
+bool status_profile_dump(bool be_verbose,
+ struct traverse_state *state)
+{
+ fprintf(stderr, "Profile data unavailable\n");
+ return true;
+}
+
+bool status_profile_rates(bool be_verbose)
+{
+ fprintf(stderr, "Profile data unavailable\n");
+ return true;
+}
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
new file mode 100644
index 0000000..fd90e8d
--- /dev/null
+++ b/source3/utils/testparm.c
@@ -0,0 +1,1060 @@
+/*
+ Unix SMB/CIFS implementation.
+ Test validity of smb.conf
+ Copyright (C) Karl Auer 1993, 1994-1998
+
+ Extensively modified by Andrew Tridgell, 1995
+ Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ * Testbed for loadparm.c/params.c
+ *
+ * This module simply loads a specified configuration file and
+ * if successful, dumps it's contents to stdout. Note that the
+ * operation is performed with DEBUGLEVEL at 3.
+ *
+ * Useful for a quick 'syntax check' of a configuration file.
+ *
+ */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/param/loadparm.h"
+#include "lib/param/param.h"
+#include "lib/crypto/gnutls_helpers.h"
+#include "cmdline_contexts.h"
+
+#include <regex.h>
+
+/*******************************************************************
+ Check if a directory exists.
+********************************************************************/
+
+static bool directory_exist_stat(const char *dname,SMB_STRUCT_STAT *st)
+{
+ SMB_STRUCT_STAT st2;
+ bool ret;
+
+ if (!st)
+ st = &st2;
+
+ if (sys_stat(dname, st, false) != 0)
+ return(False);
+
+ ret = S_ISDIR(st->st_ex_mode);
+ if(!ret)
+ errno = ENOTDIR;
+ return ret;
+}
+
+struct idmap_config {
+ const char *domain_name;
+ const char *backend;
+ uint32_t high;
+ uint32_t low;
+};
+
+struct idmap_domains {
+ struct idmap_config *c;
+ uint32_t count;
+ uint32_t size;
+};
+
+static bool lp_scan_idmap_found_domain(const char *string,
+ regmatch_t matches[],
+ void *private_data)
+{
+ bool ok = false;
+
+ if (matches[1].rm_so == -1) {
+ fprintf(stderr, "Found match, but no name - invalid idmap config");
+ return false;
+ }
+ if (matches[1].rm_eo <= matches[1].rm_so) {
+ fprintf(stderr, "Invalid match - invalid idmap config");
+ return false;
+ }
+
+ {
+ struct idmap_domains *d = private_data;
+ struct idmap_config *c = &d->c[d->count];
+ regoff_t len = matches[1].rm_eo - matches[1].rm_so;
+ char domname[len + 1];
+
+ if (d->count >= d->size) {
+ return false;
+ }
+
+ memcpy(domname, string + matches[1].rm_so, len);
+ domname[len] = '\0';
+
+ c->domain_name = talloc_strdup_upper(d->c, domname);
+ if (c->domain_name == NULL) {
+ return false;
+ }
+ c->backend = talloc_strdup(d->c, lp_idmap_backend(domname));
+ if (c->backend == NULL) {
+ return false;
+ }
+
+ if (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) {
+ ok = lp_idmap_range(domname, &c->low, &c->high);
+ if (!ok) {
+ fprintf(stderr,
+ "ERROR: Invalid idmap range for domain "
+ "%s!\n\n",
+ c->domain_name);
+ return false;
+ }
+ }
+
+ d->count++;
+ }
+
+ return false; /* Keep scanning */
+}
+
+static int idmap_config_int(const char *domname, const char *option, int def)
+{
+ int len = snprintf(NULL, 0, "idmap config %s", domname);
+
+ if (len == -1) {
+ return def;
+ }
+ {
+ char config_option[len+1];
+ snprintf(config_option, sizeof(config_option),
+ "idmap config %s", domname);
+ return lp_parm_int(-1, config_option, option, def);
+ }
+}
+
+static bool do_idmap_check(void)
+{
+ struct idmap_domains *d;
+ uint32_t i;
+ bool ok = false;
+ int rc;
+
+ d = talloc_zero(talloc_tos(), struct idmap_domains);
+ if (d == NULL) {
+ return false;
+ }
+ d->count = 0;
+ d->size = 32;
+
+ d->c = talloc_array(d, struct idmap_config, d->size);
+ if (d->c == NULL) {
+ goto done;
+ }
+
+ rc = lp_wi_scan_global_parametrics("idmapconfig\\(.*\\):backend",
+ 2,
+ lp_scan_idmap_found_domain,
+ d);
+ if (rc != 0) {
+ fprintf(stderr,
+ "FATAL: wi_scan_global_parametrics failed: %d",
+ rc);
+ }
+
+ /* Check autorid backend */
+ if (strequal(lp_idmap_default_backend(), "autorid")) {
+ struct idmap_config *c = NULL;
+ bool found = false;
+
+ for (i = 0; i < d->count; i++) {
+ c = &d->c[i];
+
+ if (strequal(c->backend, "autorid")) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found) {
+ uint32_t rangesize =
+ idmap_config_int("*", "rangesize", 100000);
+ uint32_t maxranges =
+ (c->high - c->low + 1) / rangesize;
+
+ if (((c->high - c->low + 1) % rangesize) != 0) {
+ fprintf(stderr,
+ "WARNING: The idmap autorid range "
+ "[%u-%u] SHOULD be a multiple of "
+ "the rangesize [%u]!"
+ "\n\n",
+ c->low,
+ c->high,
+ rangesize);
+ }
+
+ if (maxranges < 2) {
+ fprintf(stderr,
+ "ERROR: The idmap autorid range "
+ "[%u-%u] needs to be at least twice as "
+ "big as the rangesize [%u]!"
+ "\n\n",
+ c->low,
+ c->high,
+ rangesize);
+ ok = false;
+ goto done;
+ }
+ }
+ }
+
+ /* Check for overlapping idmap ranges */
+ for (i = 0; i < d->count; i++) {
+ struct idmap_config *c = &d->c[i];
+ uint32_t j;
+
+ for (j = 0; j < d->count && j != i; j++) {
+ struct idmap_config *x = &d->c[j];
+
+ if ((c->low >= x->low && c->low <= x->high) ||
+ (c->high >= x->low && c->high <= x->high)) {
+ /*
+ * Allow overlapping ranges for idmap_ad
+ * and idmap_nss
+ */
+ ok = strequal(c->backend, x->backend);
+ if (ok) {
+ ok = strequal(c->backend, "ad") ||
+ strequal(c->backend, "nss");
+ if (ok) {
+ fprintf(stderr,
+ "NOTE: The idmap_%s "
+ "range for the domain "
+ "%s overlaps with the "
+ "range of %s.\n\n",
+ c->backend,
+ c->domain_name,
+ x->domain_name);
+ continue;
+ }
+ }
+
+ fprintf(stderr,
+ "ERROR: The idmap range for the domain "
+ "%s (%s) overlaps with the range of "
+ "%s (%s)!\n\n",
+ c->domain_name,
+ c->backend,
+ x->domain_name,
+ x->backend);
+ ok = false;
+ goto done;
+ }
+ }
+ }
+
+ ok = true;
+done:
+ TALLOC_FREE(d);
+ return ok;
+}
+
+/***********************************************
+ Here we do a set of 'hard coded' checks for bad
+ configuration settings.
+************************************************/
+
+static int do_global_checks(void)
+{
+ int ret = 0;
+ SMB_STRUCT_STAT st;
+ const char *socket_options;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+
+ fprintf(stderr, "\n");
+
+ if (lp_security() >= SEC_DOMAIN && !lp_encrypt_passwords()) {
+ fprintf(stderr, "ERROR: in 'security=domain' mode the "
+ "'encrypt passwords' parameter must always be "
+ "set to 'true'.\n\n");
+ ret = 1;
+ }
+
+ if (lp_security() == SEC_ADS) {
+ const char *workgroup = lp_workgroup();
+ const char *realm = lp_realm();
+
+ if (workgroup == NULL || strlen(workgroup) == 0) {
+ fprintf(stderr,
+ "ERROR: The 'security=ADS' mode requires "
+ "'workgroup' parameter to be set!\n\n ");
+ ret = 1;
+ }
+
+ if (realm == NULL || strlen(realm) == 0) {
+ fprintf(stderr,
+ "ERROR: The 'security=ADS' mode requires "
+ "'realm' parameter to be set!\n\n ");
+ ret = 1;
+ }
+ }
+
+
+ if (lp_we_are_a_wins_server() && lp_wins_server_list()) {
+ fprintf(stderr, "ERROR: both 'wins support = true' and "
+ "'wins server = <server list>' cannot be set in "
+ "the smb.conf file. nmbd will abort with this "
+ "setting.\n\n");
+ ret = 1;
+ }
+
+ if (strequal(lp_workgroup(), lp_netbios_name())) {
+ fprintf(stderr, "WARNING: 'workgroup' and 'netbios name' "
+ "must differ.\n\n");
+ }
+
+ if (lp_client_ipc_signing() == SMB_SIGNING_IF_REQUIRED
+ || lp_client_ipc_signing() == SMB_SIGNING_OFF) {
+ fprintf(stderr, "WARNING: The 'client ipc signing' value "
+ "%s SMB signing is not used when contacting a "
+ "domain controller or other server. "
+ "This setting is not recommended; please be "
+ "aware of the security implications when using "
+ "this configuration setting.\n\n",
+ lp_client_ipc_signing() == SMB_SIGNING_OFF ?
+ "ensures" : "may mean");
+ }
+
+ if (strlen(lp_netbios_name()) > 15) {
+ fprintf(stderr, "WARNING: The 'netbios name' is too long "
+ "(max. 15 chars).\n\n");
+ }
+
+ if (!directory_exist_stat(lp_lock_directory(), &st)) {
+ fprintf(stderr, "ERROR: lock directory %s does not exist\n\n",
+ lp_lock_directory());
+ ret = 1;
+ } else if ((st.st_ex_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: lock directory %s should have "
+ "permissions 0755 for browsing to work\n\n",
+ lp_lock_directory());
+ }
+
+ if (!directory_exist_stat(lp_state_directory(), &st)) {
+ fprintf(stderr, "ERROR: state directory %s does not exist\n\n",
+ lp_state_directory());
+ ret = 1;
+ } else if ((st.st_ex_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: state directory %s should have "
+ "permissions 0755 for browsing to work\n\n",
+ lp_state_directory());
+ }
+
+ if (!directory_exist_stat(lp_cache_directory(), &st)) {
+ fprintf(stderr, "ERROR: cache directory %s does not exist\n\n",
+ lp_cache_directory());
+ ret = 1;
+ } else if ((st.st_ex_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: cache directory %s should have "
+ "permissions 0755 for browsing to work\n\n",
+ lp_cache_directory());
+ }
+
+ if (!directory_exist_stat(lp_pid_directory(), &st)) {
+ fprintf(stderr, "ERROR: pid directory %s does not exist\n\n",
+ lp_pid_directory());
+ ret = 1;
+ }
+
+ if (lp_passdb_expand_explicit()) {
+ fprintf(stderr, "WARNING: passdb expand explicit = yes is "
+ "deprecated\n\n");
+ }
+
+ /*
+ * Socket options.
+ */
+ socket_options = lp_socket_options();
+ if (socket_options != NULL &&
+ (strstr(socket_options, "SO_SNDBUF") ||
+ strstr(socket_options, "SO_RCVBUF") ||
+ strstr(socket_options, "SO_SNDLOWAT") ||
+ strstr(socket_options, "SO_RCVLOWAT")))
+ {
+ fprintf(stderr,
+ "WARNING: socket options = %s\n"
+ "This warning is printed because you set one of the\n"
+ "following options: SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT,\n"
+ "SO_RCVLOWAT\n"
+ "Modern server operating systems are tuned for\n"
+ "high network performance in the majority of situations;\n"
+ "when you set 'socket options' you are overriding those\n"
+ "settings.\n"
+ "Linux in particular has an auto-tuning mechanism for\n"
+ "buffer sizes (SO_SNDBUF, SO_RCVBUF) that will be\n"
+ "disabled if you specify a socket buffer size. This can\n"
+ "potentially cripple your TCP/IP stack.\n\n"
+ "Getting the 'socket options' correct can make a big\n"
+ "difference to your performance, but getting them wrong\n"
+ "can degrade it by just as much. As with any other low\n"
+ "level setting, if you must make changes to it, make\n "
+ "small changes and test the effect before making any\n"
+ "large changes.\n\n",
+ socket_options);
+ }
+
+ /*
+ * Password server sanity checks.
+ */
+
+ if((lp_security() >= SEC_DOMAIN) && !*lp_password_server()) {
+ const char *sec_setting;
+ if(lp_security() == SEC_DOMAIN)
+ sec_setting = "domain";
+ else if(lp_security() == SEC_ADS)
+ sec_setting = "ads";
+ else
+ sec_setting = "";
+
+ fprintf(stderr, "ERROR: The setting 'security=%s' requires the "
+ "'password server' parameter be set to the "
+ "default value * or a valid password server.\n\n",
+ sec_setting );
+ ret = 1;
+ }
+
+ if((lp_security() >= SEC_DOMAIN) && (strcmp(lp_password_server(), "*") != 0)) {
+ const char *sec_setting;
+ if(lp_security() == SEC_DOMAIN)
+ sec_setting = "domain";
+ else if(lp_security() == SEC_ADS)
+ sec_setting = "ads";
+ else
+ sec_setting = "";
+
+ fprintf(stderr, "WARNING: The setting 'security=%s' should NOT "
+ "be combined with the 'password server' "
+ "parameter.\n"
+ "(by default Samba will discover the correct DC "
+ "to contact automatically).\n\n",
+ sec_setting );
+ }
+
+ /*
+ * Password chat sanity checks.
+ */
+
+ if(lp_security() == SEC_USER && lp_unix_password_sync()) {
+
+ /*
+ * Check that we have a valid lp_passwd_program() if not using pam.
+ */
+
+#ifdef WITH_PAM
+ if (!lp_pam_password_change()) {
+#endif
+
+ if((lp_passwd_program(talloc_tos(), lp_sub) == NULL) ||
+ (strlen(lp_passwd_program(talloc_tos(), lp_sub)) == 0))
+ {
+ fprintf(stderr,
+ "ERROR: the 'unix password sync' "
+ "parameter is set and there is no valid "
+ "'passwd program' parameter.\n\n");
+ ret = 1;
+ } else {
+ const char *passwd_prog;
+ char *truncated_prog = NULL;
+ const char *p;
+
+ passwd_prog = lp_passwd_program(talloc_tos(), lp_sub);
+ p = passwd_prog;
+ next_token_talloc(talloc_tos(),
+ &p,
+ &truncated_prog, NULL);
+ if (truncated_prog && access(truncated_prog, F_OK) == -1) {
+ fprintf(stderr,
+ "ERROR: the 'unix password sync' "
+ "parameter is set and the "
+ "'passwd program' (%s) cannot be "
+ "executed (error was %s).\n\n",
+ truncated_prog,
+ strerror(errno));
+ ret = 1;
+ }
+ }
+
+#ifdef WITH_PAM
+ }
+#endif
+
+ if(lp_passwd_chat(talloc_tos(), lp_sub) == NULL) {
+ fprintf(stderr,
+ "ERROR: the 'unix password sync' parameter is "
+ "set and there is no valid 'passwd chat' "
+ "parameter.\n\n");
+ ret = 1;
+ }
+
+ if ((lp_passwd_program(talloc_tos(), lp_sub) != NULL) &&
+ (strlen(lp_passwd_program(talloc_tos(), lp_sub)) > 0))
+ {
+ /* check if there's a %u parameter present */
+ if(strstr_m(lp_passwd_program(talloc_tos(), lp_sub), "%u") == NULL) {
+ fprintf(stderr,
+ "ERROR: the 'passwd program' (%s) "
+ "requires a '%%u' parameter.\n\n",
+ lp_passwd_program(talloc_tos(), lp_sub));
+ ret = 1;
+ }
+ }
+
+ /*
+ * Check that we have a valid script and that it hasn't
+ * been written to expect the old password.
+ */
+
+ if(lp_encrypt_passwords()) {
+ if(strstr_m( lp_passwd_chat(talloc_tos(), lp_sub), "%o")!=NULL) {
+ fprintf(stderr,
+ "ERROR: the 'passwd chat' script [%s] "
+ "expects to use the old plaintext "
+ "password via the %%o substitution. With "
+ "encrypted passwords this is not "
+ "possible.\n\n",
+ lp_passwd_chat(talloc_tos(), lp_sub) );
+ ret = 1;
+ }
+ }
+ }
+
+ if (strlen(lp_winbind_separator()) != 1) {
+ fprintf(stderr, "ERROR: the 'winbind separator' parameter must "
+ "be a single character.\n\n");
+ ret = 1;
+ }
+
+ if (*lp_winbind_separator() == '+') {
+ fprintf(stderr, "'winbind separator = +' might cause problems "
+ "with group membership.\n\n");
+ }
+
+ if (lp_algorithmic_rid_base() < BASE_RID) {
+ /* Try to prevent admin foot-shooting, we can't put algorithmic
+ rids below 1000, that's the 'well known RIDs' on NT */
+ fprintf(stderr, "'algorithmic rid base' must be equal to or "
+ "above %lu\n\n", BASE_RID);
+ }
+
+ if (lp_algorithmic_rid_base() & 1) {
+ fprintf(stderr, "'algorithmic rid base' must be even.\n\n");
+ }
+
+ if (lp_server_role() != ROLE_STANDALONE) {
+ const char *default_backends[] = {
+ "tdb", "tdb2", "ldap", "autorid", "hash"
+ };
+ const char *idmap_backend;
+ bool valid_backend = false;
+ uint32_t i;
+ bool ok;
+
+ idmap_backend = lp_idmap_default_backend();
+
+ for (i = 0; i < ARRAY_SIZE(default_backends); i++) {
+ ok = strequal(idmap_backend, default_backends[i]);
+ if (ok) {
+ valid_backend = true;
+ }
+ }
+
+ if (!valid_backend) {
+ ret = 1;
+ fprintf(stderr, "ERROR: Do not use the '%s' backend "
+ "as the default idmap backend!\n\n",
+ idmap_backend);
+ }
+
+ ok = do_idmap_check();
+ if (!ok) {
+ ret = 1;
+ }
+ }
+
+#ifndef HAVE_DLOPEN
+ if (lp_preload_modules()) {
+ fprintf(stderr, "WARNING: 'preload modules = ' set while loading "
+ "plugins not supported.\n\n");
+ }
+#endif
+
+ if (!lp_passdb_backend()) {
+ fprintf(stderr, "ERROR: passdb backend must have a value or be "
+ "left out\n\n");
+ }
+
+ if (lp_os_level() > 255) {
+ fprintf(stderr, "WARNING: Maximum value for 'os level' is "
+ "255!\n\n");
+ }
+
+ if (strequal(lp_dos_charset(), "UTF8") || strequal(lp_dos_charset(), "UTF-8")) {
+ fprintf(stderr, "ERROR: 'dos charset' must not be UTF8\n\n");
+ ret = 1;
+ }
+
+ if (lp_server_schannel() != true) { /* can be 'auto' */
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'server schannel = yes' (the default). "
+ "Your server is vulnerable to \"ZeroLogon\" "
+ "(CVE-2020-1472)\n"
+ "If required use individual "
+ "'server require schannel:COMPUTERACCOUNT$ = no' "
+ "options\n\n");
+ }
+ if (lp_allow_nt4_crypto()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'allow nt4 crypto = no' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023 and others!\n"
+ "If required use individual "
+ "'allow nt4 crypto:COMPUTERACCOUNT$ = yes' "
+ "options\n\n");
+ }
+ if (!lp_reject_md5_clients()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'reject md5 clients = yes' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023!\n"
+ "If required use individual "
+ "'server reject md5 schannel:COMPUTERACCOUNT$ = yes' "
+ "options\n\n");
+ }
+ if (!lp_server_schannel_require_seal()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'server schannel require seal = yes' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023!\n"
+ "If required use individual "
+ "'server schannel require seal:COMPUTERACCOUNT$ = no' "
+ "options\n\n");
+ }
+
+ if (lp_client_schannel() != true) { /* can be 'auto' */
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'client schannel = yes' (the default). "
+ "Your server is vulnerable to \"ZeroLogon\" "
+ "(CVE-2020-1472)\n"
+ "If required use individual "
+ "'client schannel:NETBIOSDOMAIN = no' "
+ "options\n\n");
+ }
+ if (!lp_reject_md5_servers()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'reject md5 servers = yes' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023\n"
+ "If required use individual "
+ "'reject md5 servers:NETBIOSDOMAIN = no' "
+ "options\n\n");
+ }
+ if (!lp_require_strong_key()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'require strong key = yes' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023\n"
+ "If required use individual "
+ "'require strong key:NETBIOSDOMAIN = no' "
+ "options\n\n");
+ }
+ if (!lp_winbind_sealed_pipes()) {
+ fprintf(stderr,
+ "WARNING: You have not configured "
+ "'winbind sealed pipes = yes' (the default). "
+ "Your server is vulnerable to "
+ "CVE-2022-38023\n"
+ "If required use individual "
+ "'winbind sealed pipes:NETBIOSDOMAIN = no' "
+ "options\n\n");
+ }
+
+ if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) {
+ fprintf(stderr,
+ "WARNING: You have configured "
+ "'kerberos encryption types = legacy'. "
+ "Your server is vulnerable to "
+ "CVE-2022-37966\n\n");
+ }
+
+ return ret;
+}
+
+/**
+ * per-share logic tests
+ */
+static void do_per_share_checks(int s)
+{
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ const char **deny_list = lp_hosts_deny(s);
+ const char **allow_list = lp_hosts_allow(s);
+ const char **vfs_objects = NULL;
+ int i;
+ static bool uses_fruit;
+ static bool doesnt_use_fruit;
+ static bool fruit_mix_warned;
+
+ if(deny_list) {
+ for (i=0; deny_list[i]; i++) {
+ char *hasstar = strchr_m(deny_list[i], '*');
+ char *hasquery = strchr_m(deny_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,
+ "Invalid character %c in hosts deny list "
+ "(%s) for service %s.\n\n",
+ hasstar ? *hasstar : *hasquery,
+ deny_list[i],
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ }
+ }
+
+ if(allow_list) {
+ for (i=0; allow_list[i]; i++) {
+ char *hasstar = strchr_m(allow_list[i], '*');
+ char *hasquery = strchr_m(allow_list[i], '?');
+ if(hasstar || hasquery) {
+ fprintf(stderr,
+ "Invalid character %c in hosts allow "
+ "list (%s) for service %s.\n\n",
+ hasstar ? *hasstar : *hasquery,
+ allow_list[i],
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ }
+ }
+
+ if(lp_level2_oplocks(s) && !lp_oplocks(s)) {
+ fprintf(stderr, "Invalid combination of parameters for service "
+ "%s. Level II oplocks can only be set if oplocks "
+ "are also set.\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+
+ if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
+ && !(lp_create_mask(s) & S_IXOTH))
+ {
+ fprintf(stderr,
+ "Invalid combination of parameters for service %s. Map "
+ "hidden can only work if create mask includes octal "
+ "01 (S_IXOTH).\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ if (!lp_store_dos_attributes(s) && lp_map_hidden(s)
+ && (lp_force_create_mode(s) & S_IXOTH))
+ {
+ fprintf(stderr,
+ "Invalid combination of parameters for service "
+ "%s. Map hidden can only work if force create mode "
+ "excludes octal 01 (S_IXOTH).\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ if (!lp_store_dos_attributes(s) && lp_map_system(s)
+ && !(lp_create_mask(s) & S_IXGRP))
+ {
+ fprintf(stderr,
+ "Invalid combination of parameters for service "
+ "%s. Map system can only work if create mask includes "
+ "octal 010 (S_IXGRP).\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ if (!lp_store_dos_attributes(s) && lp_map_system(s)
+ && (lp_force_create_mode(s) & S_IXGRP))
+ {
+ fprintf(stderr,
+ "Invalid combination of parameters for service "
+ "%s. Map system can only work if force create mode "
+ "excludes octal 010 (S_IXGRP).\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ if (lp_printing(s) == PRINT_CUPS && *(lp_print_command(s)) != '\0') {
+ fprintf(stderr,
+ "Warning: Service %s defines a print command, but "
+ "parameter is ignored when using CUPS libraries.\n\n",
+ lp_servicename(talloc_tos(), lp_sub, s));
+ }
+
+ vfs_objects = lp_vfs_objects(s);
+ if (vfs_objects && str_list_check(vfs_objects, "fruit")) {
+ uses_fruit = true;
+ } else {
+ doesnt_use_fruit = true;
+ }
+
+ if (uses_fruit && doesnt_use_fruit && !fruit_mix_warned) {
+ fruit_mix_warned = true;
+ fprintf(stderr,
+ "WARNING: some services use vfs_fruit, others don't. Mounting them "
+ "in conjunction on OS X clients results in undefined behaviour.\n\n");
+ }
+}
+
+ int main(int argc, const char *argv[])
+{
+ const char *config_file = NULL;
+ const struct loadparm_substitution *lp_sub =
+ loadparm_s3_global_substitution();
+ int opt;
+ int s;
+ static int silent_mode = False;
+ static int show_all_parameters = False;
+ int ret = 0;
+ poptContext pc;
+ static char *parameter_name = NULL;
+ static const char *section_name = NULL;
+ const char *cname;
+ const char *caddr;
+ static int show_defaults;
+ static int skip_logic_checks = 0;
+ bool ok;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "suppress-prompt",
+ .shortName = 's',
+ .argInfo = POPT_ARG_VAL,
+ .arg = &silent_mode,
+ .val = 1,
+ .descrip = "Suppress prompt for enter",
+ },
+ {
+ .longName = "verbose",
+ .shortName = 'v',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &show_defaults,
+ .val = 1,
+ .descrip = "Show default options too",
+ },
+ {
+ .longName = "skip-logic-checks",
+ .shortName = 'l',
+ .argInfo = POPT_ARG_NONE,
+ .arg = &skip_logic_checks,
+ .val = 1,
+ .descrip = "Skip the global checks",
+ },
+ {
+ .longName = "show-all-parameters",
+ .shortName = '\0',
+ .argInfo = POPT_ARG_VAL,
+ .arg = &show_all_parameters,
+ .val = True,
+ .descrip = "Show the parameters, type, possible "
+ "values",
+ },
+ {
+ .longName = "parameter-name",
+ .shortName = '\0',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &parameter_name,
+ .val = 0,
+ .descrip = "Limit testparm to a named parameter",
+ },
+ {
+ .longName = "section-name",
+ .shortName = '\0',
+ .argInfo = POPT_ARG_STRING,
+ .arg = &section_name,
+ .val = 0,
+ .descrip = "Limit testparm to a named section",
+ },
+ POPT_COMMON_DEBUG_ONLY
+ POPT_COMMON_OPTION_ONLY
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct loadparm_context *lp_ctx = NULL;
+
+ smb_init_locale();
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_NONE,
+ true /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ ret = 1;
+ goto done;
+ }
+ lp_ctx = samba_cmdline_get_lp_ctx();
+
+ /*
+ * Set the default debug level to 1.
+ * Allow it to be overridden by the command line,
+ * not by smb.conf.
+ */
+ lpcfg_set_cmdline(lp_ctx, "log level", "1");
+
+ pc = samba_popt_get_context(getprogname(),
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ ret = 1;
+ goto done;
+ }
+
+ poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case POPT_ERROR_BADOPT:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ }
+
+ if (show_all_parameters) {
+ show_parameter_list();
+ exit(0);
+ }
+
+ if (poptPeekArg(pc)) {
+ config_file = talloc_strdup(frame, poptGetArg(pc));
+ if (config_file == NULL) {
+ DBG_ERR("out of memory\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+ } else {
+ config_file = get_dyn_CONFIGFILE();
+ }
+
+ cname = talloc_strdup(frame, poptGetArg(pc));
+ caddr = talloc_strdup(frame, poptGetArg(pc));
+
+ poptFreeContext(pc);
+
+ if ( cname && ! caddr ) {
+ printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
+ ret = 1;
+ goto done;
+ }
+
+ fprintf(stderr,"Load smb config files from %s\n",config_file);
+
+ if (!lp_load_with_registry_shares(config_file)) {
+ fprintf(stderr,"Error loading services.\n");
+ ret = 1;
+ goto done;
+ }
+
+ fprintf(stderr,"Loaded services file OK.\n");
+
+ fprintf(stderr,
+ "Weak crypto is %sallowed by GnuTLS "
+ "(e.g. NTLM as a compatibility fallback)\n",
+ samba_gnutls_weak_crypto_allowed() ? "" : "dis");
+
+ if (skip_logic_checks == 0) {
+ ret = do_global_checks();
+ }
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s) && (skip_logic_checks == 0)) {
+ do_per_share_checks(s);
+ }
+ }
+
+
+ if (!section_name && !parameter_name) {
+ fprintf(stderr,
+ "Server role: %s\n\n",
+ server_role_str(lp_server_role()));
+ }
+
+ if (!cname) {
+ if (!silent_mode) {
+ fprintf(stderr,"Press enter to see a dump of your service definitions\n");
+ fflush(stdout);
+ getc(stdin);
+ }
+ if (parameter_name || section_name) {
+ bool isGlobal = False;
+ s = GLOBAL_SECTION_SNUM;
+
+ if (!section_name) {
+ section_name = GLOBAL_NAME;
+ isGlobal = True;
+ } else if ((isGlobal=!strwicmp(section_name, GLOBAL_NAME)) == 0 &&
+ (s=lp_servicenumber(section_name)) == -1) {
+ fprintf(stderr,"Unknown section %s\n",
+ section_name);
+ ret = 1;
+ goto done;
+ }
+ if (parameter_name) {
+ if (!dump_a_parameter( s, parameter_name, stdout, isGlobal)) {
+ fprintf(stderr,"Parameter %s unknown for section %s\n",
+ parameter_name, section_name);
+ ret = 1;
+ goto done;
+ }
+ } else {
+ if (isGlobal == True)
+ lp_dump(stdout, show_defaults, 0);
+ else
+ lp_dump_one(stdout, show_defaults, s);
+ }
+ goto done;
+ }
+
+ lp_dump(stdout, show_defaults, lp_numservices());
+ }
+
+ if(cname && caddr){
+ /* this is totally ugly, a real `quick' hack */
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s)) {
+ if (allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1), cname, caddr)
+ && allow_access(lp_hosts_deny(s), lp_hosts_allow(s), cname, caddr)) {
+ fprintf(stderr,"Allow connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(talloc_tos(), lp_sub, s));
+ } else {
+ fprintf(stderr,"Deny connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(talloc_tos(), lp_sub, s));
+ }
+ }
+ }
+ }
+
+done:
+ gfree_loadparm();
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build
new file mode 100644
index 0000000..ca57e80
--- /dev/null
+++ b/source3/utils/wscript_build
@@ -0,0 +1,364 @@
+#!/usr/bin/env python
+
+bld.SAMBA3_SUBSYSTEM('PASSWD_UTIL',
+ source='passwd_util.c',
+ deps='samba-util')
+
+bld.SAMBA3_SUBSYSTEM('CONN_TDB',
+ source='conn_tdb.c')
+
+bld.SAMBA3_SUBSYSTEM('DNS_UTIL',
+ source='net_dns.c net_ads_join_dns.c',
+ deps='addns')
+
+bld.SAMBA3_BINARY('profiles',
+ source='profiles.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ smbconf
+ REGFIO''')
+
+bld.SAMBA3_BINARY('smbcontrol',
+ source='smbcontrol.c',
+ deps='''
+ talloc
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ PRINTBASE''')
+
+bld.SAMBA3_BINARY('smbtree',
+ source='smbtree.c',
+ deps='''
+ talloc
+ smbconf
+ smbclient
+ msrpc3
+ CMDLINE_S3
+ RPC_NDR_SRVSVC''')
+
+bld.SAMBA3_BINARY('smbpasswd',
+ source='smbpasswd.c',
+ deps='''
+ talloc
+ smbconf
+ pdb
+ PASSWD_UTIL
+ PASSCHANGE
+ cmdline_contexts
+ ''')
+
+bld.SAMBA3_BINARY('pdbedit',
+ source='pdbedit.c',
+ deps='''
+ talloc
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ pdb
+ PASSWD_UTIL''')
+
+bld.SAMBA3_BINARY('smbget',
+ source='smbget.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ smbclient''')
+
+bld.SAMBA3_BINARY('nmblookup',
+ source='nmblookup.c',
+ deps='''
+ talloc
+ smbconf
+ CMDLINE_S3
+ LIBNMB''')
+
+bld.SAMBA3_BINARY('smbcacls',
+ source='smbcacls.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ msrpc3
+ libcli_lsa3
+ util_sd
+ krb5samba''')
+
+bld.SAMBA3_BINARY('smbcquotas',
+ source='smbcquotas.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ libsmb
+ msrpc3
+ libcli_lsa3''')
+
+bld.SAMBA3_BINARY('eventlogadm',
+ source='eventlogadm.c',
+ deps='''
+ talloc
+ smbconf
+ cmdline_contexts
+ LIBEVENTLOG''',
+ install_path='${SBINDIR}')
+
+bld.SAMBA3_BINARY('sharesec',
+ source='sharesec.c',
+ deps='''
+ talloc
+ msrpc3
+ libcli_lsa3
+ CMDLINE_S3
+ cmdline_contexts
+ util_sd
+ ''')
+
+bld.SAMBA3_BINARY('log2pcap',
+ source='log2pcaphex.c',
+ deps='''talloc popt''',
+ install=False)
+
+bld.SAMBA3_BINARY('smbfilter',
+ source='smbfilter.c',
+ deps='''
+ talloc
+ smbconf
+ LIBNMB''',
+ install=False)
+
+bld.SAMBA3_BINARY('ntlm_auth',
+ source='''ntlm_auth.c ntlm_auth_diagnostics.c''',
+ deps='''
+ talloc
+ krb5samba
+ tiniparser
+ libsmb
+ CMDLINE_S3
+ cmdline_contexts
+ wbclient
+ gse gensec''')
+
+bld.SAMBA3_BINARY('dbwrap_tool',
+ source='dbwrap_tool.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ cmdline_contexts
+ ''')
+
+bld.SAMBA3_BINARY('dbwrap_torture',
+ source='dbwrap_torture.c',
+ deps='''
+ talloc
+ CMDLINE_S3
+ smbconf
+ ''',
+ install=False)
+
+bld.SAMBA3_BINARY('samba-regedit',
+ source="""regedit.c regedit_samba3.c
+ regedit_wrap.c regedit_treeview.c
+ regedit_valuelist.c regedit_dialog.c
+ regedit_hexedit.c regedit_list.c""",
+ deps='''
+ ncurses
+ menu
+ panel
+ form
+ registry
+ smbconf
+ CMDLINE_S3
+ ''',
+ enabled=bld.env.build_regedit)
+
+bld.SAMBA3_BINARY('testparm',
+ source='testparm.c',
+ deps='''
+ talloc
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ GNUTLS_HELPERS
+ ''')
+
+bld.SAMBA3_BINARY('net',
+ source='''net.c
+ net_ads.c
+ net_help.c
+ clirap2.c
+ net_rap.c
+ net_rpc.c
+ net_rpc_samsync.c
+ net_time.c
+ net_lookup.c
+ net_cache.c
+ net_groupmap.c
+ net_idmap.c
+ net_idmap_check.c
+ interact.c
+ net_status.c
+ net_rpc_printer.c
+ net_rpc_rights.c
+ net_rpc_service.c
+ net_rpc_registry.c
+ net_usershare.c
+ netlookup.c
+ net_sam.c
+ net_rpc_shell.c
+ net_util.c
+ net_rpc_sh_acct.c
+ net_rpc_audit.c
+ net_ads_gpo.c
+ net_conf.c
+ net_conf_util.c
+ net_join.c
+ net_offlinejoin.c
+ net_user.c
+ net_group.c
+ net_file.c
+ net_registry.c
+ net_registry_check.c
+ net_dom.c
+ net_share.c
+ net_g_lock.c
+ net_serverid.c
+ net_eventlog.c
+ net_printing.c
+ net_rpc_trust.c
+ net_rpc_conf.c
+ net_afs.c
+ net_notify.c
+ net_tdb.c
+ net_witness.c
+ net_vfs.c
+ ../registry/reg_format.c
+ ../registry/reg_import.c
+ net_registry_util.c
+ net_help_common.c''',
+ deps='''
+ talloc
+ netapi
+ addns
+ samba_intl
+ CMDLINE_S3
+ cmdline_contexts
+ pdb
+ libsmb
+ smbconf
+ KRBCLIENT
+ ndr-standard
+ msrpc3
+ gpo
+ ads
+ smbd_base
+ LIBADS_SERVER
+ LIBADS_PRINTER
+ SMBREADLINE
+ PASSWD_UTIL
+ LIBNET
+ LIBNET_DSSYNC
+ LIBEVENTLOG
+ REGFIO
+ NDR_NTPRINTING
+ RPC_NDR_WINREG
+ RPC_CLIENT_SCHANNEL
+ LIBCLI_SAMR
+ libcli_lsa3
+ libcli_netlogon3
+ cli_spoolss
+ RPC_NDR_SRVSVC
+ RPC_NDR_SVCCTL
+ RPC_NDR_DSSETUP
+ RPC_NDR_INITSHUTDOWN
+ printing_migrate
+ trusts_util
+ IDMAP_AUTORID_TDB
+ CONN_TDB
+ jansson
+ common_auth
+ ADOUBLE
+ DNS_UTIL
+ util_sd
+ ''')
+
+bld.SAMBA3_BINARY('mvxattr',
+ source='mvxattr.c',
+ deps='''
+ talloc
+ popt
+ samba-util
+ ''',
+ enabled=bld.env.build_mvxattr)
+
+bld.SAMBA3_BINARY('destroy_netlogon_creds_cli',
+ source='destroy_netlogon_creds_cli.c',
+ deps = '''
+ talloc
+ smbconf
+ NETLOGON_CREDS_CLI
+ ''',
+ install=False)
+
+smbstatus_source = 'status.c'
+
+if bld.CONFIG_GET("WITH_PROFILE"):
+ smbstatus_source += ' status_profile.c'
+else:
+ smbstatus_source += ' status_profile_dummy.c'
+
+if bld.CONFIG_GET("HAVE_JANSSON"):
+ smbstatus_source += ' status_json.c'
+else:
+ smbstatus_source += ' status_json_dummy.c'
+
+bld.SAMBA3_BINARY('smbstatus',
+ source=smbstatus_source,
+ deps='''
+ talloc
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ smbd_base
+ LOCKING
+ PROFILE
+ CONN_TDB
+ ''')
+
+bld.SAMBA3_BINARY('mdsearch',
+ source='mdsearch.c',
+ deps='''
+ talloc
+ tevent
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ libsmb
+ msrpc3
+ RPCCLI_MDSSVC
+ mdssvc
+ ''')
+
+bld.SAMBA3_BINARY('wspsearch',
+ source='wspsearch.c',
+ deps='''
+ talloc
+ tevent
+ smbconf
+ CMDLINE_S3
+ cmdline_contexts
+ libsmb
+ msrpc3
+ LIBSAMBA_WSP
+ RPCCLI_WSP
+ WSP_UTIL
+ dcerpc
+ ''',
+ enabled=bld.env.with_wsp)
+
+pytalloc_util = bld.pyembed_libname('pytalloc-util')
+pyrpc_util = bld.pyembed_libname('pyrpc_util')
+bld.SAMBA3_PYTHON('python_net_s3',
+ source='py_net.c',
+ deps='LIBNET DNS_UTIL cmdline_contexts %s %s' % (pytalloc_util, pyrpc_util),
+ realname='samba/net_s3.so'
+ )
diff --git a/source3/utils/wspsearch.c b/source3/utils/wspsearch.c
new file mode 100644
index 0000000..063b952
--- /dev/null
+++ b/source3/utils/wspsearch.c
@@ -0,0 +1,842 @@
+/*
+ * Unix SMB/CIFS implementation.
+ *
+ * Window Search Service
+ *
+ * Copyright (c) Noel Power
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+#include "includes.h"
+#include "lib/util/debug.h"
+#include "lib/cmdline/cmdline.h"
+#include "lib/cmdline_contexts.h"
+#include "param.h"
+#include "client.h"
+#include "libsmb/proto.h"
+#include "librpc/rpc/rpc_common.h"
+#include "librpc/wsp/wsp_util.h"
+#include "rpc_client/cli_pipe.h"
+#include "rpc_client/wsp_cli.h"
+#include "libcli/wsp/wsp_aqs.h"
+#include "librpc/gen_ndr/ndr_wsp.h"
+#include "librpc/gen_ndr/ndr_wsp_data.h"
+#include "dcerpc.h"
+
+#define WIN_VERSION_64 0x10000
+
+/* send connectin message */
+static NTSTATUS wsp_connect(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ const char* clientmachine,
+ const char* clientuser,
+ const char* server,
+ bool *is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ uint32_t client_ver;
+ uint32_t server_ver;
+ DATA_BLOB unread = data_blob_null;
+ NTSTATUS status;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!init_connectin_request(local_ctx, request,
+ clientmachine, clientuser, server)) {
+ DBG_ERR("Failed in initialise connection message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = wsp_request_response(local_ctx, wsp_ctx,
+ request, response, &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ client_ver = request->message.cpmconnect.iclientversion;
+ server_ver = response->message.cpmconnect.server_version;
+ *is_64bit =
+ (server_ver & WIN_VERSION_64)
+ && (client_ver & WIN_VERSION_64);
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS create_query(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ uint32_t limit,
+ t_select_stmt *select,
+ uint32_t *single_cursor)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;;
+ }
+
+ if (!create_querysearch_request(ctx, request, select)) {
+ DBG_ERR("error setting up query request message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit;
+
+ status = wsp_request_response(local_ctx,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ if (unread.length == 4) {
+ *single_cursor = IVAL(unread.data, 0);
+ }
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS create_bindings(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ t_select_stmt *select,
+ uint32_t cursor,
+ struct wsp_cpmsetbindingsin *bindings_out,
+ bool is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+
+ request = talloc_zero(ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (!create_setbindings_request(ctx,
+ request,
+ select,
+ cursor,
+ is_64bit)) {
+ DBG_ERR("Failed to create setbindings message\n");
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ status = wsp_request_response(ctx,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ *bindings_out = request->message.cpmsetbindings;
+ }
+
+out:
+ data_blob_free(&unread);
+ return status;
+}
+
+static NTSTATUS create_querystatusex(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ uint32_t cursor,
+ uint32_t *nrows)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ struct wsp_cpmgetquerystatusexin *statusexin = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ TALLOC_CTX *local_ctx = talloc_new(ctx);
+
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ request = talloc_zero(local_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ response = talloc_zero(local_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ statusexin = &request->message.cpmgetquerystatusex;
+
+ request->header.msg = CPMGETQUERYSTATUSEX;
+ statusexin->hcursor = cursor;
+ statusexin->bmk = 0xfffffffc;
+ status = wsp_request_response(local_ctx,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (NT_STATUS_IS_OK(status)) {
+ *nrows = response->message.cpmgetquerystatusex.resultsfound;
+ }
+
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(local_ctx);
+ return status;
+}
+
+static NTSTATUS print_rowsreturned(
+ TALLOC_CTX *ctx,
+ DATA_BLOB *buffer,
+ bool is_64bit,
+ bool disp_all_cols,
+ struct wsp_cpmsetbindingsin *bindings,
+ uint32_t cbreserved,
+ uint64_t address,
+ uint32_t rowsreturned,
+ uint32_t *rows_processed)
+{
+ NTSTATUS status;
+ uint32_t row = 0;
+ TALLOC_CTX *local_ctx = NULL;
+ struct wsp_cbasestoragevariant **rowsarray = NULL;
+ enum ndr_err_code err;
+
+ local_ctx = talloc_init("results");
+ if (local_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ rowsarray = talloc_zero_array(local_ctx,
+ struct wsp_cbasestoragevariant*,
+ rowsreturned);
+ if (rowsarray == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ err = extract_rowsarray(rowsarray,
+ buffer,
+ is_64bit,
+ bindings,
+ cbreserved,
+ address,
+ rowsreturned,
+ rowsarray);
+ if (err) {
+ DBG_ERR("failed to extract rows from getrows response\n");
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+
+ for(row = 0; row < rowsreturned; row++) {
+ TALLOC_CTX *row_ctx = NULL;
+ const char *col_str = NULL;
+
+ row_ctx = talloc_init("row");
+ if (row_ctx == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (disp_all_cols) {
+ int i;
+ for (i = 0; i < bindings->ccolumns; i++){
+ col_str =
+ variant_as_string(
+ row_ctx,
+ &rowsarray[row][i],
+ true);
+ if (col_str) {
+ printf("%s%s",
+ i ? ", " : "", col_str);
+ } else {
+ printf("%sN/A",
+ i ? ", " : "");
+ }
+ }
+ } else {
+ col_str = variant_as_string(
+ row_ctx,
+ &rowsarray[row][0],
+ true);
+ printf("%s", col_str);
+ }
+ printf("\n");
+ TALLOC_FREE(row_ctx);
+ }
+ status = NT_STATUS_OK;
+out:
+ TALLOC_FREE(local_ctx);
+ *rows_processed = row;
+ return status;
+}
+
+static NTSTATUS create_getrows(TALLOC_CTX *ctx,
+ struct wsp_client_ctx *wsp_ctx,
+ struct wsp_cpmsetbindingsin *bindings,
+ uint32_t cursor,
+ uint32_t nrows,
+ bool disp_all_cols,
+ bool is_64bit)
+{
+ struct wsp_request *request = NULL;
+ struct wsp_response *response = NULL;
+ NTSTATUS status;
+ DATA_BLOB unread = data_blob_null;
+ uint32_t bmk = 0xfffffffc;
+ uint32_t skip = 0;
+ uint32_t total_rows = 0;
+ uint32_t INITIAL_ROWS = 32;
+ uint32_t requested_rows = INITIAL_ROWS;
+ uint32_t rows_printed;
+ uint64_t baseaddress;
+ uint32_t offset_lowbits = 0xdeabd860;
+ uint32_t offset_hibits = 0xfeeddeaf;
+
+ TALLOC_CTX *row_ctx;
+ bool loop_again;
+
+ do {
+ row_ctx = talloc_new(NULL);
+ if (!row_ctx) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ request = talloc_zero(row_ctx, struct wsp_request);
+ if (!request) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ response = talloc_zero(row_ctx, struct wsp_response);
+ if (!response) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ create_seekat_getrows_request(request,
+ request,
+ cursor,
+ bmk,
+ skip,
+ requested_rows,
+ 40,
+ offset_lowbits,
+ bindings->brow,
+ 0);
+
+ if (is_64bit) {
+ /*
+ * MS-WSP 2.2.2
+ * ulreservered holds the high 32-bits part of
+ * a 64-bit offset if 64-bit offsets are being used.
+ */
+ request->header.ulreserved2 = offset_hibits;
+ baseaddress = request->header.ulreserved2;
+ baseaddress <<= 32;
+ baseaddress += offset_lowbits;
+ } else {
+ baseaddress = offset_lowbits;
+ }
+
+ status = wsp_request_response(request,
+ wsp_ctx,
+ request,
+ response,
+ &unread);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ total_rows += response->message.cpmgetrows.rowsreturned;
+ if (response->message.cpmgetrows.rowsreturned
+ != requested_rows) {
+ uint32_t rowsreturned =
+ response->message.cpmgetrows.rowsreturned;
+ if (response->message.cpmgetrows.etype == EROWSEEKAT) {
+ struct wsp_cpmgetrowsout *resp;
+ struct wsp_crowseekat *seekat;
+ resp = &response->message.cpmgetrows;
+ seekat =
+ &resp->seekdescription.crowseekat;
+ bmk = seekat->bmkoffset;
+ skip = seekat->cskip;
+ } else {
+ bmk = 0xfffffffc;
+ skip = total_rows;
+ }
+ requested_rows = requested_rows - rowsreturned;
+ } else {
+ requested_rows = INITIAL_ROWS;
+ bmk = 0xfffffffc;
+ skip = total_rows;
+ }
+
+ if (response->message.cpmgetrows.rowsreturned) {
+ status = print_rowsreturned(row_ctx, &unread,
+ is_64bit,
+ disp_all_cols,
+ bindings, 40,
+ baseaddress,
+ response->message.cpmgetrows.rowsreturned,
+ &rows_printed);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+ data_blob_free(&unread);
+ }
+
+ /*
+ * response is a talloc child of row_ctx so we need to
+ * assign loop_again before we delete row_ctx
+ */
+ loop_again = response->message.cpmgetrows.rowsreturned;
+
+ TALLOC_FREE(row_ctx);
+ if (nrows && total_rows > nrows) {
+ DBG_ERR("Something is wrong, results returned %d "
+ "exceed expected number of results %d\n",
+ total_rows, nrows);
+ status = NT_STATUS_UNSUCCESSFUL;
+ goto out;
+ }
+ } while (loop_again);
+out:
+ data_blob_free(&unread);
+ TALLOC_FREE(row_ctx);
+ return status;
+}
+
+const char *default_column = "System.ItemUrl";
+
+static bool is_valid_kind(const char *kind)
+{
+ const char* kinds[] = {"calendar",
+ "communication",
+ "contact",
+ "document",
+ "email",
+ "feed",
+ "folder",
+ "game",
+ "instantMessage",
+ "journal",
+ "link",
+ "movie",
+ "music",
+ "note",
+ "picture",
+ "program",
+ "recordedtv",
+ "searchfolder",
+ "task",
+ "video",
+ "webhistory"};
+ char* search_kind = NULL;
+ int i;
+ bool found = false;
+
+ search_kind = strlower_talloc(NULL, kind);
+ if (search_kind == NULL) {
+ DBG_ERR("couldn't convert %s to lower case\n",
+ kind);
+ return NULL;
+ }
+
+ for (i=0; i<ARRAY_SIZE(kinds); i++) {
+ if (strequal(search_kind, kinds[i])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ DBG_ERR("Invalid kind %s\n", kind);
+ }
+ TALLOC_FREE(search_kind);
+ return found;
+}
+
+static char * build_default_sql(TALLOC_CTX *ctx,
+ const char *kind,
+ const char *phrase,
+ const char *location)
+{
+ char *sql = NULL;
+ /* match what windows clients do */
+ sql = talloc_asprintf(ctx,
+ "Scope:\"%s\" AND NOT System.Shell.SFGAOFlagsStrings:hidden"
+ " AND NOT System.Shell.OmitFromView:true", location);
+
+ if (kind) {
+ if (!is_valid_kind(kind)) {
+ return NULL;
+ }
+ sql = talloc_asprintf(ctx, "System.Kind:%s AND %s",
+ kind, sql);
+ }
+
+ if (phrase) {
+ sql = talloc_asprintf(ctx,
+ "All:$=\"%s\" OR All:$<\"%s\""
+ " AND %s", phrase, phrase, sql);
+ }
+ sql = talloc_asprintf(ctx, "SELECT %s"
+ " WHERE %s", default_column, sql);
+ return sql;
+}
+
+int main(int argc, char **argv)
+{
+ int opt;
+ int result = 0;
+ NTSTATUS status = NT_STATUS_OK;
+ poptContext pc;
+ char* server = NULL;
+ char* share = NULL;
+ char* path = NULL;
+ char* location = NULL;
+ char* query = NULL;
+ bool custom_query = false;
+ const char* phrase = NULL;
+ const char* kind = NULL;
+ uint32_t limit = 500;
+ uint32_t nrows = 0;
+ struct wsp_cpmsetbindingsin bindings_used = {0};
+ bool is_64bit = false;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "limit",
+ 0,
+ POPT_ARG_INT,
+ &limit,
+ 0,
+ "limit results",
+ "default is 500, specifying 0 means unlimited" },
+ { "search",
+ 0,
+ POPT_ARG_STRING,
+ &phrase,
+ 0,
+ "Search phrase",
+ "phrase" },
+ { "kind", 0, POPT_ARG_STRING, &kind, 0,
+ "Kind of thing to search for [Calendar|Communication|"
+ "Contact|Document|Email|Feed|Folder|Game|"
+ "InstantMessage|Journal|Link|Movie|Music|Note|Picture|"
+ "Program|RecordedTV|SearchFolder|Task|Video"
+ "|WebHistory]",
+ "kind" },
+ { "query",
+ 0,
+ POPT_ARG_STRING,
+ &query,
+ 0,
+ "specify a more complex query",
+ "query" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct tevent_context *ev_ctx
+ = samba_tevent_context_init(talloc_tos());
+ uint32_t cursor = 0;
+ struct wsp_client_ctx *wsp_ctx = NULL;
+ t_select_stmt *select_stmt = NULL;
+ const char **const_argv = discard_const_p(const char *, argv);
+ struct dcerpc_binding_handle *h = NULL;
+ struct cli_state *c = NULL;
+ uint32_t flags = CLI_FULL_CONNECTION_IPC;
+ bool ok;
+
+ ok = samba_cmdline_init(frame,
+ SAMBA_CMDLINE_CONFIG_CLIENT,
+ false /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to set up cmdline parser\n");
+ result = -1;
+ goto out;
+ }
+
+ pc = samba_popt_get_context("wspsearch",
+ argc,
+ const_argv,
+ long_options,
+ 0);
+ poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) ;
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ result = -1;
+ goto out;
+ }
+
+ path = talloc_strdup(talloc_tos(), poptGetArg(pc));
+ if (!path) {
+ DBG_ERR("Invalid argument\n");
+ result = -1;
+ goto out;
+ }
+
+ string_replace(path,'/','\\');
+ server = talloc_strdup(talloc_tos(), path+2);
+ if (!server) {
+ DBG_ERR("Invalid argument\n");
+ return -1;
+ }
+
+ if (server) {
+ /*
+ * if we specify --query then we don't need actually need the
+ * share part, if it is specified then we don't care as we
+ * expect the scope to be part of the query (and if it isn't
+ * then it will probably fail anyway)
+ */
+ share = strchr_m(server,'\\');
+ if (!query && !share) {
+ DBG_ERR("Invalid argument\n");
+ return -1;
+ }
+ if (share) {
+ *share = 0;
+ share++;
+ }
+ }
+
+ DBG_INFO("server name is %s\n", server ? server : "N/A");
+ DBG_INFO("share name is %s\n", share ? share : "N/A");
+ DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A");
+ DBG_INFO("search kind is %s\n", kind ? kind : "N/A");
+
+ if (!query && (kind == NULL && phrase == NULL)) {
+ poptPrintUsage(pc, stderr, 0);
+ result = -1;
+ goto out;
+ }
+
+ if (!query) {
+ location = talloc_asprintf(talloc_tos(),
+ "FILE://%s/%s", server, share);
+ query = build_default_sql(talloc_tos(), kind, phrase, location);
+ if (!query) {
+ result = -1;
+ goto out;
+ }
+ } else {
+ custom_query = true;
+ }
+
+ printf("custom_query %d\n", custom_query);
+ select_stmt = get_wsp_sql_tree(query);
+
+ poptFreeContext(pc);
+
+ if (select_stmt == NULL) {
+ DBG_ERR("query failed\n");
+ result = -1;
+ goto out;
+ }
+
+ if (select_stmt->cols == NULL) {
+ select_stmt->cols = talloc_zero(select_stmt, t_col_list);
+ if (select_stmt->cols == NULL) {
+ DBG_ERR("out of memory\n");
+ result = -1;
+ goto out;
+ }
+ select_stmt->cols->num_cols = 1;
+ select_stmt->cols->cols =
+ talloc_zero_array(select_stmt->cols, char*, 1);
+ if (select_stmt->cols->cols == NULL) {
+ DBG_ERR("out of memory\n");
+ result = -1;
+ goto out;
+ }
+ select_stmt->cols->cols[0] =
+ talloc_strdup(select_stmt->cols, default_column);
+ }
+
+ status = cli_full_connection_creds(&c,
+ lp_netbios_name(),
+ server,
+ NULL,
+ 0,
+ "IPC$",
+ "IPC",
+ samba_cmdline_get_creds(),
+ flags);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to IPC$: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ status = wsp_server_connect(talloc_tos(),
+ server,
+ ev_ctx,
+ samba_cmdline_get_lp_ctx(),
+ samba_cmdline_get_creds(),
+ c,
+ &wsp_ctx);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to wsp: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ h = get_wsp_pipe(wsp_ctx);
+ if (h == NULL) {
+ DBG_ERR("Failed to communicate with server, no pipe\n");
+ result = -1;
+ goto out;
+ }
+
+ dcerpc_binding_handle_set_timeout(h,
+ DCERPC_REQUEST_TIMEOUT * 1000);
+
+ /* connect */
+ DBG_INFO("sending connect\n");
+ status = wsp_connect(talloc_tos(),
+ wsp_ctx,
+ lpcfg_netbios_name(samba_cmdline_get_lp_ctx()),
+ cli_credentials_get_username(
+ samba_cmdline_get_creds()),
+ server,
+ &is_64bit);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to connect to wsp: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ DBG_INFO("sending query\n");
+
+ status = create_query(talloc_tos(),
+ wsp_ctx,
+ limit,
+ select_stmt,
+ &cursor);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to send query: %s)\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ DBG_INFO("sending createbindings\n");
+ /* set bindings */
+ status = create_bindings(talloc_tos(),
+ wsp_ctx,
+ select_stmt,
+ cursor,
+ &bindings_used,
+ is_64bit);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("failed to setbindings: %s)\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+
+ status = create_querystatusex(talloc_tos(),
+ wsp_ctx,
+ bindings_used.hcursor,
+ &nrows);
+ if (!nrows) {
+ result = 0;
+ DBG_ERR("no results found\n");
+ goto out;
+ }
+
+ printf("found %d results, returning %d \n",
+ nrows,
+ limit ? MIN(nrows, limit) : nrows);
+ status = create_getrows(talloc_tos(),
+ wsp_ctx,
+ &bindings_used,
+ bindings_used.hcursor,
+ limit ? MIN(nrows, limit) : nrows,
+ custom_query,
+ is_64bit);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to retrieve rows, error: %s\n",
+ nt_errstr(status));
+ result = -1;
+ goto out;
+ }
+ result = 0;
+out:
+ TALLOC_FREE(frame);
+ return result;
+}