From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- source3/utils/async-tracker.c | 315 ++ source3/utils/clirap2.c | 2552 +++++++++ source3/utils/clirap2.h | 80 + source3/utils/conn_tdb.c | 173 + source3/utils/conn_tdb.h | 45 + source3/utils/dbwrap_tool.c | 593 ++ source3/utils/dbwrap_torture.c | 366 ++ source3/utils/destroy_netlogon_creds_cli.c | 136 + source3/utils/eventlogadm.c | 506 ++ source3/utils/interact.c | 135 + source3/utils/interact.h | 36 + source3/utils/log2pcaphex.c | 408 ++ source3/utils/mdsearch.c | 260 + source3/utils/mvxattr.c | 227 + source3/utils/net.c | 1450 +++++ source3/utils/net.h | 208 + source3/utils/net_ads.c | 4184 ++++++++++++++ source3/utils/net_ads_gpo.c | 428 ++ source3/utils/net_ads_join_dns.c | 342 ++ source3/utils/net_afs.c | 127 + source3/utils/net_afs.h | 29 + source3/utils/net_cache.c | 652 +++ source3/utils/net_conf.c | 1305 +++++ source3/utils/net_conf_util.c | 69 + source3/utils/net_conf_util.h | 33 + source3/utils/net_dns.c | 224 + source3/utils/net_dns.h | 44 + source3/utils/net_dom.c | 385 ++ source3/utils/net_eventlog.c | 275 + source3/utils/net_file.c | 57 + source3/utils/net_g_lock.c | 267 + source3/utils/net_group.c | 68 + source3/utils/net_groupmap.c | 1023 ++++ source3/utils/net_help.c | 69 + source3/utils/net_help_common.c | 95 + source3/utils/net_help_common.h | 49 + source3/utils/net_idmap.c | 1413 +++++ source3/utils/net_idmap_check.c | 974 ++++ source3/utils/net_idmap_check.h | 48 + source3/utils/net_join.c | 55 + source3/utils/net_lookup.c | 542 ++ source3/utils/net_notify.c | 199 + source3/utils/net_offlinejoin.c | 600 ++ source3/utils/net_printing.c | 592 ++ source3/utils/net_proto.h | 488 ++ source3/utils/net_rap.c | 1386 +++++ source3/utils/net_registry.c | 1732 ++++++ source3/utils/net_registry_check.c | 1324 +++++ source3/utils/net_registry_check.h | 52 + source3/utils/net_registry_util.c | 177 + source3/utils/net_registry_util.h | 41 + source3/utils/net_rpc.c | 8408 ++++++++++++++++++++++++++++ source3/utils/net_rpc_audit.c | 540 ++ source3/utils/net_rpc_conf.c | 2483 ++++++++ source3/utils/net_rpc_printer.c | 2624 +++++++++ source3/utils/net_rpc_registry.c | 2126 +++++++ source3/utils/net_rpc_rights.c | 798 +++ source3/utils/net_rpc_samsync.c | 257 + source3/utils/net_rpc_service.c | 1138 ++++ source3/utils/net_rpc_sh_acct.c | 489 ++ source3/utils/net_rpc_shell.c | 306 + source3/utils/net_rpc_trust.c | 735 +++ source3/utils/net_sam.c | 2308 ++++++++ source3/utils/net_serverid.c | 703 +++ source3/utils/net_share.c | 75 + source3/utils/net_status.c | 246 + source3/utils/net_tdb.c | 105 + source3/utils/net_time.c | 262 + source3/utils/net_user.c | 67 + source3/utils/net_usershare.c | 1172 ++++ source3/utils/net_util.c | 614 ++ source3/utils/net_vfs.c | 469 ++ source3/utils/net_witness.c | 2361 ++++++++ source3/utils/netlookup.c | 218 + source3/utils/nmblookup.c | 468 ++ source3/utils/ntlm_auth.c | 2856 ++++++++++ source3/utils/ntlm_auth.h | 26 + source3/utils/ntlm_auth_diagnostics.c | 724 +++ source3/utils/ntlm_auth_proto.h | 51 + source3/utils/passwd_proto.h | 31 + source3/utils/passwd_util.c | 80 + source3/utils/pdbedit.c | 1414 +++++ source3/utils/profiles.c | 365 ++ source3/utils/py_net.c | 369 ++ source3/utils/py_net.h | 26 + source3/utils/regedit.c | 835 +++ source3/utils/regedit.h | 77 + source3/utils/regedit_dialog.c | 2328 ++++++++ source3/utils/regedit_dialog.h | 240 + source3/utils/regedit_hexedit.c | 563 ++ source3/utils/regedit_hexedit.h | 49 + source3/utils/regedit_list.c | 591 ++ source3/utils/regedit_list.h | 82 + source3/utils/regedit_samba3.c | 244 + source3/utils/regedit_treeview.c | 705 +++ source3/utils/regedit_treeview.h | 89 + source3/utils/regedit_valuelist.c | 496 ++ source3/utils/regedit_valuelist.h | 72 + source3/utils/regedit_wrap.c | 143 + source3/utils/sharesec.c | 613 ++ source3/utils/smbcacls.c | 2614 +++++++++ source3/utils/smbcontrol.c | 1870 +++++++ source3/utils/smbcquotas.c | 832 +++ source3/utils/smbfilter.c | 356 ++ source3/utils/smbget.c | 1073 ++++ source3/utils/smbpasswd.c | 678 +++ source3/utils/smbtree.c | 295 + source3/utils/status.c | 1241 ++++ source3/utils/status.h | 44 + source3/utils/status_json.c | 1386 +++++ source3/utils/status_json.h | 77 + source3/utils/status_json_dummy.c | 101 + source3/utils/status_profile.c | 381 ++ source3/utils/status_profile.h | 30 + source3/utils/status_profile_dummy.c | 35 + source3/utils/testparm.c | 1060 ++++ source3/utils/wscript_build | 364 ++ source3/utils/wspsearch.c | 842 +++ 118 files changed, 82158 insertions(+) create mode 100644 source3/utils/async-tracker.c create mode 100644 source3/utils/clirap2.c create mode 100644 source3/utils/clirap2.h create mode 100644 source3/utils/conn_tdb.c create mode 100644 source3/utils/conn_tdb.h create mode 100644 source3/utils/dbwrap_tool.c create mode 100644 source3/utils/dbwrap_torture.c create mode 100644 source3/utils/destroy_netlogon_creds_cli.c create mode 100644 source3/utils/eventlogadm.c create mode 100644 source3/utils/interact.c create mode 100644 source3/utils/interact.h create mode 100644 source3/utils/log2pcaphex.c create mode 100644 source3/utils/mdsearch.c create mode 100644 source3/utils/mvxattr.c create mode 100644 source3/utils/net.c create mode 100644 source3/utils/net.h create mode 100644 source3/utils/net_ads.c create mode 100644 source3/utils/net_ads_gpo.c create mode 100644 source3/utils/net_ads_join_dns.c create mode 100644 source3/utils/net_afs.c create mode 100644 source3/utils/net_afs.h create mode 100644 source3/utils/net_cache.c create mode 100644 source3/utils/net_conf.c create mode 100644 source3/utils/net_conf_util.c create mode 100644 source3/utils/net_conf_util.h create mode 100644 source3/utils/net_dns.c create mode 100644 source3/utils/net_dns.h create mode 100644 source3/utils/net_dom.c create mode 100644 source3/utils/net_eventlog.c create mode 100644 source3/utils/net_file.c create mode 100644 source3/utils/net_g_lock.c create mode 100644 source3/utils/net_group.c create mode 100644 source3/utils/net_groupmap.c create mode 100644 source3/utils/net_help.c create mode 100644 source3/utils/net_help_common.c create mode 100644 source3/utils/net_help_common.h create mode 100644 source3/utils/net_idmap.c create mode 100644 source3/utils/net_idmap_check.c create mode 100644 source3/utils/net_idmap_check.h create mode 100644 source3/utils/net_join.c create mode 100644 source3/utils/net_lookup.c create mode 100644 source3/utils/net_notify.c create mode 100644 source3/utils/net_offlinejoin.c create mode 100644 source3/utils/net_printing.c create mode 100644 source3/utils/net_proto.h create mode 100644 source3/utils/net_rap.c create mode 100644 source3/utils/net_registry.c create mode 100644 source3/utils/net_registry_check.c create mode 100644 source3/utils/net_registry_check.h create mode 100644 source3/utils/net_registry_util.c create mode 100644 source3/utils/net_registry_util.h create mode 100644 source3/utils/net_rpc.c create mode 100644 source3/utils/net_rpc_audit.c create mode 100644 source3/utils/net_rpc_conf.c create mode 100644 source3/utils/net_rpc_printer.c create mode 100644 source3/utils/net_rpc_registry.c create mode 100644 source3/utils/net_rpc_rights.c create mode 100644 source3/utils/net_rpc_samsync.c create mode 100644 source3/utils/net_rpc_service.c create mode 100644 source3/utils/net_rpc_sh_acct.c create mode 100644 source3/utils/net_rpc_shell.c create mode 100644 source3/utils/net_rpc_trust.c create mode 100644 source3/utils/net_sam.c create mode 100644 source3/utils/net_serverid.c create mode 100644 source3/utils/net_share.c create mode 100644 source3/utils/net_status.c create mode 100644 source3/utils/net_tdb.c create mode 100644 source3/utils/net_time.c create mode 100644 source3/utils/net_user.c create mode 100644 source3/utils/net_usershare.c create mode 100644 source3/utils/net_util.c create mode 100644 source3/utils/net_vfs.c create mode 100644 source3/utils/net_witness.c create mode 100644 source3/utils/netlookup.c create mode 100644 source3/utils/nmblookup.c create mode 100644 source3/utils/ntlm_auth.c create mode 100644 source3/utils/ntlm_auth.h create mode 100644 source3/utils/ntlm_auth_diagnostics.c create mode 100644 source3/utils/ntlm_auth_proto.h create mode 100644 source3/utils/passwd_proto.h create mode 100644 source3/utils/passwd_util.c create mode 100644 source3/utils/pdbedit.c create mode 100644 source3/utils/profiles.c create mode 100644 source3/utils/py_net.c create mode 100644 source3/utils/py_net.h create mode 100644 source3/utils/regedit.c create mode 100644 source3/utils/regedit.h create mode 100644 source3/utils/regedit_dialog.c create mode 100644 source3/utils/regedit_dialog.h create mode 100644 source3/utils/regedit_hexedit.c create mode 100644 source3/utils/regedit_hexedit.h create mode 100644 source3/utils/regedit_list.c create mode 100644 source3/utils/regedit_list.h create mode 100644 source3/utils/regedit_samba3.c create mode 100644 source3/utils/regedit_treeview.c create mode 100644 source3/utils/regedit_treeview.h create mode 100644 source3/utils/regedit_valuelist.c create mode 100644 source3/utils/regedit_valuelist.h create mode 100644 source3/utils/regedit_wrap.c create mode 100644 source3/utils/sharesec.c create mode 100644 source3/utils/smbcacls.c create mode 100644 source3/utils/smbcontrol.c create mode 100644 source3/utils/smbcquotas.c create mode 100644 source3/utils/smbfilter.c create mode 100644 source3/utils/smbget.c create mode 100644 source3/utils/smbpasswd.c create mode 100644 source3/utils/smbtree.c create mode 100644 source3/utils/status.c create mode 100644 source3/utils/status.h create mode 100644 source3/utils/status_json.c create mode 100644 source3/utils/status_json.h create mode 100644 source3/utils/status_json_dummy.c create mode 100644 source3/utils/status_profile.c create mode 100644 source3/utils/status_profile.h create mode 100644 source3/utils/status_profile_dummy.c create mode 100644 source3/utils/testparm.c create mode 100644 source3/utils/wscript_build create mode 100644 source3/utils/wspsearch.c (limited to 'source3/utils') 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 + * Copyright (C) 2015, Noel Power + * Copyright (C) 2016, Ralph Boehme + * + * 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 +#include +#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 . +*/ + +/*****************************************************/ +/* */ +/* 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; irap_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; irap_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; irap_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; ipasswrd), + 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;irap_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;ishare_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;irap_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;(jrap_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;irap_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;irap_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. +*/ + +#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 . +*/ + +#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 . +*/ + +/* 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 + Copyright (C) 2011 Bjoern Baumbach + + 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 . +*/ + +#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] [ [ " + "[]]]\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 . +*/ + +#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 +#include +#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. + */ + +#include "includes.h" +#include "system/filesys.h" +#include +#include +#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 . + */ + + +#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 \t\t\t\t\tWrites records to eventlog from STDIN\n" ); + printf( " -o addsource \tAdds the specified source & DLL eventlog registry entry\n" ); + printf( " -o dump \t\t\t\t\tDump stored eventlog entries on STDOUT\n" ); + printf( "\nMiscellaneous options:\n" ); + printf( " -s \t\t\t\t\t\t\tUse configuration file .\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\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//Sources string (Creating if necessary) + need to add KEY of source to KEY_EVENTLOG// */ + + 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// 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( "\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 . + */ + +/** + * @brief Functions to interact with an user. + * @author Gregor Beck + * @date Aug 2011 + * + */ + +#include "includes.h" +#include "system/filesys.h" + +#include "interact.h" + +#include + +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 . + */ + +/** + * @brief Functions to interact with an user. + * @author Gregor Beck + * @date Aug 2011 + */ + +#ifndef __INTERACT_H +#define __INTERACT_H +#include + +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 . +*/ + +#include "includes.h" +#include + +/* We don't care about the paranoid malloc checker in this standalone + program */ +#undef malloc + +#include + +int quiet = 0; +int hexformat = 0; + +#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa)) + +#include +#include +#include +#include +#include + +#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, "[ []]"); + + + 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 + * + * 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] \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 . +*/ + +#include "includes.h" +#include "system/filesys.h" +#include +#include + +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 . */ + +/*****************************************************/ +/* */ +/* 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; iopt_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; ido_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 . */ + +/* + * 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 +#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 . +*/ + +#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 +#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; imsg_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 \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 " +"\n\tsearch for a printer in the directory\n" +"\nnet ads printer info " +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish " +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove " +"\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 [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 [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 \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 \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 \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 \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 ...]\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 ...]\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; iopt_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; ipac_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; ipac_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\" 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 \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 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 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 [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 \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 . +*/ + +#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 "), + _(" 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 "), + _(" 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 [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 \n" + " Delete a GPO link\n" + " \tContainer to delete GPO from\n" + " \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 "), + _(" 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 . +*/ + +#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 . +*/ + +#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 \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 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 \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 \n" + " Import kefile from .") + }, + { + "impersonate", + net_afs_impersonate, + NET_TRANSPORT_LOCAL, + N_("Get a user token"), + N_("net afs impersonate \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 . +*/ + +#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 . +*/ + +#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(), "", + (int)value.length); + datastr = datastr_free; + if (datastr == NULL) { + datastr = ""; + } + } + + 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 " + "\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 \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 \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 \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 \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 \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 \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 \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 . + */ + +/* + * 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] " + "[]\n" + "\t[--test|-T] testmode - do not act, just print " + "what would be done\n" + "\t only import service , " + "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 \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 " + "[writeable={y|N} [guest_ok={y|N} []]]\n" + "\t the new share name.\n" + "\t 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 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 \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
\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
\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
\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
\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
[]*\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
\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 . + */ + +/* + * 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 . + */ + +#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 . +*/ + +#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. +*/ + +/* 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 . +*/ + +#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 " + " " + " \n Join a remote machine\n")); + d_printf("%s\n%s", + _("Usage:"), + _("net dom unjoin " + " \n" + " Unjoin a remote machine\n")); + d_printf("%s\n%s", + _("Usage:"), + _("net dom renamecomputer " + " " + " \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; iopt_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; iopt_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; iopt_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 " + " \n" + " Join a remote machine") + }, + { + "unjoin", + net_dom_unjoin, + NET_TRANSPORT_LOCAL, + N_("Unjoin a remote machine"), + N_("net dom unjoin " + " \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 " + " " + "\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 . + */ + +#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 \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 \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; idisplay_usage) { + d_fprintf(stderr, + "%s\nnet eventlog export \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 . +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_file_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net [] file [misc. options] [targets]\n" + "\tlists all open files on file server\n")); + d_printf(_("net [] file USER " + "[misc. options] [targets]" + "\n\tlists all files opened by username on file server\n")); + d_printf(_("net [] file CLOSE [misc. options] [targets]\n" + "\tcloses specified file on target server\n")); + d_printf(_("net [rap] file INFO [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 . + */ + +#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 " + "\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\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 \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 \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 . +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_group_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net [] 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 [] group DELETE " + "[misc. options] [targets]" + "\n\tDelete specified group\n")); + d_printf(_("\nnet [] group ADD [-C comment] " + "[-c container] [misc. options] [targets]\n" + "\tCreate specified group\n")); + d_printf(_("\nnet rpc group MEMBERS \n\tList Group Members\n\n")); + d_printf(_("\nnet rpc group ADDMEM \n" + "\tAdd Group Members\n\n")); + d_printf(_("\nnet rpc group DELMEM \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=\tdescriptive comment " + "(for add only)\n")); + d_printf(_("\t-c or --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 . + */ + + +#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|sid=}" + " unixgroup= " + "[type=] " + "[ntgroup=] " + "[comment=]"); + + 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; isid_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=|sid=} " + "[comment=] " + "[unixgroup=] " + "[type=]"); + + if (c->display_usage) { + d_printf("%s\n%s\n", _("Usage:\n"), modify_usage_str); + return 0; + } + + /* get the options */ + for ( i=0; isid_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=|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 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; igid == -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 . +*/ + +#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 . +*/ + +#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=\t\t\tserver name\n")); + d_printf(_("\t-I|--ipaddress=\t\t\taddress of target server\n")); + d_printf(_("\t-w|--target-workgroup=\t\ttarget workgroup or domain\n")); + + d_printf("\n"); + d_printf(_("Valid misc options are:\n")); /* misc options */ + d_printf(_("\t-p|--port=\t\t\tconnection port on target\n")); + d_printf(_("\t--myname=\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=\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=\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 . +*/ + +#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 . +*/ + +#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=]]\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=] []\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=] \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=] |([ ])\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=] \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 \n" + " Delete ID mapping") + }, + { + "range", + net_idmap_autorid_delete_range, + NET_TRANSPORT_LOCAL, + N_("Delete a domain range mapping"), + N_("net idmap delete range |([ ])\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 \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" + " [] [--db=]\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 \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 " + " [--db=]\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 \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 [] [--db=]\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 [] [--db=]\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=]\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 \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=]]\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 . + */ + +/** + * @brief Check the idmap database. + * @author Gregor Beck + * @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, ""); +} + + +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 . + */ + +/** + * @brief Check the idmap database. + * @author Gregor Beck + * @date Mar 2011 + */ + +#ifndef NET_IDMAP_CHECK_H +#define NET_IDMAP_CHECK_H + +#include + +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 . +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_join_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet [] 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 . */ + +#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[#]\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; inum_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;iopt_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 \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 \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 " + " \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 + * + * 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 . + */ + +#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 " + "\n"); + return -1; + } + + ok = server_id_db_lookup_one(names_db, "notify-daemon", ¬ifyd); + 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 " + "\n"); + return -1; + } + + ok = server_id_db_lookup_one(names_db, "notify-daemon", ¬ifyd); + 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 ") + }, + { "trigger", + net_notify_trigger, + NET_TRANSPORT_LOCAL, + N_("Simulate a trigger action"), + N_("net notify trigger ") + }, + {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 . +*/ + +#include "includes.h" +#include "utils/net.h" +#include +#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=\t\t\t\tDefines AD Domain to join\n")); + d_printf(_("\tmachine_name=\t\tDefines the machine account name\n")); + d_printf(_("\tmachine_account_ou=\t\t\tDefines the machine account organizational unit DN\n")); + d_printf(_("\tdcname=\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=\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=\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 .\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=\t\t\tThe domain SID\n")); + d_printf(_("\tdomain_guid=\t\t\tThe domain GUID\n")); + d_printf(_("\tforest_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=\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= " + "--workgroup= domain_sid= domain_guid= " + "forest_name= -S -I " + "--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 parameter\n"); + return -1; + } + + if (domain_guid_str == NULL) { + d_printf("Please provide a valid parameter\n"); + return -1; + } + + if (forest_name == NULL) { + d_printf("Please provide a valid 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 . + */ + +#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] \n" + " %s\n", + _("Usage:"), + _("Dump formatted printer information of the tdb.")); + d_printf(_("Valid options:\n")); + d_printf(_(" encoding= 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] \n" + " %s\n", + _("Usage:"), + _("Migrate tdb printing files to new storage")); + d_printf(_("Valid options:\n")); + d_printf(_(" encoding= 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 . + */ + +#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 . */ + +#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 [misc. options] [targets] \n" + "\tor" + "\nnet rap session CLOSE [misc. options] [targets]" + "\n\tDeletes (closes) a session from specified client to server\n")); + d_printf(_( + "\nnet rap session INFO " + "\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 [] [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 [] [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 [misc. options] [targets]" + "\n\t Enumerate users in a group\n" + "\nnet rap groupmember DELETE [misc. options] " + "[targets]\n\t Delete specified user from specified group\n" + "\nnet rap groupmember ADD [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 [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 [service startup arguments]" + " [misc. options] [targets]" + "\n\tStart named service on remote server\n")); + d_printf(_("\nnet rap service STOP [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 [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 [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 . + */ + +#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 +#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 \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 \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 \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 \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 \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 " + "\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 " + " []+\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 " + "[]\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 \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 \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 \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 \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 [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 [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_opt] [out_opt]\n" + "net registry convert [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 ] [--wipe] []\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 while doing the check\n" + " -o|--output=\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=\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 \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 . + */ + +/** + * @brief Check the registry database. + * @author Gregor Beck + * @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 +#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 = \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 . + */ + +/** + * @brief Check the registry database. + * @author Gregor Beck + * @date Jun 2011 + */ + +#ifndef NET_REGISTRY_CHECK_H +#define NET_REGISTRY_CHECK_H + +#include + +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 . + */ + +#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(_("\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 . + */ + +#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 . */ + +#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 [%%password] \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 \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 \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 [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 [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 \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 \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; idesthost, + 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; iopt_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; isid, + &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; jnum_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; itoken->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; inum_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; inum_sids; + int i; + + for (i=0; isids[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; ibinding_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; inetapi_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 [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 \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 \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 " + "\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 \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 \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 \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 \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 \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 . */ + +#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 View configured Auditing policy setting\n")); + d_printf(_("net rpc audit set 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 . + */ + +/* + * 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 \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 " + "[writeable={y|N} [guest_ok={y|N} []]]\n" + "\t the new share name.\n" + "\t 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 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] " + "[]\n" + "\t[--test|-T] testmode - do not act, just print " + "what would be done\n" + "\t only import service , " + "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 \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 \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
\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 \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 \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 []*\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 \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 . +*/ +#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: \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 . */ + +#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 +#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; ibinding_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; itype = 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; ibinding_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 " + " []+\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 \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 \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 \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 \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 \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 \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; ibinding_handle; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry backup \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; inum_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; jvalues[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; jvalues[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(®path, "%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; inum_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 \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 \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 \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; ibinding_handle; + + if (argc < 2 || argc > 3 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry export [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 [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 . +*/ +#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; ibinding_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; ibinding_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 \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; ibinding_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 \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; idisplay_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 \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 \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 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 . +*/ + +#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 [] | [keytab] " + "[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 \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 . */ + +#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; ibinding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status \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 \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 \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 \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 \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 \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 " + " \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 \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 \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 \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 \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 \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 \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 \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 . +*/ +#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 \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 \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 \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 \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 \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 \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 \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 . + */ + + +#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 + +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 . */ + + +#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 +#include + +#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 . + */ + + +#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 \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 [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 [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 \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 \"\" \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; idisplay_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam policy show \"\"\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; idisplay_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; idisplay_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam rights grant ...\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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 \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 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 \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 . +*/ + +#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; iid2server_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 \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 ") + }, + {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 . +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_share_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet [] share [misc. options] [targets] \n" + "\tenumerates all exported resources (network shares) " + "on target server\n\n" + "net [] share ADD [misc. options] [targets]" + "\n\tadds a share from a server (makes the export active)\n\n" + "net [] share DELETE [misc. options] [targets]" + "\n\tdeletes a share from a server (makes the export inactive)\n\n" + "net [] share ALLOWEDUSERS [misc. options] [|- " + " [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 ." + "\n\n" + "net [] share MIGRATE FILES [misc. options] [targets]" + "\n\tMigrates files from remote to local server\n\n" + "net [] share MIGRATE SHARES [misc. options] [targets]" + "\n\tMigrates shares from remote to local server\n\n" + "net [] share MIGRATE SECURITY [misc. options] [targets]" + "\n\tMigrates share-ACLs from remote to local server\n\n" + "net [] share MIGRATE ALL [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=\tdescriptive comment (for add only)\n" + "\t-M or --maxusers=\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 . */ + +#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; inum_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 + * + * 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 . + */ + +#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 [ 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 ") + }, + {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 . +*/ +#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 . +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_user_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet [] user [misc. options] [targets]" + "\n\tList users\n\n")); + d_printf(_("net [] user DELETE [misc. options] [targets]" + "\n\tDelete specified user\n")); + d_printf(_("\nnet [] user INFO [misc. options] [targets]" + "\n\tList the domain groups of the specified user\n")); + d_printf(_("\nnet [] user ADD [password] [-c container] " + "[-F user flags] [misc. options]" + " [targets]\n\tAdd specified user\n")); + d_printf(_("\nnet [] user RENAME " + " [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=\tdescriptive comment " + "(for add only)\n")); + d_printf(_("\t-c or --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 . +*/ + +#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] [] [] []\n" + "\tAdds the specified share name for this user.\n" + "\t is the new share name.\n" + "\t is the path on the filesystem to export.\n" + "\t is the optional comment for the new share.\n" + "\t is an optional share acl in the format \"DOMAIN%cname:X,DOMAIN%cname:X,....\"\n" + "\t 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 \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 [] [] [] to " + "add or change a user defined share.\n" + "net usershare delete 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