diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source3/utils | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
118 files changed, 82158 insertions, 0 deletions
diff --git a/source3/utils/async-tracker.c b/source3/utils/async-tracker.c new file mode 100644 index 0000000..7b6c2c0 --- /dev/null +++ b/source3/utils/async-tracker.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2011, Nokia <ivan.frade@nokia.com> + * Copyright (C) 2015, Noel Power <nopower@suse.com> + * Copyright (C) 2016, Ralph Boehme <slow@samba.org.> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "includes.h" +#include "lib/util/debug.h" +#include "lib/cmdline/cmdline.h" +#include "param.h" +/* + * glib uses TRUE and FALSE which was redefined by "includes.h" to be + * unusable, undefine so glib can establish its own working + * replacement. + */ +#undef TRUE +#undef FALSE +#include <glib.h> +#include <libtracker-sparql/tracker-sparql.h> +#include "lib/tevent_glib_glue.h" + +enum loop_type {TEVENT_LOOP, GLIB_LOOP}; + +struct test_state { + enum loop_type loop_type; + TrackerSparqlConnection *connection; + GCancellable *cancellable; + GTimer *timer; + GMainLoop *loop; + struct tevent_context *ev; + struct tevent_glib_glue *glue; +}; + +static void cleanup(struct test_state *state) +{ + g_cancellable_cancel(state->cancellable); + g_object_unref(state->cancellable); + g_timer_destroy(state->timer); + if (state->connection != NULL) { + g_object_unref(state->connection); + state->connection = NULL; + } + if (state->loop_type == GLIB_LOOP) { + g_main_loop_quit(state->loop); + } else { + samba_tevent_glib_glue_quit(state->glue); + } +} + +static void cursor_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct test_state *state = talloc_get_type_abort( + user_data, struct test_state); + TrackerSparqlCursor *cursor = NULL; + GError *error = NULL; + gboolean more_results; + static gint i = 0; + + cursor = TRACKER_SPARQL_CURSOR(object); + more_results = tracker_sparql_cursor_next_finish(cursor, + res, + &error); + if (error) { + g_critical("Could not run cursor next: %s", error->message); + + if (cursor != NULL) { + g_object_unref(cursor); + } + + g_error_free(error); + cleanup(state); + return; + } + + if (!more_results) { + g_print("\n"); + g_print("\nAsync cursor next took: %.6f (for all %d results)\n", + g_timer_elapsed (state->timer, NULL), i); + + g_object_unref(cursor); + cleanup(state); + return; + } + + if (i++ < 5) { + int num_cols = tracker_sparql_cursor_get_n_columns(cursor); + int col; + + if (i == 1) { + g_print("Printing first 5 results:\n"); + } + for (col = 0; col < num_cols; col++) { + g_print(" %s ", tracker_sparql_cursor_get_string( + cursor, col, NULL)); + if (col == num_cols -1 ) { + g_print("\n"); + } + } + + if (i == 5) { + g_print(" ...\n"); + g_print(" Printing nothing for remaining results\n"); + } + } + + tracker_sparql_cursor_next_async(cursor, + state->cancellable, + cursor_cb, + state); +} + +static void query_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct test_state *state = talloc_get_type_abort( + user_data, struct test_state); + TrackerSparqlCursor *cursor = NULL; + GError *error = NULL; + + g_print("Async query took: %.6f\n", g_timer_elapsed(state->timer, NULL)); + + cursor = tracker_sparql_connection_query_finish( + TRACKER_SPARQL_CONNECTION(object), + res, + &error); + if (error) { + g_critical("Could not run query: %s", error->message); + + if (cursor) { + g_object_unref(cursor); + } + + g_error_free(error); + cleanup(state); + return; + } + + g_timer_start(state->timer); + + tracker_sparql_cursor_next_async(cursor, + state->cancellable, + cursor_cb, + state); +} + +static void connection_cb(GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + struct test_state *state = talloc_get_type_abort( + user_data, struct test_state); + GError *error = NULL; + + g_print("Async connection took: %.6f\n", + g_timer_elapsed(state->timer, NULL)); + + state->connection = tracker_sparql_connection_get_finish(res, &error); + if (error) { + g_critical("Could not connect: %s", error->message); + g_error_free(error); + cleanup(state); + return; + } + + g_timer_start(state->timer); + + tracker_sparql_connection_query_async( + state->connection, + "SELECT ?name nie:mimeType(?s) nfo:fileName(?s) " + "WHERE { {?s nie:url ?name}}", + state->cancellable, + query_cb, + state); +} + +static void debug_fn(void *private_data, + enum tevent_debug_level level, + const char *fmt, + va_list ap) +{ + dbgtext_va(fmt, ap); +} + +int main(int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx = NULL; + struct test_state *state = NULL; + int c; + poptContext pc; + bool ok; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "tevent", + .shortName = 't', + .argInfo = POPT_ARG_NONE, + .val = 'v', + .descrip = "Use tevent loop", + }, + { + .longName = "glib", + .shortName = 'g', + .argInfo = POPT_ARG_NONE, + .val = 'g', + .descrip = "Use glib loop", + }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + exit(1); + } + + state = talloc_zero(mem_ctx, struct test_state); + if (state == NULL) { + exit(1); + } + + state->loop_type = TEVENT_LOOP; + + smb_init_locale(); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + true /* require_smbconf */); + if (!ok) { + TALLOC_FREE(mem_ctx); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + TALLOC_FREE(mem_ctx); + exit(1); + } + + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) { + case 'g': + state->loop_type = GLIB_LOOP; + break; + case 't': + state->loop_type = TEVENT_LOOP; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(c)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (state->loop_type == GLIB_LOOP) { + state->loop = g_main_loop_new(NULL, false); + } else { + state->ev = tevent_context_init(mem_ctx); + if (CHECK_DEBUGLVL(10)) { + tevent_set_debug(state->ev, debug_fn, NULL); + } + state->glue = samba_tevent_glib_glue_create( + mem_ctx, state->ev, g_main_context_default()); + if (state->glue == NULL) { + printf("tevent_glib_glue_create failed\n"); + exit(1); + } + } + + state->timer = g_timer_new(); + state->cancellable = g_cancellable_new(); + + tracker_sparql_connection_get_async(state->cancellable, + connection_cb, + state); + + if (state->loop_type == GLIB_LOOP) { + printf("entering g_main_loop_run\n"); + g_main_loop_run(state->loop); + } else { + printf("entering tevent_loop_wait\n"); + tevent_loop_wait(state->ev); + + TALLOC_FREE(state->glue); + TALLOC_FREE(state->ev); + } + + TALLOC_FREE(mem_ctx); + poptFreeContext(pc); + + return 0; +} diff --git a/source3/utils/clirap2.c b/source3/utils/clirap2.c new file mode 100644 index 0000000..b2e5187 --- /dev/null +++ b/source3/utils/clirap2.c @@ -0,0 +1,2552 @@ +/* + Samba Unix/Linux SMB client library + More client RAP (SMB Remote Procedure Calls) functions + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2007 Jeremy Allison. jra@samba.org + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/*****************************************************/ +/* */ +/* Additional RAP functionality */ +/* */ +/* RAP is the original SMB RPC, documented */ +/* by Microsoft and X/Open in the 1990s and */ +/* supported by most SMB/CIFS servers although */ +/* it is unlikely that any one implementation */ +/* supports all RAP command codes since some */ +/* are quite obsolete and a few are specific */ +/* to a particular network operating system */ +/* */ +/* Although it has largely been replaced */ +/* for complex remote administration and management */ +/* (of servers) by the relatively newer */ +/* DCE/RPC based remote API (which better handles */ +/* large >64K data structures), there are many */ +/* important administrative and resource location */ +/* tasks and user tasks (e.g. password change) */ +/* that are performed via RAP. */ +/* */ +/* Although a few of the RAP calls are implemented */ +/* in the Samba client library already (clirap.c) */ +/* the new ones are in clirap2.c for easy patching */ +/* and integration and a corresponding header */ +/* file, rap.h, has been created. */ +/* */ +/* This is based on data from the CIFS spec */ +/* and the LAN Server and LAN Manager */ +/* Programming Reference books and published */ +/* RAP document and CIFS forum postings and */ +/* lots of trial and error */ +/* */ +/* Function names changed from API_ (as they are */ +/* in the CIFS specification) to RAP_ in order */ +/* to avoid confusion with other API calls */ +/* sent via DCE RPC */ +/* */ +/*****************************************************/ + +/*****************************************************/ +/* */ +/* cifsrap.c already includes support for: */ +/* */ +/* WshareEnum ( API number 0, level 1) */ +/* NetServerEnum2 (API num 104, level 1) */ +/* WWkstaUserLogon (132) */ +/* SamOEMchgPasswordUser2_P (214) */ +/* */ +/* cifsprint.c already includes support for: */ +/* */ +/* WPrintJobEnum (API num 76, level 2) */ +/* WPrintJobDel (API num 81) */ +/* */ +/*****************************************************/ + +#include "includes.h" +#include "libsmb/libsmb.h" +#include "../librpc/gen_ndr/rap.h" +#include "../librpc/gen_ndr/svcctl.h" +#include "clirap2.h" +#include "../libcli/smb/smbXcli_base.h" + +#define WORDSIZE 2 +#define DWORDSIZE 4 + +#define PUTBYTE(p,b) do {SCVAL(p,0,b); p++;} while(0) + +#define GETBYTE(p,b,endp) \ + do {\ + if (p+1 < endp) {\ + b = CVAL(p,0);\ + }\ + p++;\ + } while(0) + +#define PUTWORD(p,w) do {SSVAL(p,0,w); p += WORDSIZE;} while(0) + +#define GETWORD(p,w,endp) \ + do {\ + if (p+WORDSIZE < endp) {\ + w = SVAL(p,0);\ + }\ + p += WORDSIZE;\ + } while(0) + +#define PUTDWORD(p,d) do {SIVAL(p,0,d); p += DWORDSIZE;} while(0) + +#define GETDWORD(p,d,endp) \ + do {\ + if (p+DWORDSIZE < endp) {\ + d = IVAL(p,0);\ + }\ + p += DWORDSIZE;\ + } while(0) + +#define GETRES(p,endp) ((p && p+2 < endp) ? SVAL(p,0) : -1) + +/** + * Skip past some strings in a buffer - old version - no checks. + * **/ + +static char *push_skip_string(char *buf) +{ + buf += strlen(buf) + 1; + return(buf); +} + +/* put string s at p with max len n and increment p past string */ +#define PUTSTRING(p,s,n) \ + do {\ + push_ascii(p,s?s:"",n?n:256,STR_TERMINATE);\ + p = push_skip_string(p);\ + } while(0) + +/* put string s and p, using fixed len l, and increment p by l */ +#define PUTSTRINGF(p,s,l) \ + do {\ + push_ascii(p,s?s:"",l,STR_TERMINATE);\ + p += l;\ + } while (0) + +/* put string pointer at p, supplying offset o from rdata r, store */ +/* dword offset at p, increment p by 4 and o by length of s. This */ +/* means on the first call, you must calc the offset yourself! */ + +#define PUTSTRINGP(p,s,r,o) \ + do {\ + if (s) {\ + push_ascii(r+o,s,strlen(s)+1,STR_TERMINATE);\ + PUTDWORD(p,o);\ + o += strlen(s) + 1;\ + } else {\ + PUTDWORD(p,0);\ + }\ + }while(0); + +/* get asciiz string dest from src, return increment past string */ + +static size_t rap_getstring(TALLOC_CTX *ctx, char *src, char **dest, const char *endp) +{ + char *p1; + size_t len; + + *dest = NULL; + for (p1 = src, len = 0; *p1 && p1 < endp; len++) + p1++; + if (!*p1) { + len++; + } + pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII); + return len; +} + +/* get fixed length l string dest from src, return increment for src */ + +static size_t rap_getstringf(char *src, char *dest, size_t l, size_t dlen, char *endp) +{ + char *p1; + size_t len; + + if (dlen) { + dest[0] = '\0'; + } + for (p1 = src, len = 0; *p1 && p1 < endp; len++) { + p1++; + } + if (!*p1) { + len++; + } + if (len > l) { + len = l; + } + if (len) { + pull_ascii(dest,src,len,len,STR_ASCII); + } + return l; +} + +/* get string dest from offset (obtained at p) from rdata r - converter c */ +static size_t rap_getstringp(TALLOC_CTX *ctx, char *p, char **dest, char *r, uint16_t c, char *endp) +{ + uint32_t off = 0; + const char *src; + size_t len=0; + + *dest = NULL; + if (p+4 < endp) { + GETDWORD(p,off,endp); + off &= 0x0000FFFF; /* mask the obsolete segment number from the offset */ + off -= c; + } + if (r+off > endp || r+off < r) { + src=""; + len=1; + } else { + const char *p1; + src=r+off; + for (p1 = src, len = 0; *p1 && p1 < endp; len++) { + p1++; + } + if (!*p1) { + len++; + } + } + pull_string_talloc(ctx,src,0,dest,src,len,STR_ASCII); + return 4; +} + +static char *make_header(char *param, uint16_t apinum, const char *reqfmt, const char *datafmt) +{ + PUTWORD(param,apinum); + if (reqfmt) + PUTSTRING(param,reqfmt,0); + else + *param++ = (char) 0; + + if (datafmt) + PUTSTRING(param,datafmt,0); + else + *param++ = (char) 0; + + return param; +} + +/**************************************************************************** + call a NetGroupDelete - delete user group from remote server +****************************************************************************/ + +int cli_NetGroupDelete(struct cli_state *cli, const char *group_name) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDel_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group to del */ + +WORDSIZE]; /* reserved word */ + + /* now send a SMBtrans command with api GroupDel */ + p = make_header(param, RAP_WGroupDel, RAP_NetGroupDel_REQ, NULL); + PUTSTRING(p, group_name, RAP_GROUPNAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + + if (res == 0) { + /* nothing to do */ + } else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } else if (res == 2220) { + DEBUG (1, ("Group does not exist\n")); + } else { + DEBUG(4,("NetGroupDelete res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + call a NetGroupAdd - add user group to remote server +****************************************************************************/ + +int cli_NetGroupAdd(struct cli_state *cli, struct rap_group_info_1 *grinfo) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupAdd_REQ) /* req string */ + +sizeof(RAP_GROUP_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* reserved word */ + + /* offset into data of free format strings. Will be updated */ + /* by PUTSTRINGP macro and end up with total data length. */ + int soffset = RAP_GROUPNAME_LEN + 1 + DWORDSIZE; + char *data; + size_t data_size; + + /* Allocate data. */ + data_size = MAX(soffset + strlen(grinfo->comment) + 1, 1024); + + data = SMB_MALLOC_ARRAY(char, data_size); + if (!data) { + DEBUG (1, ("Malloc fail\n")); + return -1; + } + + /* now send a SMBtrans command with api WGroupAdd */ + + p = make_header(param, RAP_WGroupAdd, + RAP_NetGroupAdd_REQ, RAP_GROUP_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, 0); /* reserved word 0 */ + + p = data; + PUTSTRINGF(p, (const char *)grinfo->group_name, RAP_GROUPNAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + PUTSTRINGP(p, grinfo->comment, data, soffset); + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, data_size, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0) { + /* nothing to do */ + } else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } else if (res == 2223) { + DEBUG (1, ("Group already exists\n")); + } else { + DEBUG(4,("NetGroupAdd res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupAdd failed\n")); + } + + SAFE_FREE(data); + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetGroupEnum - try and list user groups on a different host. +****************************************************************************/ + +int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupEnum_REQ) /* parm string */ + +sizeof(RAP_GROUP_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WGroupEnum, + RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L1); + PUTWORD(p,1); /* Info level 1 */ /* add level 0 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, 0xFFE0 /* data area size */, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rdrcnt; + + res = GETRES(rparam, endp); + cli->rap_error = res; + if(cli->rap_error == 234) { + DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n")); + } else if (cli->rap_error != 0) { + DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetGroupEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + char *endp = rparam + rprcnt; + int i, converter = 0, count = 0; + TALLOC_CTX *frame = talloc_stackframe(); + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata; i<count && p < endp;i++) { + char *comment = NULL; + char groupname[RAP_GROUPNAME_LEN]; + + p += rap_getstringf(p, + groupname, + RAP_GROUPNAME_LEN, + RAP_GROUPNAME_LEN, + endp); + p++; /* pad byte */ + p += rap_getstringp(frame, + p, + &comment, + rdata, + converter, + endp); + + if (!comment || !groupname[0]) { + break; + } + + fn(groupname, comment, cli); + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetGroupEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_RNetGroupEnum0(struct cli_state *cli, + void (*fn)(const char *, void *), + void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupEnum_REQ) /* parm string */ + +sizeof(RAP_GROUP_INFO_L0) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WGroupEnum, + RAP_NetGroupEnum_REQ, RAP_GROUP_INFO_L0); + PUTWORD(p,0); /* Info level 0 */ /* Hmmm. I *very* much suspect this + is the resume count, at least + that's what smbd believes... */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, 0xFFE0 /* data area size */, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam+rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + if(cli->rap_error == 234) { + DEBUG(1,("Not all group names were returned (such as those longer than 21 characters)\n")); + } else if (cli->rap_error != 0) { + DEBUG(1,("NetGroupEnum gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetGroupEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + char *endp = rparam + rprcnt; + int i, count = 0; + + p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */ + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata; i<count && p < endp;i++) { + char groupname[RAP_GROUPNAME_LEN]; + + p += rap_getstringf(p, + groupname, + RAP_GROUPNAME_LEN, + RAP_GROUPNAME_LEN, + endp); + if (groupname[0]) { + fn(groupname, cli); + } + } + } else { + DEBUG(4,("NetGroupEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDelUser_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group name */ + +RAP_USERNAME_LEN]; /* user to del */ + + /* now send a SMBtrans command with api GroupMemberAdd */ + p = make_header(param, RAP_WGroupDelUser, RAP_NetGroupDelUser_REQ, NULL); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN); + PUTSTRING(p,user_name,RAP_USERNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + + switch(res) { + case 0: + break; + case 5: + case 65: + DEBUG(1, ("Access Denied\n")); + break; + case 50: + DEBUG(1, ("Not supported by server\n")); + break; + case 2220: + DEBUG(1, ("Group does not exist\n")); + break; + case 2221: + DEBUG(1, ("User does not exist\n")); + break; + case 2237: + DEBUG(1, ("User is not in group\n")); + break; + default: + DEBUG(4,("NetGroupDelUser res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupDelUser failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupAddUser_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_GROUPNAME_LEN /* group name */ + +RAP_USERNAME_LEN]; /* user to add */ + + /* now send a SMBtrans command with api GroupMemberAdd */ + p = make_header(param, RAP_WGroupAddUser, RAP_NetGroupAddUser_REQ, NULL); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN); + PUTSTRING(p,user_name,RAP_USERNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + + switch(res) { + case 0: + break; + case 5: + case 65: + DEBUG(1, ("Access Denied\n")); + break; + case 50: + DEBUG(1, ("Not supported by server\n")); + break; + case 2220: + DEBUG(1, ("Group does not exist\n")); + break; + case 2221: + DEBUG(1, ("User does not exist\n")); + break; + default: + DEBUG(4,("NetGroupAddUser res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetGroupAddUser failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + + +int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupGetUsers_REQ)/* parm string */ + +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */ + +RAP_GROUPNAME_LEN /* group name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api GroupGetUsers */ + p = make_header(param, RAP_WGroupGetUsers, + RAP_NetGroupGetUsers_REQ, RAP_GROUP_USERS_INFO_0); + PUTSTRING(p,group_name,RAP_GROUPNAME_LEN-1); + PUTWORD(p,0); /* info level 0 */ + PUTWORD(p,0xFFE0); /* return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetGroupGetUsers gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetGroupGetUsers no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + char *endp = rparam + rprcnt; + int i, count = 0; + char username[RAP_USERNAME_LEN]; + + p = rparam + WORDSIZE + WORDSIZE; + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata; i<count && p < endp; i++) { + p += rap_getstringf(p, + username, + RAP_USERNAME_LEN, + RAP_USERNAME_LEN, + endp); + if (username[0]) { + fn(username, state); + } + } + } else { + DEBUG(4,("NetGroupGetUsers res=%d\n", res)); + } + + out: + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} + +int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserGetGroups_REQ)/* parm string */ + +sizeof(RAP_GROUP_USERS_INFO_0) /* return string */ + +RAP_USERNAME_LEN /* user name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api GroupGetUsers */ + p = make_header(param, RAP_WUserGetGroups, + RAP_NetUserGetGroups_REQ, RAP_GROUP_USERS_INFO_0); + PUTSTRING(p,user_name,RAP_USERNAME_LEN-1); + PUTWORD(p,0); /* info level 0 */ + PUTWORD(p,0xFFE0); /* return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetUserGetGroups gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetUserGetGroups no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + char *endp = rparam + rprcnt; + int i, count = 0; + char groupname[RAP_GROUPNAME_LEN]; + + p = rparam + WORDSIZE + WORDSIZE; + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata; i<count && p < endp; i++) { + p += rap_getstringf(p, + groupname, + RAP_GROUPNAME_LEN, + RAP_GROUPNAME_LEN, + endp); + if (groupname[0]) { + fn(groupname, state); + } + } + } else { + DEBUG(4,("NetUserGetGroups res=%d\n", res)); + } + + out: + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} + +/**************************************************************************** + Call a NetUserDelete - delete user from remote server. +****************************************************************************/ + +int cli_NetUserDelete(struct cli_state *cli, const char * user_name ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetGroupDel_REQ) /* parm string */ + +1 /* no ret string */ + +RAP_USERNAME_LEN /* user to del */ + +WORDSIZE]; /* reserved word */ + + /* now send a SMBtrans command with api UserDel */ + p = make_header(param, RAP_WUserDel, RAP_NetGroupDel_REQ, NULL); + PUTSTRING(p, user_name, RAP_USERNAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + + if (res == 0) { + /* nothing to do */ + } else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } else if (res == 2221) { + DEBUG (1, ("User does not exist\n")); + } else { + DEBUG(4,("NetUserDelete res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetUserDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetUserAdd - add user to remote server. +****************************************************************************/ + +int cli_NetUserAdd(struct cli_state *cli, struct rap_user_info_1 * userinfo ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserAdd2_REQ) /* req string */ + +sizeof(RAP_USER_INFO_L1) /* data string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer length */ + +WORDSIZE]; /* reserved */ + + char data[1024]; + /* offset into data of free format strings. Will be updated */ + /* by PUTSTRINGP macro and end up with total data length. */ + int soffset=RAP_USERNAME_LEN+1 /* user name + pad */ + + RAP_UPASSWD_LEN /* password */ + + DWORDSIZE /* password age */ + + WORDSIZE /* privilege */ + + DWORDSIZE /* home dir ptr */ + + DWORDSIZE /* comment ptr */ + + WORDSIZE /* flags */ + + DWORDSIZE; /* login script ptr*/ + + /* now send a SMBtrans command with api NetUserAdd */ + p = make_header(param, RAP_WUserAdd2, + RAP_NetUserAdd2_REQ, RAP_USER_INFO_L1); + + PUTWORD(p, 1); /* info level */ + PUTWORD(p, 0); /* pwencrypt */ + PUTWORD(p, MIN(strlen((const char *)userinfo->passwrd), + RAP_UPASSWD_LEN)); + + p = data; + memset(data, '\0', soffset); + + PUTSTRINGF(p, (const char *)userinfo->user_name, RAP_USERNAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + PUTSTRINGF(p, (const char *)userinfo->passwrd, RAP_UPASSWD_LEN); + PUTDWORD(p, 0); /* pw age - n.a. on user add */ + PUTWORD(p, userinfo->priv); + PUTSTRINGP(p, userinfo->home_dir, data, soffset); + PUTSTRINGP(p, userinfo->comment, data, soffset); + PUTWORD(p, userinfo->userflags); + PUTSTRINGP(p, userinfo->logon_script, data, soffset); + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, sizeof(data), /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0) { + /* nothing to do */ + } else if ((res == 5) || (res == 65)) { + DEBUG(1, ("Access Denied\n")); + } else if (res == 2224) { + DEBUG (1, ("User already exists\n")); + } else { + DEBUG(4,("NetUserAdd res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetUserAdd failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +call a NetUserEnum - try and list users on a different host +****************************************************************************/ + +int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserEnum_REQ) /* parm string */ + +sizeof(RAP_USER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WUserEnum, + RAP_NetUserEnum_REQ, RAP_USER_INFO_L1); + PUTWORD(p,1); /* Info level 1 */ + PUTWORD(p,0xFF00); /* Return buffer size */ + + /* BB Fix handling of large numbers of users to be returned */ + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + if (cli->rap_error != 0) { + DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetUserEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + int i, converter = 0, count = 0; + char username[RAP_USERNAME_LEN]; + char userpw[RAP_UPASSWD_LEN]; + char *endp = rparam + rprcnt; + char *comment, *homedir, *logonscript; + TALLOC_CTX *frame = talloc_stackframe(); + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata;i<count && p < endp;i++) { + p += rap_getstringf(p, + username, + RAP_USERNAME_LEN, + RAP_USERNAME_LEN, + endp); + p++; /* pad byte */ + p += rap_getstringf(p, + userpw, + RAP_UPASSWD_LEN, + RAP_UPASSWD_LEN, + endp); + p += DWORDSIZE; /* skip password age */ + p += WORDSIZE; /* skip priv: 0=guest, 1=user, 2=admin */ + p += rap_getstringp(frame, + p, + &homedir, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &comment, + rdata, + converter, + endp); + p += WORDSIZE; /* skip flags */ + p += rap_getstringp(frame, + p, + &logonscript, + rdata, + converter, + endp); + if (username[0] && comment && + homedir && logonscript) { + fn(username, + comment, + homedir, + logonscript, + cli); + } + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetUserEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_RNetUserEnum0(struct cli_state *cli, + void (*fn)(const char *, void *), + void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetUserEnum_REQ) /* parm string */ + +sizeof(RAP_USER_INFO_L0) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WUserEnum, + RAP_NetUserEnum_REQ, RAP_USER_INFO_L0); + PUTWORD(p,0); /* Info level 1 */ + PUTWORD(p,0xFF00); /* Return buffer size */ + + /* BB Fix handling of large numbers of users to be returned */ + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + if (cli->rap_error != 0) { + DEBUG(1,("NetUserEnum gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetUserEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + int i, count = 0; + char *endp = rparam + rprcnt; + char username[RAP_USERNAME_LEN]; + + p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */ + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata;i<count && p < endp;i++) { + p += rap_getstringf(p, + username, + RAP_USERNAME_LEN, + RAP_USERNAME_LEN, + endp); + if (username[0]) { + fn(username, cli); + } + } + } else { + DEBUG(4,("NetUserEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetFileClose2 - close open file on another session to server. +****************************************************************************/ + +int cli_NetFileClose(struct cli_state *cli, uint32_t file_id ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileClose2_REQ) /* req string */ + +1 /* no ret string */ + +DWORDSIZE]; /* file ID */ + int res = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileClose2, RAP_WFileClose2_REQ, NULL); + PUTDWORD(p, file_id); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0) { + /* nothing to do */ + } else if (res == 2314){ + DEBUG(1, ("NetFileClose2 - attempt to close non-existent file open instance\n")); + } else { + DEBUG(4,("NetFileClose2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileClose2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetFileGetInfo - get information about server file opened from other + workstation. +****************************************************************************/ + +int cli_NetFileGetInfo(struct cli_state *cli, uint32_t file_id, void (*fn)(const char *, const char *, uint16_t, uint16_t, uint32_t)) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileGetInfo2_REQ) /* req string */ + +sizeof(RAP_FILE_INFO_L3) /* return string */ + +DWORDSIZE /* file ID */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileGetInfo2, + RAP_WFileGetInfo2_REQ, RAP_FILE_INFO_L3); + PUTDWORD(p, file_id); + PUTWORD(p, 3); /* info level */ + PUTWORD(p, 0x1000); /* buffer size */ + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0x1000, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + int converter = 0,id = 0, perms = 0, locks = 0; + char *fpath, *fuser; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter, endp); + + p = rdata; + endp = rdata + rdrcnt; + + GETDWORD(p, id, endp); + GETWORD(p, perms, endp); + GETWORD(p, locks, endp); + + p += rap_getstringp(frame, + p, + &fpath, + rdata, + converter, + endp); + rap_getstringp(frame, + p, + &fuser, + rdata, + converter, + endp); + + if (fpath && fuser) { + fn(fpath, fuser, perms, locks, id); + } + + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetFileGetInfo2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileGetInfo2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** +* Call a NetFileEnum2 - list open files on an SMB server +* +* PURPOSE: Remotes a NetFileEnum API call to the current server or target +* server listing the files open via the network (and their +* corresponding open instance ids) +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* user - if present, return only files opened by this remote user +* base_path - if present, return only files opened below this +* base path +* fn - display function to invoke for each entry in the result +* +* +* Returns: +* True - success +* False - failure +* +****************************************************************************/ + +int cli_NetFileEnum(struct cli_state *cli, const char * user, + const char * base_path, + void (*fn)(const char *, const char *, uint16_t, uint16_t, + uint32_t)) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WFileEnum2_REQ) /* req string */ + +sizeof(RAP_FILE_INFO_L3) /* return string */ + +1024 /* base path (opt) */ + +RAP_USERNAME_LEN /* user name (opt) */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* resume key ? */ + +DWORDSIZE]; /* resume key ? */ + int count = -1; + int res = -1; + + /* now send a SMBtrans command with api RNetShareEnum */ + p = make_header(param, RAP_WFileEnum2, + RAP_WFileEnum2_REQ, RAP_FILE_INFO_L3); + + PUTSTRING(p, base_path, 1024); + PUTSTRING(p, user, RAP_USERNAME_LEN); + PUTWORD(p, 3); /* info level */ + PUTWORD(p, 0xFF00); /* buffer size */ + PUTDWORD(p, 0); /* zero out the resume key */ + PUTDWORD(p, 0); /* or is this one the resume key? */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 0xFF00, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + int converter = 0, i; + + p = rparam + WORDSIZE; /* skip result */ + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + p = rdata; + endp = rdata + rdrcnt; + for (i=0; i<count && p < endp; i++) { + int id = 0, perms = 0, locks = 0; + char *fpath, *fuser; + + GETDWORD(p, id, endp); + GETWORD(p, perms, endp); + GETWORD(p, locks, endp); + p += rap_getstringp(frame, + p, + &fpath, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &fuser, + rdata, + converter, + endp); + + if (fpath && fuser) { + fn(fpath, fuser, perms, locks, id); + } + } /* BB fix ERRmoredata case to send resume request */ + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetFileEnum2 res=%d\n", res)); + } + } else { + DEBUG(4,("NetFileEnum2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return count; +} + +/**************************************************************************** + Call a NetShareAdd - share/export directory on remote server. +****************************************************************************/ + +int cli_NetShareAdd(struct cli_state *cli, struct rap_share_info_2 * sinfo ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WShareAdd_REQ) /* req string */ + +sizeof(RAP_SHARE_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* reserved word */ + char data[1024]; + /* offset to free format string section following fixed length data. */ + /* will be updated by PUTSTRINGP macro and will end up with total len */ + int soffset = RAP_SHARENAME_LEN + 1 /* share name + pad */ + + WORDSIZE /* share type */ + + DWORDSIZE /* comment pointer */ + + WORDSIZE /* permissions */ + + WORDSIZE /* max users */ + + WORDSIZE /* active users */ + + DWORDSIZE /* share path */ + + RAP_SPASSWD_LEN + 1; /* share password + pad */ + + memset(param,'\0',sizeof(param)); + /* now send a SMBtrans command with api RNetShareAdd */ + p = make_header(param, RAP_WshareAdd, + RAP_WShareAdd_REQ, RAP_SHARE_INFO_L2); + PUTWORD(p, 2); /* info level */ + PUTWORD(p, 0); /* reserved word 0 */ + + p = data; + PUTSTRINGF(p, (const char *)sinfo->share_name, RAP_SHARENAME_LEN); + PUTBYTE(p, 0); /* pad byte 0 */ + + PUTWORD(p, sinfo->share_type); + PUTSTRINGP(p, sinfo->comment, data, soffset); + PUTWORD(p, sinfo->perms); + PUTWORD(p, sinfo->maximum_users); + PUTWORD(p, sinfo->active_users); + PUTSTRINGP(p, sinfo->path, data, soffset); + PUTSTRINGF(p, (const char *)sinfo->password, RAP_SPASSWD_LEN); + SCVAL(p,-1,0x0A); /* required 0x0A at end of password */ + + if (cli_api(cli, + param, sizeof(param), 1024, /* Param, length, maxlen */ + data, soffset, sizeof(data), /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0) { + /* nothing to do */ + } else { + DEBUG(4,("NetShareAdd res=%d\n", res)); + } + } else { + DEBUG(4,("NetShareAdd failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetShareDelete - unshare exported directory on remote server. +****************************************************************************/ + +int cli_NetShareDelete(struct cli_state *cli, const char * share_name ) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + int res = -1; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WShareDel_REQ) /* req string */ + +1 /* no ret string */ + +RAP_SHARENAME_LEN /* share to del */ + +WORDSIZE]; /* reserved word */ + + /* now send a SMBtrans command with api RNetShareDelete */ + p = make_header(param, RAP_WshareDel, RAP_WShareDel_REQ, NULL); + PUTSTRING(p,share_name,RAP_SHARENAME_LEN); + PUTWORD(p,0); /* reserved word MBZ on input */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0) { + /* nothing to do */ + } else { + DEBUG(4,("NetShareDelete res=%d\n", res)); + } + } else { + DEBUG(4,("NetShareDelete failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/************************************************************************* +* +* Function Name: cli_get_pdc_name +* +* PURPOSE: Remotes a NetServerEnum API call to the current server +* requesting the name of a server matching the server +* type of SV_TYPE_DOMAIN_CTRL (PDC). +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* workgroup - pointer to string containing name of domain +* pdc_name - pointer to string that will contain PDC name +* on successful return +* +* Returns: +* True - success +* False - failure +* +************************************************************************/ + +bool cli_get_pdc_name(struct cli_state *cli, const char *workgroup, char **pdc_name) +{ + char *rparam = NULL; + char *rdata = NULL; + unsigned int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServerEnum2_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* server type */ + +RAP_MACHNAME_LEN]; /* workgroup */ + int count = -1; + int res = -1; + + *pdc_name = NULL; + + /* send a SMBtrans command with api NetServerEnum */ + p = make_header(param, RAP_NetServerEnum2, + RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, CLI_BUFFER_SIZE); + PUTDWORD(p, SV_TYPE_DOMAIN_CTRL); + PUTSTRING(p, workgroup, RAP_MACHNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + + /* + * We only really care to copy a name if the + * API succeeded and we got back a name. + */ + if (cli->rap_error == 0) { + p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */ + GETWORD(p, count, endp); + p = rdata; + endp = rdata + rdrcnt; + + if (count > 0) { + TALLOC_CTX *frame = talloc_stackframe(); + char *dcname; + rap_getstring(frame, + p, + &dcname, + endp); + if (dcname) { + *pdc_name = SMB_STRDUP(dcname); + } + TALLOC_FREE(frame); + } + } else { + DEBUG(4, ("cli_get_pdc_name: machine %s failed the " + "NetServerEnum call. Error was : %s.\n", + smbXcli_conn_remote_name(cli->conn), + win_errstr(W_ERROR(cli->rap_error)))); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return(count > 0); +} + +bool cli_get_server_name(TALLOC_CTX *mem_ctx, struct cli_state *cli, + char **servername) +{ + char *rparam = NULL; + char *rdata = NULL; + unsigned int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_WserverGetInfo_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L1) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + bool res = false; + char *endp; + fstring tmp; + + /* send a SMBtrans command with api NetServerGetInfo */ + p = make_header(param, RAP_WserverGetInfo, + RAP_WserverGetInfo_REQ, RAP_SERVER_INFO_L1); + PUTWORD(p, 1); /* info level */ + PUTWORD(p, CLI_BUFFER_SIZE); + + if (!cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + goto failed; + } + + endp = rparam + rprcnt; + if (GETRES(rparam, endp) != 0) { + goto failed; + } + + if (rdrcnt < 16) { + DEBUG(10, ("invalid data count %d, expected >= 16\n", rdrcnt)); + goto failed; + } + + if (pull_ascii(tmp, rdata, sizeof(tmp)-1, 16, STR_TERMINATE) == -1) { + DEBUG(10, ("pull_ascii failed\n")); + goto failed; + } + + if (!(*servername = talloc_strdup(mem_ctx, tmp))) { + DEBUG(1, ("talloc_strdup failed\n")); + goto failed; + } + + res = true; + + failed: + SAFE_FREE(rparam); + SAFE_FREE(rdata); + return res; +} + +/************************************************************************* +* +* Function Name: cli_ns_check_server_type +* +* PURPOSE: Remotes a NetServerEnum2 API call to the current server +* requesting server_info_0 level information of machines +* matching the given server type. If the returned server +* list contains the machine name contained in smbXcli_conn_remote_name(->conn) +* then we conclude the server type checks out. This routine +* is useful to retrieve list of server's of a certain +* type when all you have is a null session connection and +* can't remote API calls such as NetWkstaGetInfo or +* NetServerGetInfo. +* +* Dependencies: none +* +* Parameters: +* cli - pointer to cli_state structure +* workgroup - pointer to string containing domain +* stype - server type +* +* Returns: +* True - success +* False - failure +* +************************************************************************/ + +bool cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32_t stype) +{ + char *rparam = NULL; + char *rdata = NULL; + unsigned int rdrcnt,rprcnt; + char *p; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServerEnum2_REQ) /* req string */ + +sizeof(RAP_SERVER_INFO_L0) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +DWORDSIZE /* server type */ + +RAP_MACHNAME_LEN]; /* workgroup */ + bool found_server = false; + int res = -1; + const char *remote_name = smbXcli_conn_remote_name(cli->conn); + + /* send a SMBtrans command with api NetServerEnum */ + p = make_header(param, RAP_NetServerEnum2, + RAP_NetServerEnum2_REQ, RAP_SERVER_INFO_L0); + PUTWORD(p, 0); /* info level 0 */ + PUTWORD(p, CLI_BUFFER_SIZE); + PUTDWORD(p, stype); + PUTSTRING(p, workgroup, RAP_MACHNAME_LEN); + + if (cli_api(cli, + param, PTR_DIFF(p,param), 8, /* params, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + + if (res == 0 || res == ERRmoredata) { + int i, count = 0; + + p = rparam + WORDSIZE + WORDSIZE; + GETWORD(p, count,endp); + + p = rdata; + endp = rdata + rdrcnt; + for (i = 0;i < count && p < endp;i++, p += 16) { + char ret_server[RAP_MACHNAME_LEN]; + + p += rap_getstringf(p, + ret_server, + RAP_MACHNAME_LEN, + RAP_MACHNAME_LEN, + endp); + if (strequal(ret_server, remote_name)) { + found_server = true; + break; + } + } + } else { + DEBUG(4, ("cli_ns_check_server_type: machine %s " + "failed the NetServerEnum call. Error was : " + "%s.\n", remote_name, + win_errstr(W_ERROR(cli->rap_error)))); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return found_server; +} + +/**************************************************************************** + Perform a NetWkstaUserLogoff. +****************************************************************************/ + +bool cli_NetWkstaUserLogoff(struct cli_state *cli, const char *user, const char *workstation) +{ + char *rparam = NULL; + char *rdata = NULL; + char *p; + unsigned int rdrcnt,rprcnt; + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetWkstaUserLogoff_REQ) /* req string */ + +sizeof(RAP_USER_LOGOFF_INFO_L1) /* return string */ + +RAP_USERNAME_LEN+1 /* user name+pad */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* buffer size */ + +WORDSIZE]; /* buffer size? */ + char upperbuf[MAX(RAP_USERNAME_LEN,RAP_MACHNAME_LEN)]; + int res = -1; + char *tmp = NULL; + + memset(param, 0, sizeof(param)); + + /* send a SMBtrans command with api NetWkstaUserLogoff */ + p = make_header(param, RAP_WWkstaUserLogoff, + RAP_NetWkstaUserLogoff_REQ, RAP_USER_LOGOFF_INFO_L1); + PUTDWORD(p, 0); /* Null pointer */ + PUTDWORD(p, 0); /* Null pointer */ + strlcpy(upperbuf, user, sizeof(upperbuf)); + if (!strupper_m(upperbuf)) { + return false; + } + tmp = upperbuf; + PUTSTRINGF(p, tmp, RAP_USERNAME_LEN); + p++; /* strange format, but ok */ + strlcpy(upperbuf, workstation, sizeof(upperbuf)); + if (!strupper_m(upperbuf)) { + return false; + } + tmp = upperbuf; + PUTSTRINGF(p, tmp, RAP_MACHNAME_LEN); + PUTWORD(p, CLI_BUFFER_SIZE); + PUTWORD(p, CLI_BUFFER_SIZE); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, /* param, length, max */ + NULL, 0, CLI_BUFFER_SIZE, /* data, length, max */ + &rparam, &rprcnt, /* return params, return size */ + &rdata, &rdrcnt /* return data, return size */ + )) { + char *endp = rparam + rprcnt; + res = GETRES(rparam,endp); + cli->rap_error = res; + + if (cli->rap_error != 0) { + DEBUG(4,("NetwkstaUserLogoff gave error %d\n", cli->rap_error)); + } + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + return (cli->rap_error == 0); +} + +int cli_NetPrintQEnum(struct cli_state *cli, + void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t), + void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetPrintQEnum_REQ) /* req string */ + +sizeof(RAP_PRINTQ_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0',sizeof(param)); + p = make_header(param, RAP_WPrintQEnum, + RAP_NetPrintQEnum_REQ, RAP_PRINTQ_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetPrintQEnum gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetPrintQEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + char *endp = rparam + rprcnt; + int i, converter = 0, count = 0; + + p = rparam + WORDSIZE; + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + p = rdata; + endp = rdata + rdrcnt; + for (i=0;i<count && p < endp;i++) { + char qname[RAP_SHARENAME_LEN]; + char *sep_file, *print_proc, *dest, *parms, *comment; + uint16_t jobcount = 0, priority = 0; + uint16_t start_time = 0, until_time = 0, status = 0; + + p += rap_getstringf(p, + qname, + RAP_SHARENAME_LEN, + RAP_SHARENAME_LEN, + endp); + p++; /* pad */ + GETWORD(p, priority, endp); + GETWORD(p, start_time, endp); + GETWORD(p, until_time, endp); + p += rap_getstringp(frame, + p, + &sep_file, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &print_proc, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &dest, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &parms, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &comment, + rdata, + converter, + endp); + GETWORD(p, status, endp); + GETWORD(p, jobcount, endp); + + if (sep_file && print_proc && dest && parms && + comment) { + qfn(qname, priority, start_time, until_time, sep_file, print_proc, + dest, parms, comment, status, jobcount); + } + + if (jobcount) { + int j; + for (j=0;j<jobcount;j++) { + uint16_t jid = 0, pos = 0, fsstatus = 0; + char ownername[RAP_USERNAME_LEN]; + char notifyname[RAP_MACHNAME_LEN]; + char datatype[RAP_DATATYPE_LEN]; + char *jparms, *jstatus, *jcomment; + unsigned int submitted = 0, jsize = 0; + + GETWORD(p, jid, endp); + p += rap_getstringf(p, + ownername, + RAP_USERNAME_LEN, + RAP_USERNAME_LEN, + endp); + p++; /* pad byte */ + p += rap_getstringf(p, + notifyname, + RAP_MACHNAME_LEN, + RAP_MACHNAME_LEN, + endp); + p += rap_getstringf(p, + datatype, + RAP_DATATYPE_LEN, + RAP_DATATYPE_LEN, + endp); + p += rap_getstringp(frame, + p, + &jparms, + rdata, + converter, + endp); + GETWORD(p, pos, endp); + GETWORD(p, fsstatus, endp); + p += rap_getstringp(frame, + p, + &jstatus, + rdata, + converter, + endp); + GETDWORD(p, submitted, endp); + GETDWORD(p, jsize, endp); + p += rap_getstringp(frame, + p, + &jcomment, + rdata, + converter, + endp); + + if (jparms && jstatus && jcomment) { + jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus, + jstatus, submitted, jsize, jcomment); + } + } + } + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetPrintQEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer, + void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t), + void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetPrintQGetInfo_REQ) /* req string */ + +sizeof(RAP_PRINTQ_INFO_L2) /* return string */ + +RAP_SHARENAME_LEN /* printer name */ + +WORDSIZE /* info level */ + +WORDSIZE /* buffer size */ + +sizeof(RAP_SMB_PRINT_JOB_L1)]; /* more ret data */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0',sizeof(param)); + p = make_header(param, RAP_WPrintQGetInfo, + RAP_NetPrintQGetInfo_REQ, RAP_PRINTQ_INFO_L2); + PUTSTRING(p, printer, RAP_SHARENAME_LEN-1); + PUTWORD(p, 2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + PUTSTRING(p, RAP_SMB_PRINT_JOB_L1, 0); + + if (cli_api(cli, + param, PTR_DIFF(p,param),1024, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetPrintQGetInfo gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetPrintQGetInfo no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + char *endp = rparam + rprcnt; + int rsize = 0, converter = 0; + char qname[RAP_SHARENAME_LEN]; + char *sep_file, *print_proc, *dest, *parms, *comment; + uint16_t jobcount = 0, priority = 0; + uint16_t start_time = 0, until_time = 0, status = 0; + + p = rparam + WORDSIZE; + GETWORD(p, converter, endp); + GETWORD(p, rsize, endp); + + p = rdata; + endp = rdata + rdrcnt; + p += rap_getstringf(p, + qname, + RAP_SHARENAME_LEN, + RAP_SHARENAME_LEN, + endp); + p++; /* pad */ + GETWORD(p, priority, endp); + GETWORD(p, start_time, endp); + GETWORD(p, until_time, endp); + p += rap_getstringp(frame, + p, + &sep_file, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &print_proc, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &dest, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &parms, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &comment, + rdata, + converter, + endp); + GETWORD(p, status, endp); + GETWORD(p, jobcount, endp); + + if (sep_file && print_proc && dest && + parms && comment) { + qfn(qname, priority, start_time, until_time, sep_file, print_proc, + dest, parms, comment, status, jobcount); + } + if (jobcount) { + int j; + for (j=0;(j<jobcount)&&(PTR_DIFF(p,rdata)< rsize)&& + p<endp;j++) { + uint16_t jid = 0, pos = 0, fsstatus = 0; + char ownername[RAP_USERNAME_LEN]; + char notifyname[RAP_MACHNAME_LEN]; + char datatype[RAP_DATATYPE_LEN]; + char *jparms, *jstatus, *jcomment; + unsigned int submitted = 0, jsize = 0; + + GETWORD(p, jid, endp); + p += rap_getstringf(p, + ownername, + RAP_USERNAME_LEN, + RAP_USERNAME_LEN, + endp); + p++; /* pad byte */ + p += rap_getstringf(p, + notifyname, + RAP_MACHNAME_LEN, + RAP_MACHNAME_LEN, + endp); + p += rap_getstringf(p, + datatype, + RAP_DATATYPE_LEN, + RAP_DATATYPE_LEN, + endp); + p += rap_getstringp(frame, + p, + &jparms, + rdata, + converter, + endp); + GETWORD(p, pos,endp); + GETWORD(p, fsstatus,endp); + p += rap_getstringp(frame, + p, + &jstatus, + rdata, + converter, + endp); + GETDWORD(p, submitted,endp); + GETDWORD(p, jsize,endp); + p += rap_getstringp(frame, + p, + &jcomment, + rdata, + converter, + endp); + + if (jparms && jstatus && jcomment) { + jfn(jid, ownername, notifyname, datatype, jparms, pos, fsstatus, + jstatus, submitted, jsize, jcomment); + } + } + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetPrintQGetInfo res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetServiceEnum - list running services on a different host. +****************************************************************************/ + +int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetServiceEnum_REQ) /* parm string */ + +sizeof(RAP_SERVICE_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WServiceEnum, + RAP_NetServiceEnum_REQ, RAP_SERVICE_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, 0xFFE0 /* data area size */, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if(cli->rap_error == 234) { + DEBUG(1,("Not all service names were returned (such as those longer than 15 characters)\n")); + } else if (cli->rap_error != 0) { + DEBUG(1,("NetServiceEnum gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetServiceEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + char *endp = rparam + rprcnt; + int i, count = 0; + + p = rparam + WORDSIZE + WORDSIZE; /* skip result and converter */ + GETWORD(p, count,endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata;i<count && p < endp;i++) { + char comment[RAP_SRVCCMNT_LEN]; + char servicename[RAP_SRVCNAME_LEN]; + + p += rap_getstringf(p, + servicename, + RAP_SRVCNAME_LEN, + RAP_SRVCNAME_LEN, + endp); + p+=8; /* pass status words */ + p += rap_getstringf(p, + comment, + RAP_SRVCCMNT_LEN, + RAP_SRVCCMNT_LEN, + endp); + + if (servicename[0]) { + fn(servicename, comment, cli); /* BB add status too */ + } + } + } else { + DEBUG(4,("NetServiceEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetSessionEnum - list workstations with sessions to an SMB server. +****************************************************************************/ + +int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, char *)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionEnum_REQ) /* parm string */ + +sizeof(RAP_SESSION_INFO_L2) /* return string */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionEnum, + RAP_NetSessionEnum_REQ, RAP_SESSION_INFO_L2); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFF); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),8, + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetSessionEnum gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetSessionEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + char *endp = rparam + rprcnt; + int i, converter = 0, count = 0; + + p = rparam + WORDSIZE; + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata;i<count && p < endp;i++) { + char *wsname, *username, *clitype_name; + uint16_t num_conns = 0, num_opens = 0, num_users = 0; + unsigned int sess_time = 0, idle_time = 0, user_flags = 0; + + p += rap_getstringp(frame, + p, + &wsname, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &username, + rdata, + converter, + endp); + GETWORD(p, num_conns, endp); + GETWORD(p, num_opens, endp); + GETWORD(p, num_users, endp); + GETDWORD(p, sess_time, endp); + GETDWORD(p, idle_time, endp); + GETDWORD(p, user_flags, endp); + p += rap_getstringp(frame, + p, + &clitype_name, + rdata, + converter, + endp); + + if (wsname && username && clitype_name) { + fn(wsname, username, num_conns, num_opens, num_users, sess_time, + idle_time, user_flags, clitype_name); + } + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetSessionEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetSessionGetInfo - get information about other session to an SMB server. +****************************************************************************/ + +int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation, + void (*fn)(const char *, const char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, const char *)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionGetInfo_REQ) /* req string */ + +sizeof(RAP_SESSION_INFO_L2) /* return string */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + char *endp; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionGetInfo, + RAP_NetSessionGetInfo_REQ, RAP_SESSION_INFO_L2); + PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1); + PUTWORD(p,2); /* Info level 2 */ + PUTWORD(p,0xFF); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if (cli->rap_error != 0) { + DEBUG(1,("NetSessionGetInfo gave error %d\n", cli->rap_error)); + } + } + + if (!rdata) { + DEBUG(4,("NetSessionGetInfo no data returned\n")); + goto out; + } + + endp = rparam + rprcnt; + res = GETRES(rparam, endp); + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + int converter = 0; + char *wsname, *username, *clitype_name; + uint16_t num_conns = 0, num_opens = 0, num_users = 0; + unsigned int sess_time = 0, idle_time = 0, user_flags = 0; + + p = rparam + WORDSIZE; + GETWORD(p, converter,endp); + + p = rdata; + endp = rdata + rdrcnt; + p += rap_getstringp(frame, + p, + &wsname, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &username, + rdata, + converter, + endp); + GETWORD(p, num_conns, endp); + GETWORD(p, num_opens, endp); + GETWORD(p, num_users, endp); + GETDWORD(p, sess_time, endp); + GETDWORD(p, idle_time, endp); + GETDWORD(p, user_flags, endp); + rap_getstringp(frame, + p, + &clitype_name, + rdata, + converter, + endp); + + if (wsname && username && clitype_name) { + fn(wsname, username, num_conns, num_opens, num_users, sess_time, + idle_time, user_flags, clitype_name); + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetSessionGetInfo res=%d\n", res)); + } + + out: + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +/**************************************************************************** + Call a NetSessionDel - close a session to an SMB server. +****************************************************************************/ + +int cli_NetSessionDel(struct cli_state *cli, const char *workstation) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetSessionDel_REQ) /* req string */ + +1 /* no return string */ + +RAP_MACHNAME_LEN /* workstation name */ + +WORDSIZE]; /* reserved (0) */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WsessionDel, RAP_NetSessionDel_REQ, NULL); + PUTSTRING(p, workstation, RAP_MACHNAME_LEN-1); + PUTWORD(p,0); /* reserved word of 0 */ + + if (cli_api(cli, + param, PTR_DIFF(p,param), 1024, /* Param, length, maxlen */ + NULL, 0, 200, /* data, length, maxlen */ + &rparam, &rprcnt, /* return params, length */ + &rdata, &rdrcnt)) /* return data, length */ + { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + + if (res == 0) { + /* nothing to do */ + } else { + DEBUG(4,("NetFileClose2 res=%d\n", res)); + } + } else { + res = -1; + DEBUG(4,("NetFileClose2 failed\n")); + } + + SAFE_FREE(rparam); + SAFE_FREE(rdata); + + return res; +} + +int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier, + void (*fn)(uint16_t conid, uint16_t contype, + uint16_t numopens, uint16_t numusers, + uint32_t contime, const char *username, + const char *netname)) +{ + char param[WORDSIZE /* api number */ + +sizeof(RAP_NetConnectionEnum_REQ) /* req string */ + +sizeof(RAP_CONNECTION_INFO_L1) /* return string */ + +RAP_MACHNAME_LEN /* wksta name */ + +WORDSIZE /* info level */ + +WORDSIZE]; /* buffer size */ + char *p; + char *rparam = NULL; + char *rdata = NULL; + unsigned int rprcnt, rdrcnt; + int res = -1; + + memset(param, '\0', sizeof(param)); + p = make_header(param, RAP_WconnectionEnum, + RAP_NetConnectionEnum_REQ, RAP_CONNECTION_INFO_L1); + PUTSTRING(p, qualifier, RAP_MACHNAME_LEN-1);/* Workstation name */ + PUTWORD(p,1); /* Info level 1 */ + PUTWORD(p,0xFFE0); /* Return buffer size */ + + if (cli_api(cli, + param, PTR_DIFF(p,param),PTR_DIFF(p,param), + NULL, 0, CLI_BUFFER_SIZE, + &rparam, &rprcnt, + &rdata, &rdrcnt)) { + char *endp = rparam + rprcnt; + res = GETRES(rparam, endp); + cli->rap_error = res; + if (res != 0) { + DEBUG(1,("NetConnectionEnum gave error %d\n", res)); + } + } + + if (!rdata) { + DEBUG(4,("NetConnectionEnum no data returned\n")); + goto out; + } + + if (res == 0 || res == ERRmoredata) { + TALLOC_CTX *frame = talloc_stackframe(); + char *endp = rparam + rprcnt; + int i, converter = 0, count = 0; + + p = rparam + WORDSIZE; + GETWORD(p, converter, endp); + GETWORD(p, count, endp); + + endp = rdata + rdrcnt; + for (i=0,p=rdata;i<count && p < endp;i++) { + char *netname, *username; + uint16_t conn_id = 0, conn_type = 0, num_opens = 0, num_users = 0; + unsigned int conn_time = 0; + + GETWORD(p,conn_id, endp); + GETWORD(p,conn_type, endp); + GETWORD(p,num_opens, endp); + GETWORD(p,num_users, endp); + GETDWORD(p,conn_time, endp); + p += rap_getstringp(frame, + p, + &username, + rdata, + converter, + endp); + p += rap_getstringp(frame, + p, + &netname, + rdata, + converter, + endp); + + if (username && netname) { + fn(conn_id, conn_type, num_opens, num_users, conn_time, + username, netname); + } + } + TALLOC_FREE(frame); + } else { + DEBUG(4,("NetConnectionEnum res=%d\n", res)); + } + + out: + + SAFE_FREE(rdata); + SAFE_FREE(rparam); + return res; +} diff --git a/source3/utils/clirap2.h b/source3/utils/clirap2.h new file mode 100644 index 0000000..275d491 --- /dev/null +++ b/source3/utils/clirap2.h @@ -0,0 +1,80 @@ +/* + Samba Unix/Linux SMB client library + More client RAP (SMB Remote Procedure Calls) functions + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2007 Jeremy Allison. jra@samba.org + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) James Peach 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef __UTILS_CLIRAP2_H__ +#define __UTILS_CLIRAP2_H__ + +#include "../libsmb/clirap.h" + +struct rap_group_info_1; +struct rap_user_info_1; +struct rap_share_info_2; + +int cli_NetGroupDelete(struct cli_state *cli, const char *group_name); +int cli_NetGroupAdd(struct cli_state *cli, struct rap_group_info_1 *grinfo); +int cli_RNetGroupEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state); +int cli_RNetGroupEnum0(struct cli_state *cli, + void (*fn)(const char *, void *), + void *state); +int cli_NetGroupDelUser(struct cli_state * cli, const char *group_name, const char *user_name); +int cli_NetGroupAddUser(struct cli_state * cli, const char *group_name, const char *user_name); +int cli_NetGroupGetUsers(struct cli_state * cli, const char *group_name, void (*fn)(const char *, void *), void *state ); +int cli_NetUserGetGroups(struct cli_state * cli, const char *user_name, void (*fn)(const char *, void *), void *state ); +int cli_NetUserDelete(struct cli_state *cli, const char * user_name ); +int cli_NetUserAdd(struct cli_state *cli, struct rap_user_info_1 * userinfo ); +int cli_RNetUserEnum(struct cli_state *cli, void (*fn)(const char *, const char *, const char *, const char *, void *), void *state); +int cli_RNetUserEnum0(struct cli_state *cli, + void (*fn)(const char *, void *), + void *state); +int cli_NetFileClose(struct cli_state *cli, uint32_t file_id ); +int cli_NetFileGetInfo(struct cli_state *cli, uint32_t file_id, void (*fn)(const char *, const char *, uint16_t, uint16_t, uint32_t)); +int cli_NetFileEnum(struct cli_state *cli, const char * user, + const char * base_path, + void (*fn)(const char *, const char *, uint16_t, uint16_t, + uint32_t)); +int cli_NetShareAdd(struct cli_state *cli, struct rap_share_info_2 * sinfo ); +int cli_NetShareDelete(struct cli_state *cli, const char * share_name ); +bool cli_get_pdc_name(struct cli_state *cli, const char *workgroup, char **pdc_name); +bool cli_get_server_name(TALLOC_CTX *mem_ctx, struct cli_state *cli, + char **servername); +bool cli_ns_check_server_type(struct cli_state *cli, char *workgroup, uint32_t stype); +bool cli_NetWkstaUserLogoff(struct cli_state *cli, const char *user, const char *workstation); +int cli_NetPrintQEnum(struct cli_state *cli, + void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t), + void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*)); +int cli_NetPrintQGetInfo(struct cli_state *cli, const char *printer, + void (*qfn)(const char*,uint16_t,uint16_t,uint16_t,const char*,const char*,const char*,const char*,const char*,uint16_t,uint16_t), + void (*jfn)(uint16_t,const char*,const char*,const char*,const char*,uint16_t,uint16_t,const char*,unsigned int,unsigned int,const char*)); +int cli_RNetServiceEnum(struct cli_state *cli, void (*fn)(const char *, const char *, void *), void *state); +int cli_NetSessionEnum(struct cli_state *cli, void (*fn)(char *, char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, char *)); +int cli_NetSessionGetInfo(struct cli_state *cli, const char *workstation, + void (*fn)(const char *, const char *, uint16_t, uint16_t, uint16_t, unsigned int, unsigned int, unsigned int, const char *)); +int cli_NetSessionDel(struct cli_state *cli, const char *workstation); +int cli_NetConnectionEnum(struct cli_state *cli, const char *qualifier, + void (*fn)(uint16_t conid, uint16_t contype, + uint16_t numopens, uint16_t numusers, + uint32_t contime, const char *username, + const char *netname)); + +#endif /* __UTILS_CLIRAP2_H__ */ diff --git a/source3/utils/conn_tdb.c b/source3/utils/conn_tdb.c new file mode 100644 index 0000000..3724bd4 --- /dev/null +++ b/source3/utils/conn_tdb.c @@ -0,0 +1,173 @@ +/* + Unix SMB/CIFS implementation. + Low-level connections.tdb access functions + Copyright (C) Volker Lendecke 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "smbd/globals.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "dbwrap/dbwrap_rbt.h" +#include "messages.h" +#include "conn_tdb.h" +#include "util_tdb.h" +#include "lib/util/string_wrappers.h" + +struct connections_forall_state { + struct db_context *session_by_pid; + int (*fn)(const struct connections_data *data, + void *private_data); + void *private_data; + int count; +}; + +struct connections_forall_session { + uid_t uid; + gid_t gid; + fstring machine; + fstring addr; + uint16_t cipher; + uint16_t dialect; + uint16_t signing; + uint8_t signing_flags; +}; + +static int collect_sessions_fn(struct smbXsrv_session_global0 *global, + void *connections_forall_state) +{ + NTSTATUS status; + struct connections_forall_state *state = + (struct connections_forall_state*)connections_forall_state; + + uint32_t id = global->session_global_id; + struct connections_forall_session sess; + + if (global->auth_session_info == NULL) { + sess.uid = -1; + sess.gid = -1; + } else { + sess.uid = global->auth_session_info->unix_token->uid; + sess.gid = global->auth_session_info->unix_token->gid; + } + fstrcpy(sess.machine, global->channels[0].remote_name); + fstrcpy(sess.addr, global->channels[0].remote_address); + sess.cipher = global->channels[0].encryption_cipher; + sess.signing = global->channels[0].signing_algo; + sess.dialect = global->connection_dialect; + sess.signing_flags = global->signing_flags; + + status = dbwrap_store(state->session_by_pid, + make_tdb_data((void*)&id, sizeof(id)), + make_tdb_data((void*)&sess, sizeof(sess)), + TDB_INSERT); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to store record: %s\n", nt_errstr(status))); + } + return 0; +} + +static int traverse_tcon_fn(struct smbXsrv_tcon_global0 *global, + void *connections_forall_state) +{ + NTSTATUS status; + struct connections_forall_state *state = + (struct connections_forall_state*)connections_forall_state; + + struct connections_data data; + + uint32_t sess_id = global->session_global_id; + struct connections_forall_session sess = { + .uid = -1, + .gid = -1, + }; + + TDB_DATA val = tdb_null; + + /* + * Note: that share_name is defined as array without a pointer. + * that's why it's always a valid pointer here. + */ + if (strlen(global->share_name) == 0) { + /* + * when a smbXsrv_tcon is created it's created + * with empty share_name first in order to allocate + * an id, before filling in the details. + */ + return 0; + } + + status = dbwrap_fetch(state->session_by_pid, state, + make_tdb_data((void*)&sess_id, sizeof(sess_id)), + &val); + if (NT_STATUS_IS_OK(status)) { + memcpy((uint8_t *)&sess, val.dptr, val.dsize); + } + + ZERO_STRUCT(data); + + data.pid = global->server_id; + data.cnum = global->tcon_global_id; + data.sess_id = sess_id; + fstrcpy(data.servicename, global->share_name); + data.uid = sess.uid; + data.gid = sess.gid; + fstrcpy(data.addr, sess.addr); + fstrcpy(data.machine, sess.machine); + data.start = global->creation_time; + data.encryption_flags = global->encryption_flags; + data.cipher = sess.cipher; + data.dialect = sess.dialect; + data.signing = sess.signing; + data.signing_flags = global->signing_flags; + + state->count++; + + return state->fn(&data, state->private_data); +} + +int connections_forall_read(int (*fn)(const struct connections_data *data, + void *private_data), + void *private_data) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct connections_forall_state *state = + talloc_zero(talloc_tos(), struct connections_forall_state); + NTSTATUS status; + int ret = -1; + + state->session_by_pid = db_open_rbt(state); + state->fn = fn; + state->private_data = private_data; + status = smbXsrv_session_global_traverse(collect_sessions_fn, state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse sessions: %s\n", + nt_errstr(status))); + goto done; + } + + status = smbXsrv_tcon_global_traverse(traverse_tcon_fn, state); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse tree connects: %s\n", + nt_errstr(status))); + goto done; + } + ret = state->count; +done: + talloc_free(frame); + return ret; +} diff --git a/source3/utils/conn_tdb.h b/source3/utils/conn_tdb.h new file mode 100644 index 0000000..2a6e04e --- /dev/null +++ b/source3/utils/conn_tdb.h @@ -0,0 +1,45 @@ +/* + Unix SMB/CIFS implementation. + Low-level connections.tdb access functions + Copyright (C) Volker Lendecke 2007 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* key and data in the connections database - used in smbstatus and smbd */ + +#include "source3/include/includes.h" + +struct connections_data { + struct server_id pid; + int cnum; + uint32_t sess_id; + uid_t uid; + gid_t gid; + fstring servicename; + fstring addr; + fstring machine; + NTTIME start; + uint8_t encryption_flags; + uint16_t cipher; + uint16_t dialect; + uint8_t signing_flags; + uint16_t signing; +}; + +/* The following definitions come from lib/conn_tdb.c */ + +int connections_forall_read(int (*fn)(const struct connections_data *data, + void *private_data), + void *private_data); diff --git a/source3/utils/dbwrap_tool.c b/source3/utils/dbwrap_tool.c new file mode 100644 index 0000000..eb97d64 --- /dev/null +++ b/source3/utils/dbwrap_tool.c @@ -0,0 +1,593 @@ +/* + Samba Unix/Linux CIFS implementation + + low level TDB/CTDB tool using the dbwrap interface + + Copyright (C) 2009 Michael Adam <obnox@samba.org> + Copyright (C) 2011 Bjoern Baumbach <bb@sernet.de> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "messages.h" +#include "util_tdb.h" +#include "cmdline_contexts.h" +#include "lib/param/param.h" + +enum dbwrap_op { OP_FETCH, OP_STORE, OP_DELETE, OP_ERASE, OP_LISTKEYS, + OP_EXISTS }; + +enum dbwrap_type { TYPE_INT32, TYPE_UINT32, TYPE_STRING, TYPE_HEX, TYPE_NONE }; + +static int dbwrap_tool_fetch_int32(struct db_context *db, + const char *keyname, + const char *data) +{ + int32_t value; + NTSTATUS status; + + status = dbwrap_fetch_int32_bystring(db, keyname, &value); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Error fetching int32 from key '%s': %s\n", + keyname, nt_errstr(status)); + return -1; + } + d_printf("%d\n", value); + + return 0; +} + +static int dbwrap_tool_fetch_uint32(struct db_context *db, + const char *keyname, + const char *data) +{ + uint32_t value; + NTSTATUS ret; + + ret = dbwrap_fetch_uint32_bystring(db, keyname, &value); + if (NT_STATUS_IS_OK(ret)) { + d_printf("%u\n", value); + return 0; + } else { + d_fprintf(stderr, "ERROR: could not fetch uint32 key '%s': " + "%s\n", nt_errstr(ret), keyname); + return -1; + } +} + +static int dbwrap_tool_fetch_string(struct db_context *db, + const char *keyname, + const char *data) +{ + TDB_DATA tdbdata; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + int ret; + + status = dbwrap_fetch_bystring(db, tmp_ctx, keyname, &tdbdata); + if (NT_STATUS_IS_OK(status)) { + d_printf("%-*.*s\n", (int)tdbdata.dsize, (int)tdbdata.dsize, + tdbdata.dptr); + ret = 0; + } else { + d_fprintf(stderr, "ERROR: could not fetch string key '%s': " + "%s\n", nt_errstr(status), keyname); + ret = -1; + } + + talloc_free(tmp_ctx); + return ret; +} + +static int dbwrap_tool_fetch_hex(struct db_context *db, + const char *keyname, + const char *data) +{ + TDB_DATA tdbdata; + DATA_BLOB datablob; + NTSTATUS status; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + char *hex_string; + int ret; + + status = dbwrap_fetch_bystring(db, tmp_ctx, keyname, &tdbdata); + if (NT_STATUS_IS_OK(status)) { + datablob.data = tdbdata.dptr; + datablob.length = tdbdata.dsize; + + hex_string = data_blob_hex_string_upper(tmp_ctx, &datablob); + if (hex_string == NULL) { + d_fprintf(stderr, "ERROR: could not get hex string " + "from data blob\n"); + ret = -1; + } else { + d_printf("%s\n", hex_string); + ret = 0; + } + } else { + d_fprintf(stderr, "ERROR: could not fetch hex key '%s': " + "%s\n", nt_errstr(status), keyname); + ret = -1; + } + + talloc_free(tmp_ctx); + return ret; +} + +static int dbwrap_tool_store_int32(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + int32_t value = (int32_t)strtol(data, NULL, 10); + + if (dbwrap_is_persistent(db)) { + status = dbwrap_trans_store_int32_bystring(db, keyname, value); + } else { + status = dbwrap_store_int32_bystring(db, keyname, value); + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "ERROR: could not store int32 key '%s': %s\n", + keyname, nt_errstr(status)); + return -1; + } + + return 0; +} + +static int dbwrap_tool_store_uint32(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + uint32_t value = (uint32_t)strtol(data, NULL, 10); + + if (dbwrap_is_persistent(db)) { + status = dbwrap_trans_store_uint32_bystring(db, keyname, value); + } else { + status = dbwrap_store_uint32_bystring(db, keyname, value); + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + "ERROR: could not store uint32 key '%s': %s\n", + keyname, nt_errstr(status)); + return -1; + } + + return 0; +} + +static int dbwrap_tool_store_string(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + TDB_DATA tdbdata; + + tdbdata = string_term_tdb_data(data); + + if (dbwrap_is_persistent(db)) { + status = dbwrap_trans_store_bystring(db, keyname, + tdbdata, + TDB_REPLACE); + } else { + status = dbwrap_store_bystring(db, keyname, + tdbdata, + TDB_REPLACE); + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + "ERROR: could not store string key '%s': %s\n", + keyname, nt_errstr(status)); + return -1; + } + + return 0; +} + +static int dbwrap_tool_store_hex(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + DATA_BLOB datablob; + TDB_DATA tdbdata; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + datablob = strhex_to_data_blob(tmp_ctx, data); + if(strlen(data) > 0 && datablob.length == 0) { + d_fprintf(stderr, + "ERROR: could not convert hex string to data blob\n" + " Not a valid hex string?\n"); + talloc_free(tmp_ctx); + return -1; + } + + tdbdata.dptr = (unsigned char *)datablob.data; + tdbdata.dsize = datablob.length; + + if (dbwrap_is_persistent(db)) { + status = dbwrap_trans_store_bystring(db, keyname, + tdbdata, + TDB_REPLACE); + } else { + status = dbwrap_store_bystring(db, keyname, + tdbdata, + TDB_REPLACE); + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + "ERROR: could not store string key '%s': %s\n", + keyname, nt_errstr(status)); + talloc_free(tmp_ctx); + return -1; + } + + talloc_free(tmp_ctx); + return 0; +} + +static int dbwrap_tool_delete(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + + if (dbwrap_is_persistent(db)) { + status = dbwrap_trans_delete_bystring(db, keyname); + } else { + status = dbwrap_delete_bystring(db, keyname); + } + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "ERROR deleting record %s : %s\n", + keyname, nt_errstr(status)); + return -1; + } + + return 0; +} + +static int dbwrap_tool_exists(struct db_context *db, + const char *keyname, + const char *data) +{ + bool result; + + result = dbwrap_exists(db, string_term_tdb_data(keyname)); + + if (result) { + d_fprintf(stdout, "Key %s exists\n", keyname); + } else { + d_fprintf(stdout, "Key %s does not exist\n", keyname); + } + + return (result)?0:1; +} + +/** + * dbwrap_tool_erase: erase the whole data base + * the keyname argument is not used. + */ +static int dbwrap_tool_erase(struct db_context *db, + const char *keyname, + const char *data) +{ + int ret; + + ret = dbwrap_wipe(db); + + if (ret != 0) { + d_fprintf(stderr, "ERROR erasing the database\n"); + return -1; + } + + return 0; +} + +static int listkey_fn(struct db_record *rec, void *private_data) +{ + TDB_DATA key = dbwrap_record_get_key(rec); + size_t length = key.dsize; + unsigned char *p = (unsigned char *)key.dptr; + + while (length--) { + if (isprint(*p) && !strchr("\"\\", *p)) { + d_printf("%c", *p); + } else { + d_printf("\\%02X", *p); + } + p++; + } + + d_printf("\n"); + + return 0; +} + +static int dbwrap_tool_listkeys(struct db_context *db, + const char *keyname, + const char *data) +{ + NTSTATUS status; + + status = dbwrap_traverse_read(db, listkey_fn, NULL, NULL); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "ERROR listing db keys\n"); + return -1; + } + + return 0; +} + +struct dbwrap_op_dispatch_table { + enum dbwrap_op op; + enum dbwrap_type type; + int (*cmd)(struct db_context *db, + const char *keyname, + const char *data); +}; + +struct dbwrap_op_dispatch_table dispatch_table[] = { + { OP_FETCH, TYPE_INT32, dbwrap_tool_fetch_int32 }, + { OP_FETCH, TYPE_UINT32, dbwrap_tool_fetch_uint32 }, + { OP_FETCH, TYPE_STRING, dbwrap_tool_fetch_string }, + { OP_FETCH, TYPE_HEX, dbwrap_tool_fetch_hex }, + { OP_STORE, TYPE_INT32, dbwrap_tool_store_int32 }, + { OP_STORE, TYPE_UINT32, dbwrap_tool_store_uint32 }, + { OP_STORE, TYPE_STRING, dbwrap_tool_store_string }, + { OP_STORE, TYPE_HEX, dbwrap_tool_store_hex }, + { OP_DELETE, TYPE_INT32, dbwrap_tool_delete }, + { OP_ERASE, TYPE_INT32, dbwrap_tool_erase }, + { OP_LISTKEYS, TYPE_INT32, dbwrap_tool_listkeys }, + { OP_EXISTS, TYPE_STRING, dbwrap_tool_exists }, + { 0, 0, NULL }, +}; + +int main(int argc, const char **argv) +{ + struct tevent_context *evt_ctx; + struct messaging_context *msg_ctx; + struct db_context *db; + + uint16_t count; + + const char *dbname; + const char *opname; + enum dbwrap_op op; + const char *keyname = ""; + const char *keytype = "int32"; + enum dbwrap_type type; + const char *valuestr = "0"; + int persistent = 0; + int non_persistent = 0; + int tdb_flags = TDB_DEFAULT; + + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + + int ret = 1; + bool ok; + + struct poptOption popt_options[] = { + POPT_AUTOHELP + POPT_COMMON_SAMBA + { "non-persistent", 0, POPT_ARG_NONE, &non_persistent, 0, + "treat the database as non-persistent " + "(CAVEAT: This mode might wipe your database!)", + NULL }, + { "persistent", 0, POPT_ARG_NONE, &persistent, 0, + "treat the database as persistent", + NULL }, + POPT_COMMON_VERSION + POPT_TABLEEND + }; + int opt; + const char **extra_argv; + int extra_argc = 0; + poptContext pc; + + smb_init_locale(); + + setup_logging(argv[0], DEBUG_DEFAULT_STDERR); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + popt_options, + POPT_CONTEXT_KEEP_FIRST); + if (!ok) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "Invalid option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto done; + } + } + + /* setup the remaining options for the main program to use */ + extra_argv = poptGetArgs(pc); + if (extra_argv) { + extra_argv++; + while (extra_argv[extra_argc]) extra_argc++; + } + + if ((extra_argc < 2) || (extra_argc > 5)) { + d_fprintf(stderr, + "USAGE: %s [options] <database> <op> [<key> [<type> " + "[<value>]]]\n" + " ops: fetch, store, delete, exists, " + "erase, listkeys\n" + " types: int32, uint32, string, hex\n", + argv[0]); + goto done; + } + + if ((persistent + non_persistent) != 1) { + d_fprintf(stderr, "ERROR: you must specify exactly one " + "of --persistent and --non-persistent\n"); + goto done; + } + if (non_persistent == 1) { + tdb_flags |= TDB_CLEAR_IF_FIRST; + } + + dbname = extra_argv[0]; + opname = extra_argv[1]; + + if (strcmp(opname, "store") == 0) { + if (extra_argc != 5) { + d_fprintf(stderr, "ERROR: operation 'store' requires " + "value argument\n"); + goto done; + } + valuestr = extra_argv[4]; + keytype = extra_argv[3]; + keyname = extra_argv[2]; + op = OP_STORE; + } else if (strcmp(opname, "fetch") == 0) { + if (extra_argc != 4) { + d_fprintf(stderr, "ERROR: operation 'fetch' requires " + "type but not value argument\n"); + goto done; + } + op = OP_FETCH; + keytype = extra_argv[3]; + keyname = extra_argv[2]; + } else if (strcmp(opname, "delete") == 0) { + if (extra_argc != 3) { + d_fprintf(stderr, "ERROR: operation 'delete' does " + "not allow type nor value argument\n"); + goto done; + } + keyname = extra_argv[2]; + op = OP_DELETE; + } else if (strcmp(opname, "erase") == 0) { + if (extra_argc != 2) { + d_fprintf(stderr, "ERROR: operation 'erase' does " + "not take a key argument\n"); + goto done; + } + op = OP_ERASE; + } else if (strcmp(opname, "listkeys") == 0) { + if (extra_argc != 2) { + d_fprintf(stderr, "ERROR: operation 'listkeys' does " + "not take a key argument\n"); + goto done; + } + op = OP_LISTKEYS; + } else if (strcmp(opname, "exists") == 0) { + if (extra_argc != 3) { + d_fprintf(stderr, "ERROR: operation 'exists' does " + "not allow type nor value argument\n"); + goto done; + } + keyname = extra_argv[2]; + op = OP_EXISTS; + keytype = "string"; + } else { + d_fprintf(stderr, + "ERROR: invalid op '%s' specified\n" + " supported ops: fetch, store, delete, exists, " + "erase, listkeys\n", + opname); + goto done; + } + + if (strcmp(keytype, "int32") == 0) { + type = TYPE_INT32; + } else if (strcmp(keytype, "uint32") == 0) { + type = TYPE_UINT32; + } else if (strcmp(keytype, "string") == 0) { + type = TYPE_STRING; + } else if (strcmp(keytype, "hex") == 0) { + type = TYPE_HEX; + } else if (strcmp(keytype, "none") == 0) { + type = TYPE_NONE; + } else { + d_fprintf(stderr, "ERROR: invalid type '%s' specified.\n" + " supported types: int32, uint32, " + "string, hex, none\n", + keytype); + goto done; + } + + evt_ctx = samba_tevent_context_init(mem_ctx); + if (evt_ctx == NULL) { + d_fprintf(stderr, "ERROR: could not init event context\n"); + goto done; + } + + msg_ctx = messaging_init(mem_ctx, evt_ctx); + if (msg_ctx == NULL) { + d_fprintf(stderr, "ERROR: could not init messaging context\n"); + goto done; + } + + switch (op) { + case OP_FETCH: + case OP_STORE: + case OP_DELETE: + case OP_ERASE: + case OP_LISTKEYS: + case OP_EXISTS: + db = db_open(mem_ctx, dbname, 0, tdb_flags, O_RDWR | O_CREAT, + 0644, DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (db == NULL) { + d_fprintf(stderr, "ERROR: could not open dbname\n"); + goto done; + } + break; + default: + db = NULL; + break; + } + + for (count = 0; dispatch_table[count].cmd != NULL; count++) { + if ((op == dispatch_table[count].op) && + (type == dispatch_table[count].type)) + { + ret = dispatch_table[count].cmd(db, keyname, valuestr); + break; + } + } + +done: + poptFreeContext(pc); + TALLOC_FREE(mem_ctx); + return ret; +} diff --git a/source3/utils/dbwrap_torture.c b/source3/utils/dbwrap_torture.c new file mode 100644 index 0000000..ec33853 --- /dev/null +++ b/source3/utils/dbwrap_torture.c @@ -0,0 +1,366 @@ +/* + Samba Linux/Unix CIFS implementation + + simple tool to test persistent databases + + Copyright (C) Michael Adam 2009 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "messages.h" +#include "lib/util/util_tdb.h" +#include "lib/param/param.h" + +#if 0 +#include "lib/events/events.h" +#include "system/filesys.h" +#include "popt.h" +#include "cmdline.h" + +#include <sys/time.h> +#include <time.h> +#endif + +#define DEFAULT_DB_NAME "transaction.tdb" + +static int timelimit = 10; +static int torture_delay = 0; +static int verbose = 0; +static int no_trans = 0; +static const char *db_name = DEFAULT_DB_NAME; + + +static unsigned int pnn; + +static TDB_DATA old_data; + +static int success = true; + +static void print_counters(void) +{ + int i; + uint32_t *old_counters; + + printf("[%4u] Counters: ", (unsigned int)getpid()); + old_counters = (uint32_t *)old_data.dptr; + for (i=0; i < old_data.dsize/sizeof(uint32_t); i++) { + printf("%6u ", old_counters[i]); + } + printf("\n"); +} + +static void each_second(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval t, + void *private_data) +{ + struct db_context *db = talloc_get_type(private_data, struct db_context); + + print_counters(); + + tevent_add_timer(ev, db, timeval_current_ofs(1, 0), each_second, db); +} + +static bool check_counters(struct db_context *db, TDB_DATA data) +{ + int i; + uint32_t *counters, *old_counters; + + counters = (uint32_t *)data.dptr; + old_counters = (uint32_t *)old_data.dptr; + + /* check that all the counters are monotonic increasing */ + for (i=0; i < old_data.dsize/sizeof(uint32_t); i++) { + if (counters[i] < old_counters[i]) { + printf("[%4u] ERROR: counters has decreased for node %u From %u to %u\n", + (unsigned int)getpid(), i, old_counters[i], counters[i]); + success = false; + return false; + } + } + + if (old_data.dsize != data.dsize) { + old_data.dsize = data.dsize; + old_data.dptr = (unsigned char*)talloc_realloc_size(db, old_data.dptr, old_data.dsize); + } + + memcpy(old_data.dptr, data.dptr, data.dsize); + if (verbose) print_counters(); + + return true; +} + + +static void do_sleep(unsigned int sec) +{ + unsigned int i; + + if (sec == 0) { + return; + } + + for (i=0; i<sec; i++) { + if (verbose) printf("."); + sleep(1); + } + + if (verbose) printf("\n"); +} + +static void test_store_records(struct db_context *db, struct tevent_context *ev) +{ + TDB_DATA key; + uint32_t *counters; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct timeval start; + + key = string_term_tdb_data("testkey"); + + start = timeval_current(); + while ((timelimit == 0) || (timeval_elapsed(&start) < timelimit)) { + struct db_record *rec; + TDB_DATA data; + TDB_DATA value; + int ret; + NTSTATUS status; + + if (!no_trans) { + if (verbose) DEBUG(1, ("starting transaction\n")); + ret = dbwrap_transaction_start(db); + if (ret != 0) { + DEBUG(0, ("Failed to start transaction on node " + "%d\n", pnn)); + goto fail; + } + if (verbose) DEBUG(1, ("transaction started\n")); + do_sleep(torture_delay); + } + + if (verbose) DEBUG(1, ("calling fetch_lock\n")); + rec = dbwrap_fetch_locked(db, tmp_ctx, key); + if (rec == NULL) { + DEBUG(0, ("Failed to fetch record\n")); + goto fail; + } + if (verbose) DEBUG(1, ("fetched record ok\n")); + do_sleep(torture_delay); + value = dbwrap_record_get_value(rec); + + data.dsize = MAX(value.dsize, sizeof(uint32_t) * (pnn+1)); + data.dptr = (unsigned char *)talloc_zero_size(tmp_ctx, + data.dsize); + if (data.dptr == NULL) { + DEBUG(0, ("Failed to allocate data\n")); + goto fail; + } + memcpy(data.dptr, value.dptr, value.dsize); + + counters = (uint32_t *)data.dptr; + + /* bump our counter */ + counters[pnn]++; + + if (verbose) DEBUG(1, ("storing data\n")); + status = dbwrap_record_store(rec, data, TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to store record\n")); + if (!no_trans) { + ret = dbwrap_transaction_cancel(db); + if (ret != 0) { + DEBUG(0, ("Error cancelling transaction.\n")); + } + } + goto fail; + } + talloc_free(rec); + if (verbose) DEBUG(1, ("stored data ok\n")); + do_sleep(torture_delay); + + if (!no_trans) { + if (verbose) DEBUG(1, ("calling transaction_commit\n")); + ret = dbwrap_transaction_commit(db); + if (ret != 0) { + DEBUG(0, ("Failed to commit transaction\n")); + goto fail; + } + if (verbose) DEBUG(1, ("transaction committed\n")); + } + + /* store the counters and verify that they are sane */ + if (verbose || (pnn == 0)) { + if (!check_counters(db, data)) { + goto fail; + } + } + talloc_free(data.dptr); + + do_sleep(torture_delay); + } + + goto done; + +fail: + success = false; + +done: + talloc_free(tmp_ctx); + return; +} + +/* + main program +*/ +int main(int argc, const char *argv[]) +{ + TALLOC_CTX *mem_ctx; + struct tevent_context *ev_ctx; + struct messaging_context *msg_ctx; + struct db_context *db; + + int unsafe_writes = 0; + struct poptOption popt_options[] = { + POPT_AUTOHELP + POPT_COMMON_SAMBA + { "timelimit", 't', POPT_ARG_INT, &timelimit, 0, "timelimit", "INTEGER" }, + { "delay", 'D', POPT_ARG_INT, &torture_delay, 0, "delay (in seconds) between operations", "INTEGER" }, + { "verbose", 'v', POPT_ARG_NONE, &verbose, 0, "switch on verbose mode", NULL }, + { "db-name", 'N', POPT_ARG_STRING, &db_name, 0, "name of the test db", "NAME" }, + { "no-trans", 'n', POPT_ARG_NONE, &no_trans, 0, "use fetch_lock/record store instead of transactions", NULL }, + { "unsafe-writes", 'u', POPT_ARG_NONE, &unsafe_writes, 0, "do not use tdb transactions when writing", NULL }, + POPT_COMMON_VERSION + POPT_TABLEEND + }; + int opt; + const char **extra_argv; + int extra_argc = 0; + poptContext pc; + int tdb_flags; + bool ok; + int ret = 1; + struct loadparm_context *lp_ctx = NULL; + + mem_ctx = talloc_stackframe(); + + if (verbose) { + setbuf(stdout, (char *)NULL); /* don't buffer */ + } else { + setlinebuf(stdout); + } + + smb_init_locale(); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + popt_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + default: + fprintf(stderr, "Invalid option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + goto done; + } + } + + /* setup the remaining options for the main program to use */ + extra_argv = poptGetArgs(pc); + if (extra_argv) { + extra_argv++; + while (extra_argv[extra_argc]) extra_argc++; + } + + ev_ctx = samba_tevent_context_init(mem_ctx); + if (ev_ctx == NULL) { + d_fprintf(stderr, "ERROR: could not init event context\n"); + goto done; + } + + msg_ctx = messaging_init(mem_ctx, ev_ctx); + if (msg_ctx == NULL) { + d_fprintf(stderr, "ERROR: could not init messaging context\n"); + goto done; + } + + if (unsafe_writes == 1) { + tdb_flags = TDB_NOSYNC; + } else { + tdb_flags = TDB_DEFAULT; + } + + if (no_trans) { + tdb_flags |= TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH; + } + + db = db_open(mem_ctx, db_name, 0, tdb_flags, O_RDWR | O_CREAT, 0644, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + + if (db == NULL) { + d_fprintf(stderr, "failed to open db '%s': %s\n", db_name, + strerror(errno)); + goto done; + } + + if (get_my_vnn() == NONCLUSTER_VNN) { + set_my_vnn(0); + } + pnn = get_my_vnn(); + + printf("Starting test on node %u. running for %u seconds. " + "sleep delay: %u seconds.\n", pnn, timelimit, torture_delay); + + if (!verbose && (pnn == 0)) { + tevent_add_timer(ev_ctx, db, timeval_current_ofs(1, 0), each_second, db); + } + + test_store_records(db, ev_ctx); + + if (verbose || (pnn == 0)) { + if (success != true) { + printf("The test FAILED\n"); + ret = 2; + } else { + printf("SUCCESS!\n"); + ret = 0; + } + } + +done: + poptFreeContext(pc); + talloc_free(mem_ctx); + return ret; +} diff --git a/source3/utils/destroy_netlogon_creds_cli.c b/source3/utils/destroy_netlogon_creds_cli.c new file mode 100644 index 0000000..a2e1952 --- /dev/null +++ b/source3/utils/destroy_netlogon_creds_cli.c @@ -0,0 +1,136 @@ +/* + * Unix SMB/CIFS implementation. + * Garble the netlogon_creds_cli key for testing purposes + * Copyright (C) Volker Lendecke 2018 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include <talloc.h> +#include <tevent.h> +#include "messages.h" +#include "lib/util/talloc_stack.h" +#include "lib/param/loadparm.h" +#include "lib/param/param.h" +#include "libcli/auth/netlogon_creds_cli.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_open.h" + +int main(int argc, const char *argv[]) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct tevent_context *ev; + struct messaging_context *msg_ctx; + struct loadparm_context *lp_ctx; + struct db_context *global_db; + struct netlogon_creds_cli_context *ctx; + struct netlogon_creds_CredentialState *creds; + NTSTATUS status; + int ret = 1; + + smb_init_locale(); + + if (!lp_load_global(get_dyn_CONFIGFILE())) { + fprintf(stderr, "error opening config file %s. Error was %s\n", + get_dyn_CONFIGFILE(), strerror(errno)); + goto done; + } + + if (argc != 4) { + fprintf(stderr, "usage: %s cli_computer domain dc\n", argv[0]); + goto done; + } + + lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + fprintf(stderr, "loadparm_init_s3 failed\n"); + goto done; + } + + ev = samba_tevent_context_init(mem_ctx); + if (ev == NULL) { + fprintf(stderr, "samba3_tevent_context_init failed\n"); + goto done; + } + msg_ctx = messaging_init(mem_ctx, ev); + if (msg_ctx == NULL) { + fprintf(stderr, "messaging_init failed\n"); + goto done; + } + + global_db = db_open( + mem_ctx, + lpcfg_private_db_path(mem_ctx, lp_ctx, "netlogon_creds_cli"), + 0, TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, + O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_2, + DBWRAP_FLAG_OPTIMIZE_READONLY_ACCESS); + if (global_db == NULL) { + fprintf(stderr, "db_open failed\n"); + goto done; + } + + status = netlogon_creds_cli_set_global_db(lp_ctx, &global_db); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "netlogon_creds_cli_set_global_db failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = netlogon_creds_cli_context_global( + lp_ctx, + msg_ctx, + talloc_asprintf(mem_ctx, "%s$", argv[1]), + SEC_CHAN_WKSTA, + argv[3], + argv[2], + "", + mem_ctx, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "netlogon_creds_cli_context_global failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = netlogon_creds_cli_lock(ctx, + mem_ctx, + &creds); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "netlogon_creds_cli_get failed: %s\n", + nt_errstr(status)); + goto done; + } + + creds->session_key[0]++; + + status = netlogon_creds_cli_store(ctx, creds); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "netlogon_creds_cli_store failed: %s\n", + nt_errstr(status)); + goto done; + } + + TALLOC_FREE(creds); + + ret = 0; +done: + TALLOC_FREE(mem_ctx); + return ret; +} diff --git a/source3/utils/eventlogadm.c b/source3/utils/eventlogadm.c new file mode 100644 index 0000000..f831927 --- /dev/null +++ b/source3/utils/eventlogadm.c @@ -0,0 +1,506 @@ + +/* + * Samba Unix/Linux SMB client utility + * Write Eventlog records to a tdb, perform other eventlog related functions + * + * + * Copyright (C) Brian Moran 2005. + * Copyright (C) Guenther Deschner 2009. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "includes.h" +#include "lib/eventlog/eventlog.h" +#include "registry.h" +#include "registry/reg_api.h" +#include "registry/reg_init_basic.h" +#include "registry/reg_util_token.h" +#include "registry/reg_backend_db.h" +#include "../libcli/registry/util_reg.h" +#include "cmdline_contexts.h" +#include "lib/util/string_wrappers.h" + +extern int optind; +extern char *optarg; + +int opt_debug = 0; + +static void usage( char *s ) +{ + printf( "\nUsage: %s [OPTION]\n\n", s ); + printf( " -o write <Eventlog Name> \t\t\t\t\tWrites records to eventlog from STDIN\n" ); + printf( " -o addsource <EventlogName> <sourcename> <msgfileDLLname> \tAdds the specified source & DLL eventlog registry entry\n" ); + printf( " -o dump <Eventlog Name> <starting_record>\t\t\t\t\tDump stored eventlog entries on STDOUT\n" ); + printf( "\nMiscellaneous options:\n" ); + printf( " -s <filename>\t\t\t\t\t\t\tUse configuration file <filename>.\n"); + printf( " -d\t\t\t\t\t\t\t\tturn debug on\n" ); + printf( " -h\t\t\t\t\t\t\t\tdisplay help\n\n" ); +} + +static void display_eventlog_names( void ) +{ + const char **elogs; + int i; + + elogs = lp_eventlog_list( ); + printf( "Active eventlog names:\n" ); + printf( "--------------------------------------\n" ); + if ( elogs ) { + for ( i = 0; elogs[i]; i++ ) { + printf( "\t%s\n", elogs[i] ); + } + } + else + printf( "\t<None specified>\n"); +} + +/********************************************************************* + for an eventlog, add in a source name. If the eventlog doesn't + exist (not in the list) do nothing. If a source for the log + already exists, change the information (remove, replace) +*********************************************************************/ +static bool eventlog_add_source( const char *eventlog, const char *sourcename, + const char *messagefile ) +{ + /* Find all of the eventlogs, add keys for each of them */ + /* need to add to the value KEY_EVENTLOG/<eventlog>/Sources string (Creating if necessary) + need to add KEY of source to KEY_EVENTLOG/<eventlog>/<source> */ + + const char **elogs = lp_eventlog_list( ); + const char **wrklist, **wp; + char *evtlogpath = NULL; + int ii = 0; + bool already_in; + int i; + int numsources = 0; + TALLOC_CTX *ctx = talloc_stackframe(); + WERROR werr; + struct registry_key *key_hive, *key_eventlog, *key_source; + struct security_token *token = NULL; + const char *hive_name, *relpath; + enum winreg_CreateAction action; + struct registry_value *value; + static const uint32_t ACCESS = REG_KEY_READ | REG_KEY_WRITE; + bool ret = false; + + if (!elogs) { + d_printf("No Eventlogs configured\n"); + goto done; + } + + for ( i = 0; elogs[i]; i++ ) { + if ( strequal( elogs[i], eventlog ) ) + break; + } + + if ( !elogs[i] ) { + d_printf("Eventlog [%s] not found in list of valid event logs\n", + eventlog); + goto done; + } + + /* have to assume that the evenlog key itself exists at this point */ + /* add in a key of [sourcename] under the eventlog key */ + + /* todo add to Sources */ + + evtlogpath = talloc_asprintf(ctx, "%s\\%s", KEY_EVENTLOG, eventlog); + if (!evtlogpath) { + d_printf("Out of memory\n"); + goto done; + } + + relpath = evtlogpath + sizeof(KEY_EVENTLOG); + hive_name = talloc_strndup(ctx, evtlogpath, relpath - evtlogpath); + if (!hive_name) { + d_printf("Out of memory\n"); + goto done; + } + relpath++; + + werr = ntstatus_to_werror(registry_create_admin_token(ctx, &token)); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to create admin token: %s\n", win_errstr(werr)); + goto done; + } + + werr = reg_openhive(ctx, hive_name, ACCESS, token, &key_hive); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to open hive [%s]: %s\n", hive_name, win_errstr(werr)); + goto done; + } + + werr = reg_openkey(ctx, key_hive, relpath, ACCESS, &key_eventlog); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to open key [%s]: %s\n", evtlogpath, win_errstr(werr)); + goto done; + } + + werr = reg_queryvalue(ctx, key_eventlog, "Sources", &value); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to get value \"Sources\" for [%s]: %s\n", evtlogpath, win_errstr(werr)); + goto done; + } + /* perhaps this adding a new string to a multi_sz should be a fn? */ + /* check to see if it's there already */ + + if ( value->type != REG_MULTI_SZ ) { + d_printf("Wrong type for \"Sources\", should be REG_MULTI_SZ\n"); + goto done; + } + /* convert to a 'regulah' chars to do some comparisons */ + + already_in = false; + wrklist = NULL; + dump_data(1, value->data.data, value->data.length); + + if (!pull_reg_multi_sz(ctx, &value->data, &wrklist)) { + d_printf("Failed to pull REG_MULTI_SZ from \"Sources\"\n"); + goto done; + } + + for (ii=0; wrklist[ii]; ii++) { + numsources++; + } + + if (numsources > 0) { + /* see if it's in there already */ + wp = wrklist; + + while (wp && *wp ) { + if ( strequal( *wp, sourcename ) ) { + d_printf("Source name [%s] already in list for [%s] \n", + sourcename, eventlog); + already_in = true; + break; + } + wp++; + } + } else { + d_printf("Nothing in the sources list, this might be a problem\n"); + } + + if ( !already_in ) { + /* make a new list with an additional entry; copy values, add another */ + wp = talloc_realloc(ctx, wrklist, const char *, numsources + 2 ); + if ( !wp ) { + d_printf("Out of memory\n"); + goto done; + } + + wp[numsources] = sourcename; + wp[numsources+1] = NULL; + if (!push_reg_multi_sz(ctx, &value->data, wp)) { + d_printf("Failed to push Sources\n"); + goto done; + } + dump_data( 1, value->data.data, value->data.length); + werr = reg_setvalue(key_eventlog, "Sources", value); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to set value Sources: %s\n", win_errstr(werr)); + goto done; + } + } else { + d_printf("Source name [%s] found in existing list of sources\n", + sourcename); + } + + werr = reg_createkey(ctx, key_eventlog, sourcename, ACCESS, &key_source, &action); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to create subkey \"%s\" of \"%s\": %s\n", sourcename, evtlogpath, win_errstr(werr)); + goto done; + } + + if (action == REG_CREATED_NEW_KEY) { + d_printf(" Source name [%s] for eventlog [%s] didn't exist, adding \n", + sourcename, eventlog); + } + + /* at this point KEY_EVENTLOG/<eventlog>/<sourcename> key is in there. Now need to add EventMessageFile */ + + /* now add the values to the KEY_EVENTLOG/Application form key */ + d_printf("Storing EventMessageFile [%s] to eventlog path of [%s]\n", + messagefile, evtlogpath); + + if (!push_reg_sz(ctx, &value->data, messagefile)) { + d_printf("Failed to push \"EventMessageFile\"\n"); + goto done; + } + value->type = REG_SZ; + + werr = reg_setvalue(key_source, "EventMessageFile", value); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to set value \"EventMessageFile\": %s\n", win_errstr(werr)); + return false; + } + ret = true; +done: + talloc_free(ctx); + return ret; +} + +static int DoAddSourceCommand( int argc, char **argv, bool debugflag, char *exename ) +{ + WERROR werr; + + if ( argc < 3 ) { + printf( "need more arguments:\n" ); + printf( "-o addsource EventlogName SourceName /path/to/EventMessageFile.dll\n" ); + return -1; + } + + /* must open the registry before we access it */ + werr = registry_init_common(); + if (!W_ERROR_IS_OK(werr)) { + printf("Can't open the registry: %s.\n", win_errstr(werr)); + return -1; + } + werr = regdb_transaction_start(); + if (!W_ERROR_IS_OK(werr)) { + printf("Can't start transaction on registry: %s.\n", win_errstr(werr)); + return -1; + } + + if ( !eventlog_add_source( argv[0], argv[1], argv[2] ) ) { + regdb_transaction_cancel(); + return -2; + } + werr = regdb_transaction_commit(); + if (!W_ERROR_IS_OK(werr)) { + printf("Failed to commit transaction on registry: %s.\n", win_errstr(werr)); + return -1; + } + return 0; +} + +static int DoWriteCommand( int argc, char **argv, bool debugflag, char *exename ) +{ + FILE *f1; + char *argfname; + ELOG_TDB *etdb; + NTSTATUS status; + + /* fixed constants are bad bad bad */ + char linein[1024]; + bool is_eor; + struct eventlog_Record_tdb ee; + uint32_t record_number = 0; + TALLOC_CTX *mem_ctx = talloc_tos(); + + f1 = stdin; + if ( !f1 ) { + printf( "Can't open STDIN\n" ); + return -1; + } + + if ( debugflag ) { + printf( "Starting write for eventlog [%s]\n", argv[0] ); + display_eventlog_names( ); + } + + argfname = argv[0]; + + if ( !( etdb = elog_open_tdb( argfname, False, False ) ) ) { + printf( "can't open the eventlog TDB (%s)\n", argfname ); + return -1; + } + + ZERO_STRUCT( ee ); /* MUST initialize between records */ + + while ( !feof( f1 ) ) { + if (fgets( linein, sizeof( linein ) - 1, f1 ) == NULL) { + break; + } + if ((strlen(linein) > 0) + && (linein[strlen(linein)-1] == '\n')) { + linein[strlen(linein)-1] = 0; + } + + if ( debugflag ) + printf( "Read line [%s]\n", linein ); + + is_eor = False; + + + parse_logentry( mem_ctx, ( char * ) &linein, &ee, &is_eor ); + /* should we do something with the return code? */ + + if ( is_eor ) { + fixup_eventlog_record_tdb( &ee ); + + if ( opt_debug ) + printf( "record number [%d], tg [%d] , tw [%d]\n", + ee.record_number, (int)ee.time_generated, (int)ee.time_written ); + + if ( ee.time_generated != 0 ) { + + /* printf("Writing to the event log\n"); */ + + status = evlog_push_record_tdb( mem_ctx, ELOG_TDB_CTX(etdb), + &ee, &record_number ); + if ( !NT_STATUS_IS_OK(status) ) { + printf( "Can't write to the event log: %s\n", + nt_errstr(status) ); + } else { + if ( opt_debug ) + printf( "Wrote record %d\n", + record_number ); + } + } else { + if ( opt_debug ) + printf( "<null record>\n" ); + } + ZERO_STRUCT( ee ); /* MUST initialize between records */ + } + } + + elog_close_tdb( etdb , False ); + + return 0; +} + +static int DoDumpCommand(int argc, char **argv, bool debugflag, char *exename) +{ + ELOG_TDB *etdb; + TALLOC_CTX *mem_ctx = talloc_tos(); + uint32_t count = 1; + + if (argc > 2) { + return -1; + } + + if (argc > 1) { + count = atoi(argv[1]); + } + + etdb = elog_open_tdb(argv[0], false, true); + if (!etdb) { + printf("can't open the eventlog TDB (%s)\n", argv[0]); + return -1; + } + + while (1) { + + struct eventlog_Record_tdb *r; + char *s; + + r = evlog_pull_record_tdb(mem_ctx, etdb->tdb, count); + if (!r) { + break; + } + + printf("displaying record: %d\n", count); + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, eventlog_Record_tdb, r); + if (s) { + printf("%s\n", s); + talloc_free(s); + } + count++; + } + + elog_close_tdb(etdb, false); + + return 0; +} + +/* would be nice to use the popT stuff here, however doing so forces us to drag in a lot of other infrastructure */ + +int main( int argc, char *argv[] ) +{ + int opt, rc; + char *exename; + char *configfile = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + + + fstring opname; + + smb_init_locale(); + + opt_debug = 0; /* todo set this from getopts */ + + exename = argv[0]; + + /* default */ + + fstrcpy( opname, "write" ); /* the default */ + +#if 0 /* TESTING CODE */ + eventlog_add_source( "System", "TestSourceX", "SomeTestPathX" ); +#endif + while ( ( opt = getopt( argc, argv, "dho:s:" ) ) != EOF ) { + switch ( opt ) { + + case 'o': + fstrcpy( opname, optarg ); + break; + + case 'h': + usage( exename ); + display_eventlog_names( ); + exit( 0 ); + break; + + case 'd': + opt_debug = 1; + break; + case 's': + configfile = talloc_strdup(frame, optarg); + break; + + } + } + + argc -= optind; + argv += optind; + + if ( argc < 1 ) { + printf( "\nNot enough arguments!\n" ); + usage( exename ); + exit( 1 ); + } + + if ( configfile == NULL ) { + lp_load_global(get_dyn_CONFIGFILE()); + } else if (!lp_load_global(configfile)) { + printf("Unable to parse configfile '%s'\n",configfile); + exit( 1 ); + } + + /* note that the separate command types should call usage if they need to... */ + while ( 1 ) { + if ( !strcasecmp_m( opname, "addsource" ) ) { + rc = DoAddSourceCommand( argc, argv, opt_debug, + exename ); + break; + } + if ( !strcasecmp_m( opname, "write" ) ) { + rc = DoWriteCommand( argc, argv, opt_debug, exename ); + break; + } + if ( !strcasecmp_m( opname, "dump" ) ) { + rc = DoDumpCommand( argc, argv, opt_debug, exename ); + break; + } + printf( "unknown command [%s]\n", opname ); + usage( exename ); + exit( 1 ); + break; + } + TALLOC_FREE(frame); + return rc; +} diff --git a/source3/utils/interact.c b/source3/utils/interact.c new file mode 100644 index 0000000..f8fed6d --- /dev/null +++ b/source3/utils/interact.c @@ -0,0 +1,135 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Functions to interact with an user. + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2011 + * + */ + +#include "includes.h" +#include "system/filesys.h" + +#include "interact.h" + +#include <termios.h> + +static const char* get_editor(void) { + static char editor[64] = {0}; + + if (editor[0] == '\0') { + const char *tmp = getenv("VISUAL"); + if (tmp == NULL) { + tmp = getenv("EDITOR"); + } + if (tmp == NULL) { + tmp = "vi"; + } + snprintf(editor, sizeof(editor), "%s", tmp); + } + + return editor; +} + +int interact_prompt(const char* msg, const char* acc, char def) { + struct termios old_tio, new_tio; + int c; + + tcgetattr(STDIN_FILENO, &old_tio); + new_tio=old_tio; + new_tio.c_lflag &=(~ICANON & ~ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &new_tio); + + do { + d_printf("%s? [%c]\n", msg, def); + fflush(stdout); + c = getchar(); + if (c == '\n') { + c = def; + break; + } + else if (strchr(acc, tolower(c)) != NULL) { + break; + } + d_printf("Invalid input '%c'\n", c); + } while(c != EOF); + tcsetattr(STDIN_FILENO, TCSANOW, &old_tio); + return c; +} + + +char* interact_edit(TALLOC_CTX* mem_ctx, const char* str) { + char fname[] = "/tmp/net_idmap_check.XXXXXX"; + char buf[128]; + char* ret = NULL; + FILE* file; + mode_t mask; + int fd; + + mask = umask(S_IRWXO | S_IRWXG); + fd = mkstemp(fname); + umask(mask); + if (fd == -1) { + DEBUG(0, ("failed to mkstemp %s: %s\n", fname, + strerror(errno))); + return NULL; + } + + file = fdopen(fd, "w"); + if (!file) { + DEBUG(0, ("failed to open %s for writing: %s\n", fname, + strerror(errno))); + close(fd); + unlink(fname); + return NULL; + } + + fprintf(file, "%s", str); + fclose(file); + + snprintf(buf, sizeof(buf), "%s %s\n", get_editor(), fname); + if (system(buf) != 0) { + DEBUG(0, ("failed to start editor %s: %s\n", buf, + strerror(errno))); + unlink(fname); + return NULL; + } + + file = fopen(fname, "r"); + if (!file) { + DEBUG(0, ("failed to open %s for reading: %s\n", fname, + strerror(errno))); + unlink(fname); + return NULL; + } + while ( fgets(buf, sizeof(buf), file) ) { + ret = talloc_strdup_append(ret, buf); + } + fclose(file); + unlink(fname); + + return talloc_steal(mem_ctx, ret); +} + + + +/*Local Variables:*/ +/*mode: c*/ +/*End:*/ diff --git a/source3/utils/interact.h b/source3/utils/interact.h new file mode 100644 index 0000000..c719d09 --- /dev/null +++ b/source3/utils/interact.h @@ -0,0 +1,36 @@ +/* * Samba Unix/Linux SMB client library + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Functions to interact with an user. + * @author Gregor Beck <gb@sernet.de> + * @date Aug 2011 + */ + +#ifndef __INTERACT_H +#define __INTERACT_H +#include <talloc.h> + +char* interact_edit(TALLOC_CTX* mem_ctx, const char* str); +int interact_prompt(const char* msg, const char* accept, char def); + + + +#endif /* __INTERACT_H */ + +/*Local Variables:*/ +/*mode: c++*/ +/*End:*/ diff --git a/source3/utils/log2pcaphex.c b/source3/utils/log2pcaphex.c new file mode 100644 index 0000000..8113d8e --- /dev/null +++ b/source3/utils/log2pcaphex.c @@ -0,0 +1,408 @@ +/* + Unix SMB/CIFS implementation. + Utility to extract pcap files from samba (log level 10) log files + + Copyright (C) Jelmer Vernooij 2003 + Thanks to Tim Potter for the genial idea + + Portions (from capconvert.c) (C) Andrew Tridgell 1997 + Portions (from text2pcap.c) (C) Ashok Narayanan 2001 + + Example: + Output NBSS(SMB) packets in hex and convert to pcap adding + Eth/IP/TCP headers + + log2pcap -h < samba.log | text2pcap -T 139,139 - samba.pcap + + Output directly to pcap format without Eth headers or TCP + sequence numbers + + log2pcap samba.log samba.pcap + + TODO: + - Hex to text2pcap outputs are not properly parsed in Wireshark + the NBSS or SMB level. This is a bug. + - Writing directly to pcap format doesn't include sequence numbers + in the TCP packets + - Check if a packet is a response or request and set IP to/from + addresses accordingly. Currently all packets come from the same + dummy IP and go to the same dummy IP + - Add a message when done parsing about the number of pacekts + processed + - Parse NBSS packet header data from log file + - Have correct IP and TCP checksums. + + Warning: + Samba log level 10 outputs a max of 512 bytes from the packet data + section. Packets larger than this will be truncated. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include <popt.h> + +/* We don't care about the paranoid malloc checker in this standalone + program */ +#undef malloc + +#include <assert.h> + +int quiet = 0; +int hexformat = 0; + +#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa)) + +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <stdio.h> +#include <fcntl.h> + +#define TCPDUMP_MAGIC 0xa1b2c3d4 + +/* tcpdump file format */ +struct tcpdump_file_header { + uint32_t magic; + uint16_t major; + uint16_t minor; + int32_t zone; + uint32_t sigfigs; + uint32_t snaplen; + uint32_t linktype; +}; + +struct tcpdump_packet { + struct timeval ts; + uint32_t caplen; + uint32_t len; +}; + +typedef struct { + uint8_t ver_hdrlen; + uint8_t dscp; + uint16_t packet_length; + uint16_t identification; + uint8_t flags; + uint8_t fragment; + uint8_t ttl; + uint8_t protocol; + uint16_t hdr_checksum; + uint32_t src_addr; + uint32_t dest_addr; +} hdr_ip_t; + +static hdr_ip_t HDR_IP = {0x45, 0, 0, 0x3412, 0, 0, 0xff, 6, 0, 0x01010101, 0x02020202}; + +typedef struct { + uint16_t source_port; + uint16_t dest_port; + uint32_t seq_num; + uint32_t ack_num; + uint8_t hdr_length; + uint8_t flags; + uint16_t window; + uint16_t checksum; + uint16_t urg; +} hdr_tcp_t; + +static hdr_tcp_t HDR_TCP = {139, 139, 0, 0, 0x50, 0, 0, 0, 0}; + +static void print_pcap_header(FILE *out) +{ + struct tcpdump_file_header h; + h.magic = TCPDUMP_MAGIC; + h.major = 2; + h.minor = 4; + h.zone = 0; + h.sigfigs = 0; + h.snaplen = 102400; /* As long packets as possible */ + h.linktype = 101; /* Raw IP */ + fwrite(&h, sizeof(struct tcpdump_file_header), 1, out); +} + +static void print_pcap_packet(FILE *out, unsigned char *data, long length, + long caplen) +{ + struct tcpdump_packet p; + p.ts.tv_usec = 0; + p.ts.tv_sec = 0; + p.caplen = caplen; + p.len = length; + fwrite(&p, sizeof(struct tcpdump_packet), 1, out); + fwrite(data, sizeof(unsigned char), caplen, out); +} + +static void print_hex_packet(FILE *out, unsigned char *data, long length) +{ + long i,cur = 0; + while(cur < length) { + fprintf(out, "%06lX ", cur); + for(i = cur; i < length && i < cur + 16; i++) { + fprintf(out, "%02x ", data[i]); + } + cur = i; + fprintf(out, "\n"); + } +} + +static void print_netbios_packet(FILE *out, unsigned char *data, long length, + long actual_length) +{ + unsigned char *newdata; long offset = 0; + long newlen; + + newlen = length+sizeof(HDR_IP)+sizeof(HDR_TCP); + newdata = (unsigned char *)malloc(newlen); + + HDR_IP.packet_length = htons(newlen); + HDR_TCP.window = htons(0x2000); + HDR_TCP.source_port = HDR_TCP.dest_port = htons(139); + + memcpy(newdata+offset, &HDR_IP, sizeof(HDR_IP));offset+=sizeof(HDR_IP); + memcpy(newdata+offset, &HDR_TCP, sizeof(HDR_TCP));offset+=sizeof(HDR_TCP); + memcpy(newdata+offset,data,length); + + print_pcap_packet(out, newdata, newlen, actual_length+offset); + free(newdata); +} + +unsigned char *curpacket = NULL; +unsigned short curpacket_len = 0; +long line_num = 0; + +/* Read the log message produced by lib/util.c:show_msg() containing the: + * SMB_HEADER + * SMB_PARAMETERS + * SMB_DATA.ByteCount + * + * Example: + * [2007/04/08 20:41:39, 5] lib/util.c:show_msg(516) + * size=144 + * smb_com=0x73 + * smb_rcls=0 + * smb_reh=0 + * smb_err=0 + * smb_flg=136 + * smb_flg2=49153 + * smb_tid=1 + * smb_pid=65279 + * smb_uid=0 + * smb_mid=64 + * smt_wct=3 + * smb_vwv[ 0]= 117 (0x75) + * smb_vwv[ 1]= 128 (0x80) + * smb_vwv[ 2]= 1 (0x1) + * smb_bcc=87 + */ +static void read_log_msg(FILE *in, unsigned char **_buffer, + unsigned short *buffersize, long *data_offset, + long *data_length) +{ + unsigned char *buffer; + int tmp; long i; + assert(fscanf(in, " size=%hu\n", buffersize)); line_num++; + buffer = (unsigned char *)malloc(*buffersize+4); /* +4 for NBSS Header */ + memset(buffer, 0, *buffersize+4); + /* NetBIOS Session Service */ + buffer[0] = 0x00; + buffer[1] = 0x00; + memcpy(buffer+2, &buffersize, 2); /* TODO: need to copy as little-endian regardless of platform */ + /* SMB Packet */ + buffer[4] = 0xFF; + buffer[5] = 'S'; + buffer[6] = 'M'; + buffer[7] = 'B'; + assert(fscanf(in, " smb_com=0x%x\n", &tmp)); buffer[smb_com] = tmp; line_num++; + assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp; line_num++; + assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp; line_num++; + assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2); line_num++; + assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp; line_num++; + assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2); line_num++; + assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2); line_num++; + assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2); line_num++; + assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2); line_num++; + assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2); line_num++; + assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp; line_num++; + for(i = 0; i < buffer[smb_wct]; i++) { + assert(fscanf(in, " smb_vwv[%*3d]=%*5d (0x%X)\n", &tmp)); line_num++; + memcpy(buffer+smb_vwv+i*2, &tmp, 2); + } + + *data_offset = smb_vwv+buffer[smb_wct]*2; + assert(fscanf(in, " smb_bcc=%ld\n", data_length)); buffer[(*data_offset)] = *data_length; line_num++; + (*data_offset)+=2; + *_buffer = buffer; +} + +/* Read the log message produced by lib/util.c:dump_data() containing: + * SMB_DATA.Bytes + * + * Example: + * [2007/04/08 20:41:39, 10] lib/util.c:dump_data(2243) + * [000] 00 55 00 6E 00 69 00 78 00 00 00 53 00 61 00 6D .U.n.i.x ...S.a.m + * [010] 00 62 00 61 00 20 00 33 00 2E 00 30 00 2E 00 32 .b.a. .3 ...0...2 + * [020] 00 34 00 2D 00 49 00 73 00 69 00 6C 00 6F 00 6E .4.-.I.s .i.l.o.n + * [030] 00 20 00 4F 00 6E 00 65 00 46 00 53 00 20 00 76 . .O.n.e .F.S. .v + * [040] 00 34 00 2E 00 30 00 00 00 49 00 53 00 49 00 4C .4...0.. .I.S.I.L + * [050] 00 4F 00 4E 00 00 00 .O.N... + */ +static long read_log_data(FILE *in, unsigned char *buffer, long data_length) +{ + long i, addr; char real[2][16]; int ret; + unsigned int tmp; + for(i = 0; i < data_length; i++) { + if(i % 16 == 0){ + if(i != 0) { + /* Read and discard the ascii data after each line. */ + assert(fscanf(in, " %8c %8c\n", real[0], real[1]) == 2); + } + ret = fscanf(in, " [%03lX]", &addr); line_num++; + if(!ret) { + if(!quiet) + fprintf(stderr, "%ld: Only first %ld bytes are logged, " + "packet trace will be incomplete\n", line_num, i-1); + return i-1; + } + assert(addr == i); + } + if(!fscanf(in, "%02X", &tmp)) { + if(!quiet) + fprintf(stderr, "%ld: Log message formatted incorrectly. " + "Only first %ld bytes are logged, packet trace will " + "be incomplete\n", line_num, i-1); + while ((tmp = getc(in)) != '\n'); + return i-1; + } + buffer[i] = tmp; + } + + /* Consume the newline so we don't increment num_lines twice */ + while ((tmp = getc(in)) != '\n'); + return data_length; +} + +int main(int argc, const char **argv) +{ + const char *infile, *outfile; + FILE *out, *in; + int opt; + poptContext pc; + char buffer[4096]; + long data_offset = 0; + long data_length = 0; + long data_bytes_read = 0; + size_t in_packet = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "quiet", + .shortName = 'q', + .argInfo = POPT_ARG_NONE, + .arg = &quiet, + .val = 0, + .descrip = "Be quiet, don't output warnings", + }, + { + .longName = "hex", + .shortName = 'h', + .argInfo = POPT_ARG_NONE, + .arg = &hexformat, + .val = 0, + .descrip = "Output format readable by text2pcap", + }, + POPT_TABLEEND + }; + + pc = poptGetContext(NULL, argc, argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]"); + + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptGetArg(pc); /* Drop argv[0], the program name */ + + infile = poptGetArg(pc); + + if(infile) { + in = fopen(infile, "r"); + if(!in) { + perror("fopen"); + poptFreeContext(pc); + return 1; + } + } else in = stdin; + + outfile = poptGetArg(pc); + + if(outfile) { + out = fopen(outfile, "w+"); + if(!out) { + perror("fopen"); + fprintf(stderr, "Can't find %s, using stdout...\n", outfile); + poptFreeContext(pc); + return 1; + } + } + + if(!outfile) out = stdout; + + if(!hexformat)print_pcap_header(out); + + while(!feof(in)) { + char *p; + p = fgets(buffer, sizeof(buffer), in); + if (p == NULL) { + fprintf(stderr, "error reading from input file\n"); + break; + } + line_num++; + if(buffer[0] == '[') { /* Header */ + if(strstr(buffer, "show_msg")) { + in_packet++; + if(in_packet == 1)continue; + read_log_msg(in, &curpacket, &curpacket_len, &data_offset, &data_length); + } else if(in_packet && strstr(buffer, "dump_data")) { + data_bytes_read = read_log_data(in, curpacket+data_offset, data_length); + } else { + if(in_packet){ + if(hexformat) print_hex_packet(out, curpacket, curpacket_len); + else print_netbios_packet(out, curpacket, curpacket_len, data_bytes_read+data_offset); + free(curpacket); + } + in_packet = 0; + } + } + } + + if (in != stdin) { + fclose(in); + } + + if (out != stdout) { + fclose(out); + } + + poptFreeContext(pc); + return 0; +} diff --git a/source3/utils/mdsearch.c b/source3/utils/mdsearch.c new file mode 100644 index 0000000..0f5b887 --- /dev/null +++ b/source3/utils/mdsearch.c @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2019, Ralph Boehme <slow@samba.org.> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "includes.h" +#include "lib/util/debug.h" +#include "lib/cmdline/cmdline.h" +#include "lib/cmdline_contexts.h" +#include "param.h" +#include "client.h" +#include "libsmb/proto.h" +#include "librpc/rpc/rpc_common.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/cli_mdssvc.h" +#include "librpc/gen_ndr/ndr_mdssvc_c.h" + +static char *opt_path; +static int opt_live; + +int main(int argc, char **argv) +{ + const char **const_argv = discard_const_p(const char *, argv); + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + struct tevent_context *ev = NULL; + struct cli_credentials *creds = NULL; + struct rpc_pipe_client *rpccli = NULL; + struct mdscli_ctx *mdscli_ctx = NULL; + struct mdscli_search_ctx *search = NULL; + const char *server = NULL; + const char *share = NULL; + const char *mds_query = NULL; + struct cli_state *cli = NULL; + char *basepath = NULL; + uint32_t flags = CLI_FULL_CONNECTION_IPC; + uint64_t *cnids = NULL; + size_t ncnids; + size_t i; + int opt; + poptContext pc; + NTSTATUS status; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "path", + .shortName = 'p', + .argInfo = POPT_ARG_STRING, + .arg = &opt_path, + .descrip = "Server-relative search path", + }, + { + .longName = "live", + .shortName = 'L', + .argInfo = POPT_ARG_NONE, + .arg = &opt_live, + .descrip = "live query", + }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_LEGACY_S3 + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + pc = samba_popt_get_context(getprogname(), + argc, + const_argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + + poptSetOtherOptionHelp(pc, "mdsearch [OPTIONS] <server> <share> <query>\n"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + DBG_ERR("Invalid option %s: %s\n", + poptBadOption(pc, 0), + poptStrerror(opt)); + poptPrintHelp(pc, stderr, 0); + goto fail; + } + + poptGetArg(pc); /* Drop argv[0], the program name */ + server = poptGetArg(pc); + share = poptGetArg(pc); + mds_query = poptGetArg(pc); + + if (server == NULL || mds_query == NULL) { + poptPrintHelp(pc, stderr, 0); + goto fail; + } + + samba_cmdline_burn(argc, argv); + + if ((server[0] == '/' && server[1] == '/') || + (server[0] == '\\' && server[1] == '\\')) + { + server += 2; + } + + ev = samba_tevent_context_init(frame); + if (ev == NULL) { + goto fail; + } + + cmdline_messaging_context(get_dyn_CONFIGFILE()); + + creds = samba_cmdline_get_creds(); + + status = cli_full_connection_creds(&cli, + lp_netbios_name(), + server, + NULL, + 0, + "IPC$", + "IPC", + creds, + flags); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Cannot connect to server: %s\n", nt_errstr(status)); + goto fail_free_messaging; + } + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_mdssvc, &rpccli); + if (!NT_STATUS_IS_OK(status)) { + goto fail_free_messaging; + } + + status = mdscli_connect(frame, + rpccli->binding_handle, + share, + "/foo/bar", + &mdscli_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to connect mdssvc\n"); + goto fail_free_messaging; + } + + if (opt_path == NULL) { + basepath = mdscli_get_basepath(frame, mdscli_ctx); + } else { + basepath = talloc_strdup(frame, opt_path); + } + if (basepath == NULL) { + goto fail_free_messaging; + } + + status = mdscli_search(frame, + mdscli_ctx, + mds_query, + basepath, + opt_live == 1 ? true : false, + &search); + if (!NT_STATUS_IS_OK(status)) { + printf("mdscli_search failed\n"); + goto fail_free_messaging; + } + + if (!opt_live) { + sleep(1); + } + + while (true) { + status = mdscli_get_results(frame, + search, + &cnids); + if (NT_STATUS_EQUAL(status, NT_STATUS_NO_MORE_MATCHES)) { + if (opt_live) { + sleep(1); + continue; + } + break; + } + + ncnids = talloc_array_length(cnids); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PENDING) && + ncnids == 0) + { + sleep(1); + continue; + } + if (!NT_STATUS_IS_OK(status)) { + printf("mdscli_get_results failed\n"); + goto fail_free_messaging; + } + + if (ncnids == 0) { + break; + } + + for (i = 0; i < ncnids; i++) { + char *path = NULL; + + status = mdscli_get_path(frame, + mdscli_ctx, + cnids[i], + &path); + if (!NT_STATUS_IS_OK(status)) { + printf("Get path for CNID 0x%"PRIx64" failed\n", + cnids[i]); + goto fail_free_messaging; + } + printf("%s\n", path); + TALLOC_FREE(path); + } + } + + status = mdscli_close_search(&search); + if (!NT_STATUS_IS_OK(status)) { + printf("mdscli_close_search failed\n"); + goto fail_free_messaging; + } + + status = mdscli_disconnect(mdscli_ctx); + if (!NT_STATUS_IS_OK(status)) { + printf("mdscli_disconnect failed\n"); + goto fail_free_messaging; + } + + cmdline_messaging_context_free(); + TALLOC_FREE(frame); + poptFreeContext(pc); + return 0; + +fail_free_messaging: + cmdline_messaging_context_free(); +fail: + poptFreeContext(pc); + TALLOC_FREE(frame); + return 1; +} diff --git a/source3/utils/mvxattr.c b/source3/utils/mvxattr.c new file mode 100644 index 0000000..dd8da79 --- /dev/null +++ b/source3/utils/mvxattr.c @@ -0,0 +1,227 @@ +/* + Unix SMB/CIFS implementation. + xattr renaming + Copyright (C) Ralph Boehme 2017 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include <popt.h> +#include <ftw.h> + +static struct rename_xattr_state { + int follow_symlink; + int print; + int force; + int verbose; + char *xattr_from; + char *xattr_to; +} state; + +static int rename_xattr(const char *path, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) +{ + ssize_t len; + int ret; + + if (typeflag == FTW_SL) { + d_printf("Ignoring symlink %s\n", path); + return 0; + } + + if (state.verbose) { + d_printf("%s\n", path); + } + + len = getxattr(path, state.xattr_from, NULL, 0); + if (len < 0) { + if (errno == ENOATTR) { + return 0; + } + d_printf("getxattr [%s] failed [%s]\n", + path, strerror(errno)); + return -1; + } + + { + uint8_t buf[len]; + + len = getxattr(path, state.xattr_from, &buf[0], len); + if (len == -1) { + d_printf("getxattr [%s] failed [%s]\n", + path, strerror(errno)); + return -1; + } + + ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_CREATE); + if (ret != 0) { + if (errno != EEXIST) { + d_printf("setxattr [%s] failed [%s]\n", + path, strerror(errno)); + return -1; + } + if (!state.force) { + d_printf("destination [%s:%s] exists, use -f to force\n", + path, state.xattr_to); + return -1; + } + ret = setxattr(path, state.xattr_to, &buf[0], len, XATTR_REPLACE); + if (ret != 0) { + d_printf("setxattr [%s:%s] failed [%s]\n", + path, state.xattr_to, strerror(errno)); + return -1; + } + } + + ret = removexattr(path, state.xattr_from); + if (ret != 0) { + d_printf("removexattr [%s:%s] failed [%s]\n", + path, state.xattr_from, strerror(errno)); + return -1; + } + + if (state.print) { + d_printf("Renamed %s to %s on %s\n", + state.xattr_from, state.xattr_to, path); + } + } + + return 0; +} + +int main(int argc, const char *argv[]) +{ + int c; + const char *path = NULL; + poptContext pc = NULL; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "from", + .shortName = 's', + .argInfo = POPT_ARG_STRING, + .arg = &state.xattr_from, + .val = 's', + .descrip = "xattr source name", + }, + { + .longName = "to", + .shortName = 'd', + .argInfo = POPT_ARG_STRING, + .arg = &state.xattr_to, + .val = 'd', + .descrip = "xattr destination name", + }, + { + .longName = "follow-symlinks", + .shortName = 'l', + .argInfo = POPT_ARG_NONE, + .arg = &state.follow_symlink, + .val = 'l', + .descrip = "follow symlinks, the default is to " + "ignore them", + }, + { + .longName = "print", + .shortName = 'p', + .argInfo = POPT_ARG_NONE, + .arg = &state.print, + .val = 'p', + .descrip = "print files where the xattr got " + "renamed", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &state.verbose, + .val = 'v', + .descrip = "print files as they are checked", + }, + { + .longName = "force", + .shortName = 'f', + .argInfo = POPT_ARG_NONE, + .arg = &state.force, + .val = 'f', + .descrip = "force overwriting of destination xattr", + }, + POPT_TABLEEND + }; + TALLOC_CTX *frame = talloc_stackframe(); + const char *s = NULL; + int ret = 0; + + if (getuid() != 0) { + d_printf("%s only works as root!\n", argv[0]); + ret = 1; + goto done; + } + + pc = poptGetContext(NULL, argc, argv, long_options, 0); + poptSetOtherOptionHelp(pc, "-s STRING -d STRING PATH [PATH ...]"); + + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) { + case 's': + s = poptGetOptArg(pc); + state.xattr_from = talloc_strdup(frame, s); + if (state.xattr_from == NULL) { + ret = 1; + goto done; + } + break; + case 'd': + s = poptGetOptArg(pc); + state.xattr_to = talloc_strdup(frame, s); + if (state.xattr_to == NULL) { + ret = 1; + goto done; + } + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(c)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (state.xattr_from == NULL || state.xattr_to == NULL) { + poptPrintUsage(pc, stderr, 0); + ret = 1; + goto done; + } + + if (poptPeekArg(pc) == NULL) { + poptPrintUsage(pc, stderr, 0); + ret = 1; + goto done; + } + + while ((path = poptGetArg(pc)) != NULL) { + ret = nftw(path, rename_xattr, 256, + state.follow_symlink ? 0 : FTW_PHYS); + } + +done: + poptFreeContext(pc); + + TALLOC_FREE(frame); + return ret; +} diff --git a/source3/utils/net.c b/source3/utils/net.c new file mode 100644 index 0000000..badf2e0 --- /dev/null +++ b/source3/utils/net.c @@ -0,0 +1,1450 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + Originally written by Steve and Jim. Largely rewritten by tridge in + November 2001. + + Reworked again by abartlet in December 2001 + + Another overhaul, moving functionality into plug-ins loaded on demand by Kai + in May 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/*****************************************************/ +/* */ +/* Distributed SMB/CIFS Server Management Utility */ +/* */ +/* The intent was to make the syntax similar */ +/* to the NET utility (first developed in DOS */ +/* with additional interesting & useful functions */ +/* added in later SMB server network operating */ +/* systems). */ +/* */ +/*****************************************************/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "utils/net.h" +#include "secrets.h" +#include "lib/netapi/netapi.h" +#include "../libcli/security/security.h" +#include "passdb.h" +#include "messages.h" +#include "cmdline_contexts.h" +#include "lib/gencache.h" +#include "auth/credentials/credentials.h" +#include "source3/utils/passwd_proto.h" +#include "auth/gensec/gensec.h" +#include "lib/param/param.h" + +#ifdef WITH_FAKE_KASERVER +#include "utils/net_afs.h" +#endif + +/***********************************************************************/ +/* end of internationalization section */ +/***********************************************************************/ + +enum netr_SchannelType get_sec_channel_type(const char *param) +{ + if (!(param && *param)) { + return get_default_sec_channel(); + } else { + if (strequal(param, "PDC")) { + return SEC_CHAN_BDC; + } else if (strequal(param, "BDC")) { + return SEC_CHAN_BDC; + } else if (strequal(param, "MEMBER")) { + return SEC_CHAN_WKSTA; +#if 0 + } else if (strequal(param, "DOMAIN")) { + return SEC_CHAN_DOMAIN; +#endif + } else { + return get_default_sec_channel(); + } + } +} + +static int net_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + net_warn_member_options(); + + if (net_ads_check_our_domain(c) == 0) + return net_ads_changetrustpw(c, argc, argv); + + return net_rpc_changetrustpw(c, argc, argv); +} + +static void set_line_buffering(FILE *f) +{ + setvbuf(f, NULL, _IOLBF, 0); +} + +static int net_primarytrust_dumpinfo(struct net_context *c, int argc, + const char **argv) +{ + int role = lp_server_role(); + const char *domain = lp_workgroup(); + struct secrets_domain_info1 *info = NULL; + bool include_secrets = c->opt_force; + char *str = NULL; + NTSTATUS status; + + if (role >= ROLE_ACTIVE_DIRECTORY_DC) { + d_printf(_("net primarytrust dumpinfo is only supported " + "on a DOMAIN_MEMBER for now.\n")); + return 1; + } + + net_warn_member_options(); + + if (c->opt_stdin) { + set_line_buffering(stdin); + set_line_buffering(stdout); + set_line_buffering(stderr); + } + + status = secrets_fetch_or_upgrade_domain_info(domain, + talloc_tos(), + &info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to fetch the information for domain[%s] " + "in the secrets database.\n"), + domain); + return 1; + } + + str = secrets_domain_info_string(info, info, domain, include_secrets); + if (str == NULL) { + d_fprintf(stderr, "secrets_domain_info_string() failed.\n"); + return 1; + } + + d_printf("%s", str); + if (!c->opt_force) { + d_printf(_("The password values are only included using " + "-f flag.\n")); + } + + TALLOC_FREE(info); + return 0; +} + +/** + * Entrypoint for 'net primarytrust' code. + * + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + */ + +static int net_primarytrust(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + .funcname = "dumpinfo", + .fn = net_primarytrust_dumpinfo, + .valid_transports = NET_TRANSPORT_LOCAL, + .description = N_("Dump the details of the " + "workstation trust"), + .usage = N_(" net [options] primarytrust " + "dumpinfo'\n" + " Dump the details of the " + "workstation trust in " + "secrets.tdb.\n" + " Requires the -f flag to " + "include the password values."), + }, + { + .funcname = NULL, + }, + }; + + return net_run_function(c, argc, argv, "net primarytrust", func); +} + +static int net_changesecretpw(struct net_context *c, int argc, + const char **argv) +{ + char *trust_pw; + int role = lp_server_role(); + + if (role != ROLE_DOMAIN_MEMBER) { + d_printf(_("Machine account password change only supported on a DOMAIN_MEMBER.\n" + "Do NOT use this function unless you know what it does!\n" + "This function will change the ADS Domain member " + "machine account password in the secrets.tdb file!\n")); + return 1; + } + + net_warn_member_options(); + + if(c->opt_force) { + struct secrets_domain_info1 *info = NULL; + struct secrets_domain_info1_change *prev = NULL; + NTSTATUS status; + struct timeval tv = timeval_current(); + NTTIME now = timeval_to_nttime(&tv); + + if (c->opt_stdin) { + set_line_buffering(stdin); + set_line_buffering(stdout); + set_line_buffering(stderr); + } + + trust_pw = get_pass(_("Enter machine password: "), c->opt_stdin); + if (trust_pw == NULL) { + d_fprintf(stderr, + _("Error in reading machine password\n")); + return 1; + } + + status = secrets_prepare_password_change(lp_workgroup(), + "localhost", + trust_pw, + talloc_tos(), + &info, &prev); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to write the machine account password in the secrets database")); + return 1; + } + if (prev != NULL) { + d_fprintf(stderr, + _("Pending machine account password change found - aborting.")); + status = secrets_failed_password_change("localhost", + NT_STATUS_REQUEST_NOT_ACCEPTED, + NT_STATUS_NOT_COMMITTED, + info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Failed to abort machine account password change")); + } + return 1; + } + status = secrets_finish_password_change("localhost", now, info); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to write the machine account password in the secrets database")); + return 1; + } + + d_printf(_("Modified trust account password in secrets database\n")); + } + else { + d_printf(_("Machine account password change requires the -f flag.\n" + "Do NOT use this function unless you know what it does!\n" + "This function will change the ADS Domain member " + "machine account password in the secrets.tdb file!\n")); + } + + return 0; +} + +/** + * @brief Set the authorised user for winbindd access in secrets.tdb + */ +static int net_setauthuser(struct net_context *c, int argc, const char **argv) +{ + const char *password = NULL; + bool ok; + + if (!secrets_init()) { + d_fprintf(stderr, _("Failed to open secrets.tdb.\n")); + return 1; + } + + /* Delete the settings. */ + if (argc >= 1) { + if (strncmp(argv[0], "delete", 6) != 0) { + d_fprintf(stderr,_("Usage:\n")); + d_fprintf(stderr, + _(" net setauthuser -U user[%%password] \n" + " Set the auth user account to user" + "password. Prompt for password if not " + "specified.\n")); + d_fprintf(stderr, + _(" net setauthuser delete\n" + " Delete the auth user setting.\n")); + return 1; + } + secrets_delete_entry(SECRETS_AUTH_USER); + secrets_delete_entry(SECRETS_AUTH_DOMAIN); + secrets_delete_entry(SECRETS_AUTH_PASSWORD); + return 0; + } + + if (!c->opt_user_specified) { + d_fprintf(stderr, _("Usage:\n")); + d_fprintf(stderr, + _(" net setauthuser -U user[%%password]\n" + " Set the auth user account to user" + "password. Prompt for password if not " + "specified.\n")); + d_fprintf(stderr, + _(" net setauthuser delete\n" + " Delete the auth user setting.\n")); + return 1; + } + + password = net_prompt_pass(c, _("the auth user")); + if (password == NULL) { + d_fprintf(stderr,_("Failed to get the auth users password.\n")); + return 1; + } + + ok = secrets_store_creds(c->creds); + if (!ok) { + d_fprintf(stderr, _("Failed storing auth user credentials\n")); + return 1; + } + + return 0; +} + +/** + * @brief Get the auth user settings + */ +static int net_getauthuser(struct net_context *c, int argc, const char **argv) +{ + char *user, *domain, *password; + + /* Lift data from secrets file */ + + secrets_fetch_ipc_userpass(&user, &domain, &password); + + if ((!user || !*user) && (!domain || !*domain ) && + (!password || !*password)){ + + SAFE_FREE(user); + SAFE_FREE(domain); + BURN_FREE_STR(password); + d_printf(_("No authorised user configured\n")); + return 0; + } + + /* Pretty print authorised user info */ + + d_printf("%s%s%s%s%s\n", domain ? domain : "", + domain ? lp_winbind_separator(): "", user, + password ? "%" : "", password ? password : ""); + + SAFE_FREE(user); + SAFE_FREE(domain); + BURN_FREE_STR(password); + + return 0; +} +/* + Retrieve our local SID or the SID for the specified name + */ +static int net_getlocalsid(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + const char *name; + struct dom_sid_buf sid_str; + + if (argc >= 1) { + name = argv[0]; + } + else { + name = lp_netbios_name(); + } + + if(!initialize_password_db(false, NULL)) { + d_fprintf(stderr, _("WARNING: Could not open passdb\n")); + return 1; + } + + /* first check to see if we can even access secrets, so we don't + panic when we can't. */ + + if (!secrets_init()) { + d_fprintf(stderr, + _("Unable to open secrets.tdb. Can't fetch domain " + "SID for name: %s\n"), name); + return 1; + } + + /* Generate one, if it doesn't exist */ + get_global_sam_sid(); + + if (!secrets_fetch_domain_sid(name, &sid)) { + DEBUG(0, ("Can't fetch domain SID for name: %s\n", name)); + return 1; + } + d_printf(_("SID for domain %s is: %s\n"), + name, + dom_sid_str_buf(&sid, &sid_str)); + return 0; +} + +static int net_setlocalsid(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + + if ( (argc != 1) + || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0) + || (!string_to_sid(&sid, argv[0])) + || (sid.num_auths != 4)) { + d_printf(_("Usage:")); + d_printf(" net setlocalsid S-1-5-21-x-y-z\n"); + return 1; + } + + if (!secrets_store_domain_sid(lp_netbios_name(), &sid)) { + DEBUG(0,("Can't store domain SID as a pdc/bdc.\n")); + return 1; + } + + return 0; +} + +static int net_setdomainsid(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + + if ( (argc != 1) + || (strncmp(argv[0], "S-1-5-21-", strlen("S-1-5-21-")) != 0) + || (!string_to_sid(&sid, argv[0])) + || (sid.num_auths != 4)) { + d_printf(_("Usage:")); + d_printf(" net setdomainsid S-1-5-21-x-y-z\n"); + return 1; + } + + if (!secrets_store_domain_sid(lp_workgroup(), &sid)) { + DEBUG(0,("Can't store domain SID.\n")); + return 1; + } + + return 0; +} + +static int net_getdomainsid(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid domain_sid; + struct dom_sid_buf sid_str; + + if (argc > 0) { + d_printf(_("Usage:")); + d_printf(" net getdomainsid\n"); + return 1; + } + + if(!initialize_password_db(false, NULL)) { + d_fprintf(stderr, _("WARNING: Could not open passdb\n")); + return 1; + } + + /* first check to see if we can even access secrets, so we don't + panic when we can't. */ + + if (!secrets_init()) { + d_fprintf(stderr, _("Unable to open secrets.tdb. Can't fetch " + "domain SID for name: %s\n"), + get_global_sam_name()); + return 1; + } + + /* Generate one, if it doesn't exist */ + get_global_sam_sid(); + + if (!IS_DC) { + if (!secrets_fetch_domain_sid(lp_netbios_name(), &domain_sid)) { + d_fprintf(stderr, _("Could not fetch local SID\n")); + return 1; + } + d_printf(_("SID for local machine %s is: %s\n"), + lp_netbios_name(), + dom_sid_str_buf(&domain_sid, &sid_str)); + } + if (!secrets_fetch_domain_sid(c->opt_workgroup, &domain_sid)) { + d_fprintf(stderr, _("Could not fetch domain SID\n")); + return 1; + } + + d_printf(_("SID for domain %s is: %s\n"), + c->opt_workgroup, + dom_sid_str_buf(&domain_sid, &sid_str)); + + return 0; +} + +static bool search_maxrid(struct pdb_search *search, const char *type, + uint32_t *max_rid) +{ + struct samr_displayentry *entries; + uint32_t i, num_entries; + + if (search == NULL) { + d_fprintf(stderr, _("get_maxrid: Could not search %s\n"), type); + return false; + } + + num_entries = pdb_search_entries(search, 0, 0xffffffff, &entries); + for (i=0; i<num_entries; i++) + *max_rid = MAX(*max_rid, entries[i].rid); + TALLOC_FREE(search); + return true; +} + +static uint32_t get_maxrid(void) +{ + uint32_t max_rid = 0; + + if (!search_maxrid(pdb_search_users(talloc_tos(), 0), "users", &max_rid)) + return 0; + + if (!search_maxrid(pdb_search_groups(talloc_tos()), "groups", &max_rid)) + return 0; + + if (!search_maxrid(pdb_search_aliases(talloc_tos(), + get_global_sam_sid()), + "aliases", &max_rid)) + return 0; + + return max_rid; +} + +static int net_maxrid(struct net_context *c, int argc, const char **argv) +{ + uint32_t rid; + + if (argc != 0) { + d_fprintf(stderr, "%s net maxrid\n", _("Usage:")); + return 1; + } + + if ((rid = get_maxrid()) == 0) { + d_fprintf(stderr, _("can't get current maximum rid\n")); + return 1; + } + + d_printf(_("Currently used maximum rid: %d\n"), rid); + + return 0; +} + +/* main function table */ +static struct functable net_func[] = { + { + "rpc", + net_rpc, + NET_TRANSPORT_RPC, + N_("Run functions using RPC transport"), + N_(" Use 'net help rpc' to get more extensive information " + "about 'net rpc' commands.") + }, + { + "rap", + net_rap, + NET_TRANSPORT_RAP, + N_("Run functions using RAP transport"), + N_(" Use 'net help rap' to get more extensive information " + "about 'net rap' commands.") + }, + { + "ads", + net_ads, + NET_TRANSPORT_ADS, + N_("Run functions using ADS transport"), + N_(" Use 'net help ads' to get more extensive information " + "about 'net ads' commands.") + }, + + /* eventually these should auto-choose the transport ... */ + { + "file", + net_file, + NET_TRANSPORT_RPC | NET_TRANSPORT_RAP, + N_("Functions on remote opened files"), + N_(" Use 'net help file' to get more information about 'net " + "file' commands.") + }, + { + "share", + net_share, + NET_TRANSPORT_RPC | NET_TRANSPORT_RAP, + N_("Functions on shares"), + N_(" Use 'net help share' to get more information about 'net " + "share' commands.") + }, + { + "session", + net_rap_session, + NET_TRANSPORT_RAP, + N_("Manage sessions"), + N_(" Use 'net help session' to get more information about " + "'net session' commands.") + }, + { + "server", + net_rap_server, + NET_TRANSPORT_RAP, + N_("List servers in workgroup"), + N_(" Use 'net help server' to get more information about 'net " + "server' commands.") + }, + { + "domain", + net_rap_domain, + NET_TRANSPORT_RAP, + N_("List domains/workgroups on network"), + N_(" Use 'net help domain' to get more information about 'net " + "domain' commands.") + }, + { + "printq", + net_rap_printq, + NET_TRANSPORT_RAP, + N_("Modify printer queue"), + N_(" Use 'net help printq' to get more information about 'net " + "printq' commands.") + }, + { + "user", + net_user, + NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP, + N_("Manage users"), + N_(" Use 'net help user' to get more information about 'net " + "user' commands.") + }, + { + "group", + net_group, + NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP, + N_("Manage groups"), + N_(" Use 'net help group' to get more information about 'net " + "group' commands.") + }, + { + "groupmap", + net_groupmap, + NET_TRANSPORT_LOCAL, + N_("Manage group mappings"), + N_(" Use 'net help groupmap' to get more information about " + "'net groupmap' commands.") + }, + { + "sam", + net_sam, + NET_TRANSPORT_LOCAL, + N_("Functions on the SAM database"), + N_(" Use 'net help sam' to get more information about 'net " + "sam' commands.") + }, + { + "validate", + net_rap_validate, + NET_TRANSPORT_RAP, + N_("Validate username and password"), + N_(" Use 'net help validate' to get more information about " + "'net validate' commands.") + }, + { + "groupmember", + net_rap_groupmember, + NET_TRANSPORT_RAP, + N_("Modify group memberships"), + N_(" Use 'net help groupmember' to get more information about " + "'net groupmember' commands.") + }, + { "admin", + net_rap_admin, + NET_TRANSPORT_RAP, + N_("Execute remote command on a remote OS/2 server"), + N_(" Use 'net help admin' to get more information about 'net " + "admin' commands.") + }, + { "service", + net_rap_service, + NET_TRANSPORT_RAP, + N_("List/modify running services"), + N_(" Use 'net help service' to get more information about " + "'net service' commands.") + }, + { + "password", + net_rap_password, + NET_TRANSPORT_RAP, + N_("Change user password on target server"), + N_(" Use 'net help password' to get more information about " + "'net password' commands.") + }, + { + "primarytrust", + net_primarytrust, + NET_TRANSPORT_RPC, + N_("Run functions related to the primary workstation trust."), + N_(" Use 'net help primarytrust' to get more extensive information " + "about 'net primarytrust' commands.") + }, + { "changetrustpw", + net_changetrustpw, + NET_TRANSPORT_ADS | NET_TRANSPORT_RPC, + N_("Change the trust password"), + N_(" Use 'net help changetrustpw' to get more information " + "about 'net changetrustpw'.") + }, + { "changesecretpw", + net_changesecretpw, + NET_TRANSPORT_LOCAL, + N_("Change the secret password"), + N_(" net [options] changesecretpw\n" + " Change the ADS domain member machine account password " + "in secrets.tdb.\n" + " Do NOT use this function unless you know what it does.\n" + " Requires the -f flag to work.") + }, + { + "setauthuser", + net_setauthuser, + NET_TRANSPORT_LOCAL, + N_("Set the winbind auth user"), + N_(" net -U user[%%password] [-W domain] setauthuser\n" + " Set the auth user, password (and optionally domain\n" + " Will prompt for password if not given.\n" + " net setauthuser delete\n" + " Delete the existing auth user settings.") + }, + { + "getauthuser", + net_getauthuser, + NET_TRANSPORT_LOCAL, + N_("Get the winbind auth user settings"), + N_(" net getauthuser\n" + " Get the current winbind auth user settings.") + }, + { "time", + net_time, + NET_TRANSPORT_LOCAL, + N_("Show/set time"), + N_(" Use 'net help time' to get more information about 'net " + "time' commands.") + }, + { "lookup", + net_lookup, + NET_TRANSPORT_LOCAL, + N_("Look up host names/IP addresses"), + N_(" Use 'net help lookup' to get more information about 'net " + "lookup' commands.") + }, + { "g_lock", + net_g_lock, + NET_TRANSPORT_LOCAL, + N_("Manipulate the global lock table"), + N_(" Use 'net help g_lock' to get more information about " + "'net g_lock' commands.") + }, + { "join", + net_join, + NET_TRANSPORT_ADS | NET_TRANSPORT_RPC, + N_("Join a domain/AD"), + N_(" Use 'net help join' to get more information about 'net " + "join'.") + }, + { "offlinejoin", + net_offlinejoin, + NET_TRANSPORT_ADS | NET_TRANSPORT_RPC, + N_("Perform offline domain join"), + N_(" Use 'net help offlinejoin' to get more information about 'net " + "offlinejoin'.") + }, + { "dom", + net_dom, + NET_TRANSPORT_LOCAL, + N_("Join/unjoin (remote) machines to/from a domain/AD"), + N_(" Use 'net help dom' to get more information about 'net " + "dom' commands.") + }, + { "cache", + net_cache, + NET_TRANSPORT_LOCAL, + N_("Operate on the cache tdb file"), + N_(" Use 'net help cache' to get more information about 'net " + "cache' commands.") + }, + { "getlocalsid", + net_getlocalsid, + NET_TRANSPORT_LOCAL, + N_("Get the SID for the local domain"), + N_(" net getlocalsid") + }, + { "setlocalsid", + net_setlocalsid, + NET_TRANSPORT_LOCAL, + N_("Set the SID for the local domain"), + N_(" net setlocalsid S-1-5-21-x-y-z") + }, + { "setdomainsid", + net_setdomainsid, + NET_TRANSPORT_LOCAL, + N_("Set domain SID on member servers"), + N_(" net setdomainsid S-1-5-21-x-y-z") + }, + { "getdomainsid", + net_getdomainsid, + NET_TRANSPORT_LOCAL, + N_("Get domain SID on member servers"), + N_(" net getdomainsid") + }, + { "maxrid", + net_maxrid, + NET_TRANSPORT_LOCAL, + N_("Display the maximum RID currently used"), + N_(" net maxrid") + }, + { "idmap", + net_idmap, + NET_TRANSPORT_LOCAL, + N_("IDmap functions"), + N_(" Use 'net help idmap to get more information about 'net " + "idmap' commands.") + }, + { "status", + net_status, + NET_TRANSPORT_LOCAL, + N_("Display server status"), + N_(" Use 'net help status' to get more information about 'net " + "status' commands.") + }, + { "usershare", + net_usershare, + NET_TRANSPORT_LOCAL, + N_("Manage user-modifiable shares"), + N_(" Use 'net help usershare to get more information about " + "'net usershare' commands.") + }, + { "usersidlist", + net_usersidlist, + NET_TRANSPORT_RPC, + N_("Display list of all users with SID"), + N_(" Use 'net help usersidlist' to get more information about " + "'net usersidlist'.") + }, + { "conf", + net_conf, + NET_TRANSPORT_LOCAL, + N_("Manage Samba registry based configuration"), + N_(" Use 'net help conf' to get more information about 'net " + "conf' commands.") + }, + { "registry", + net_registry, + NET_TRANSPORT_LOCAL, + N_("Manage the Samba registry"), + N_(" Use 'net help registry' to get more information about " + "'net registry' commands.") + }, + { "eventlog", + net_eventlog, + NET_TRANSPORT_LOCAL, + N_("Process Win32 *.evt eventlog files"), + N_(" Use 'net help eventlog' to get more information about " + "'net eventlog' commands.") + }, + { "printing", + net_printing, + NET_TRANSPORT_LOCAL, + N_("Process tdb printer files"), + N_(" Use 'net help printing' to get more information about " + "'net printing' commands.") + }, + + { "serverid", + net_serverid, + NET_TRANSPORT_LOCAL, + N_("Manage the serverid tdb"), + N_(" Use 'net help serverid' to get more information about " + "'net serverid' commands.") + }, + + { "notify", + net_notify, + NET_TRANSPORT_LOCAL, + N_("notifyd client code"), + N_(" Use 'net help notify' to get more information about " + "'net notify' commands.") + }, + + { "tdb", + net_tdb, + NET_TRANSPORT_LOCAL, + N_("Show information from tdb records"), + N_(" Use 'net help tdb' to get more information about " + "'net tdb' commands.") + }, + + { "vfs", + net_vfs, + NET_TRANSPORT_LOCAL, + N_("Filesystem operation through the VFS stack"), + N_(" Use 'net help vfs' to get more information about " + "'net vfs' commands.") + }, + + { "witness", + net_witness, + NET_TRANSPORT_LOCAL, + N_("Manage witness registrations"), + N_(" Use 'net help witness' to get more information about " + "'net witness' commands.") + }, + +#ifdef WITH_FAKE_KASERVER + { "afs", + net_afs, + NET_TRANSPORT_LOCAL, + N_("Manage AFS tokens"), + N_(" Use 'net help afs' to get more information about 'net " + "afs' commands.") + }, +#endif + + { "help", + net_help, + NET_TRANSPORT_LOCAL, + N_("Print usage information"), + N_(" Use 'net help help' to list usage information for 'net' " + "commands.") + }, + {NULL, NULL, 0, NULL, NULL} +}; + + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc, char **argv) +{ + int opt,i; + int rc = 0; + int argc_new = 0; + const char ** argv_new; + const char **argv_const = discard_const_p(const char *, argv); + poptContext pc; + TALLOC_CTX *frame = talloc_stackframe(); + struct net_context *c = talloc_zero(frame, struct net_context); + bool ok; + + struct poptOption long_options[] = { + { + .longName = "help", + .shortName = 'h', + .argInfo = POPT_ARG_NONE, + .val = 'h', + }, + { + .longName = "target-workgroup", + .shortName = 'w', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_target_workgroup, + }, + { + .longName = "ipaddress", + .shortName = 'I', + .argInfo = POPT_ARG_STRING, + .arg = 0, + .val = 'I', + }, + { + .longName = "port", + .shortName = 'p', + .argInfo = POPT_ARG_INT, + .arg = &c->opt_port, + }, + { + .longName = "myname", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_requester_name, + }, + { + .longName = "server", + .shortName = 'S', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_host, + }, + { + .longName = "container", + .shortName = 'c', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_container, + }, + { + .longName = "comment", + .shortName = 'C', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_comment, + }, + { + .longName = "maxusers", + .shortName = 'M', + .argInfo = POPT_ARG_INT, + .arg = &c->opt_maxusers, + }, + { + .longName = "flags", + .shortName = 'F', + .argInfo = POPT_ARG_INT, + .arg = &c->opt_flags, + }, + { + .longName = "long", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_long_list_entries, + }, + { + .longName = "reboot", + .shortName = 'r', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_reboot, + }, + { + .longName = "force", + .shortName = 'f', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_force, + }, + { + .longName = "stdin", + .shortName = 'i', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_stdin, + }, + { + .longName = "timeout", + .shortName = 't', + .argInfo = POPT_ARG_INT, + .arg = &c->opt_timeout, + }, + { + .longName = "request-timeout", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &c->opt_request_timeout, + }, + { + .longName = "use-ccache", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_ccache, + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_verbose, + }, + { + .longName = "test", + .shortName = 'T', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_testmode, + }, + /* Options for 'net groupmap set' */ + { + .longName = "local", + .shortName = 'L', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_localgroup, + }, + { + .longName = "domain", + .shortName = 'D', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_domaingroup, + }, + { + .longName = "ntname", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_newntname, + }, + { + .longName = "rid", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &c->opt_rid, + }, + /* Options for 'net rpc share migrate' */ + { + .longName = "acls", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_acls, + }, + { + .longName = "attrs", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_attrs, + }, + { + .longName = "timestamps", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_timestamps, + }, + { + .longName = "exclude", + .shortName = 'X', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_exclude, + }, + { + .longName = "destination", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_destination, + }, + { + .longName = "tallocreport", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->do_talloc_report, + }, + /* Options for 'net rpc vampire (keytab)' */ + { + .longName = "force-full-repl", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_force_full_repl, + }, + { + .longName = "single-obj-repl", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_single_obj_repl, + }, + { + .longName = "clean-old-entries", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_clean_old_entries, + }, + /* Options for 'net idmap'*/ + { + .longName = "db", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_db, + }, + { + .longName = "lock", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_lock, + }, + { + .longName = "auto", + .shortName = 'a', + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_auto, + }, + { + .longName = "repair", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_repair, + }, + /* Options for 'net registry check'*/ + { + .longName = "reg-version", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &c->opt_reg_version, + }, + { + .longName = "output", + .shortName = 'o', + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_output, + }, + { + .longName = "wipe", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_wipe, + }, + /* Options for 'net registry import' */ + { + .longName = "precheck", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_precheck, + }, + /* Options for 'net ads join or leave' */ + { + .longName = "no-dns-updates", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_no_dns_updates, + }, + { + .longName = "keep-account", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_keep_account, + }, + { + .longName = "json", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_json, + }, + /* Options for 'net vfs' */ + { + .longName = "continue", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_continue_on_error, + .descrip = "Continue on errors", + }, + { + .longName = "recursive", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_recursive, + .descrip = "Traverse directory hierarchy", + }, + { + .longName = "follow-symlinks", + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_follow_symlink, + .descrip = "follow symlinks", + }, + /* Options for 'net ads dns register' */ + { + .longName = "dns-ttl", + .argInfo = POPT_ARG_INT, + .arg = &c->opt_dns_ttl, + .descrip = "TTL in seconds of DNS records", + }, + /* Options for 'net witness {list,...}' */ + { + .longName = "witness-registration", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_registration, + }, + { + .longName = "witness-net-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_net_name, + }, + { + .longName = "witness-share-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_share_name, + }, + { + .longName = "witness-ip-address", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_ip_address, + }, + { + .longName = "witness-client-computer-name", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_client_computer_name, + }, + { + .longName = "witness-apply-to-all", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &c->opt_witness_apply_to_all, + }, + { + .longName = "witness-new-ip", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_new_ip, + }, + { + .longName = "witness-new-node", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &c->opt_witness_new_node, + }, + { + .longName = "witness-forced-response", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &c->opt_witness_forced_response, + }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_LEGACY_S3 + POPT_TABLEEND + }; + + /* Ignore possible SIGPIPE upon ldap_unbind when over TLS */ + BlockSignals(True, SIGPIPE); + + zero_sockaddr(&c->opt_dest_ip); + c->opt_witness_new_node = -2; + + smb_init_locale(); + + setlocale(LC_ALL, ""); +#if defined(HAVE_BINDTEXTDOMAIN) + bindtextdomain(MODULE_NAME, get_dyn_LOCALEDIR()); +#endif +#if defined(HAVE_TEXTDOMAIN) + textdomain(MODULE_NAME); +#endif + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + c->lp_ctx = samba_cmdline_get_lp_ctx(); + /* set default debug level to 0 regardless of what smb.conf sets */ + lpcfg_set_cmdline(c->lp_ctx, "log level", "0"); + c->private_data = net_func; + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'h': + c->display_usage = true; + break; + case 'I': + if (!interpret_string_addr(&c->opt_dest_ip, + poptGetOptArg(pc), 0)) { + d_fprintf(stderr, _("\nInvalid ip address specified\n")); + } else { + c->opt_have_ip = true; + } + break; + default: + d_fprintf(stderr, _("\nInvalid option %s: %s\n"), + poptBadOption(pc, 0), poptStrerror(opt)); + net_help(c, argc, argv_const); + exit(1); + } + } + + c->creds = samba_cmdline_get_creds(); + + { + enum credentials_obtained username_obtained = + CRED_UNINITIALISED; + enum smb_encryption_setting encrypt_state = + cli_credentials_get_smb_encryption(c->creds); + enum credentials_use_kerberos krb5_state = + cli_credentials_get_kerberos_state(c->creds); + uint32_t gensec_features; + + c->opt_user_name = cli_credentials_get_username_and_obtained( + c->creds, + &username_obtained); + c->opt_user_specified = (username_obtained == CRED_SPECIFIED); + + c->opt_workgroup = cli_credentials_get_domain(c->creds); + + c->smb_encrypt = (encrypt_state == SMB_ENCRYPTION_REQUIRED); + + c->opt_kerberos = (krb5_state > CRED_USE_KERBEROS_DESIRED); + + gensec_features = cli_credentials_get_gensec_features(c->creds); + c->opt_ccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE); + } + + c->msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + +#if defined(HAVE_BIND_TEXTDOMAIN_CODESET) + /* Bind our gettext results to 'unix charset' + + This ensures that the translations and any embedded strings are in the + same charset. It won't be the one from the user's locale (we no + longer auto-detect that), but it will be self-consistent. + */ + bind_textdomain_codeset(MODULE_NAME, lp_unix_charset()); +#endif + + argv_new = (const char **)poptGetArgs(pc); + + argc_new = argc; + for (i=0; i<argc; i++) { + if (argv_new[i] == NULL) { + argc_new = i; + break; + } + } + + if (c->do_talloc_report) { + talloc_enable_leak_report(); + } + + if (c->opt_requester_name) { + lpcfg_set_cmdline(c->lp_ctx, "netbios name", c->opt_requester_name); + } + + if (!c->opt_target_workgroup) { + c->opt_target_workgroup = talloc_strdup(c, lp_workgroup()); + } + + load_interfaces(); + + /* this makes sure that when we do things like call scripts, + that it won't assert because we are not root */ + sec_init(); + + samba_cmdline_burn(argc, argv); + + rc = net_run_function(c, argc_new-1, argv_new+1, "net", net_func); + + DEBUG(2,("return code = %d\n", rc)); + + libnetapi_free(c->netapi_ctx); + + poptFreeContext(pc); + + cmdline_messaging_context_free(); + + gfree_all(); + + TALLOC_FREE(frame); + return rc; +} diff --git a/source3/utils/net.h b/source3/utils/net.h new file mode 100644 index 0000000..fca3a99 --- /dev/null +++ b/source3/utils/net.h @@ -0,0 +1,208 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +/* + * A function of this type is passed to the ' + * run_rpc_command' wrapper. Must go before the net_proto.h + * include + */ + +struct cli_state; + +#include "../librpc/gen_ndr/lsa.h" + +#include "intl.h" +#ifdef HAVE_LIBINTL_H +#include <libintl.h> +#endif + +#if defined(HAVE_GETTEXT) && !defined(__LCLINT__) +#define _(foo) gettext(foo) +#else +#define _(foo) foo +#endif + +#define MODULE_NAME "net" + +struct net_context { + const char *opt_requester_name; + const char *opt_host; + const char *opt_password; + const char *opt_user_name; + bool opt_user_specified; + const char *opt_workgroup; + int opt_long_list_entries; + int opt_reboot; + int opt_force; + int opt_stdin; + int opt_port; + int opt_verbose; + int opt_maxusers; + const char *opt_comment; + const char *opt_container; + int opt_flags; + int opt_timeout; + int opt_request_timeout; + const char *opt_target_workgroup; + int opt_machine_pass; + int opt_localgroup; + int opt_domaingroup; + int do_talloc_report; + const char *opt_newntname; + int opt_rid; + int opt_acls; + int opt_attrs; + int opt_timestamps; + const char *opt_exclude; + const char *opt_destination; + int opt_testmode; + int opt_kerberos; + int opt_force_full_repl; + int opt_ccache; + int opt_single_obj_repl; + int opt_clean_old_entries; + const char *opt_db; + int opt_lock; + int opt_auto; + int opt_repair; + int opt_reg_version; + const char *opt_output; + int opt_wipe; + const char *opt_precheck; + int opt_no_dns_updates; + int opt_keep_account; + int opt_json; + int opt_continue_on_error; + int opt_recursive; + int opt_follow_symlink; + int opt_dns_ttl; + const char *opt_witness_registration; + const char *opt_witness_net_name; + const char *opt_witness_share_name; + const char *opt_witness_ip_address; + const char *opt_witness_client_computer_name; + int opt_witness_apply_to_all; + const char *opt_witness_new_ip; + int opt_witness_new_node; + const char *opt_witness_forced_response; + + int opt_have_ip; + struct sockaddr_storage opt_dest_ip; + bool smb_encrypt; + struct libnetapi_ctx *netapi_ctx; + struct messaging_context *msg_ctx; + struct netlogon_creds_cli_context *netlogon_creds; + struct cli_credentials *creds; + struct loadparm_context *lp_ctx; + + bool display_usage; + void *private_data; +}; + +struct net_dc_info { + bool is_dc; + bool is_pdc; + bool is_ad; + bool is_mixed_mode; + const char *netbios_domain_name; + const char *dns_domain_name; + const char *forest_name; +}; + +#define NET_TRANSPORT_LOCAL 0x01 +#define NET_TRANSPORT_RAP 0x02 +#define NET_TRANSPORT_RPC 0x04 +#define NET_TRANSPORT_ADS 0x08 + +struct functable { + const char *funcname; + int (*fn)(struct net_context *c, int argc, const char **argv); + int valid_transports; + const char *description; + const char *usage; +}; + +typedef NTSTATUS (*rpc_command_fn)(struct net_context *c, + const struct dom_sid *, + const char *, + struct cli_state *cli, + struct rpc_pipe_client *, + TALLOC_CTX *, + int, + const char **); + +typedef struct copy_clistate { + TALLOC_CTX *mem_ctx; + struct cli_state *cli_share_src; + struct cli_state *cli_share_dst; + char *cwd; + uint16_t attribute; + struct net_context *c; +}copy_clistate; + +struct rpc_sh_ctx { + struct cli_state *cli; + + struct dom_sid *domain_sid; + const char *domain_name; + + const char *whoami; + const char *thiscmd; + struct rpc_sh_cmd *cmds; + struct rpc_sh_ctx *parent; +}; + +struct rpc_sh_cmd { + const char *name; + struct rpc_sh_cmd *(*sub)(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); + const struct ndr_interface_table *table; + NTSTATUS (*fn)(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv); + const char *help; +}; + +enum netdom_domain_t { ND_TYPE_NT4, ND_TYPE_AD }; + +/* INCLUDE FILES */ + +#include "utils/net_proto.h" +#include "utils/net_help_common.h" + +/* MACROS & DEFINES */ + +#define NET_FLAGS_MASTER 0x00000001 +#define NET_FLAGS_DMB 0x00000002 +#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 0x00000004 /* Would it be insane to set 'localhost' + as the default remote host for this + operation? For example, localhost + is insane for a 'join' operation. */ +#define NET_FLAGS_PDC 0x00000008 /* PDC only */ +#define NET_FLAGS_ANONYMOUS 0x00000010 /* use an anonymous connection */ +#define NET_FLAGS_NO_PIPE 0x00000020 /* don't open an RPC pipe */ +#define NET_FLAGS_SIGN 0x00000040 /* sign RPC connection */ +#define NET_FLAGS_SEAL 0x00000080 /* seal RPC connection */ +#define NET_FLAGS_TCP 0x00000100 /* use ncacn_ip_tcp */ +#define NET_FLAGS_EXPECT_FALLBACK 0x00000200 /* the caller will fallback */ + +/* net share operation modes */ +#define NET_MODE_SHARE_MIGRATE 1 + diff --git a/source3/utils/net_ads.c b/source3/utils/net_ads.c new file mode 100644 index 0000000..d95a209 --- /dev/null +++ b/source3/utils/net_ads.c @@ -0,0 +1,4184 @@ +/* + Samba Unix/Linux SMB client library + net ads commands + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "libsmb/namequery.h" +#include "rpc_client/cli_pipe.h" +#include "librpc/gen_ndr/ndr_krb5pac.h" +#include "../librpc/gen_ndr/ndr_spoolss.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "ads.h" +#include "libads/cldap.h" +#include "../lib/addns/dnsquery.h" +#include "../libds/common/flags.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "smb_krb5.h" +#include "secrets.h" +#include "krb5_env.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "lib/param/loadparm.h" +#include "utils/net_dns.h" +#include "auth/kerberos/pac_utils.h" +#include "lib/util/string_wrappers.h" + +#ifdef HAVE_JANSSON +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#include "auth/common_auth.h" +#endif /* [HAVE_JANSSON] */ + +#ifdef HAVE_ADS + +/* when we do not have sufficient input parameters to contact a remote domain + * we always fall back to our own realm - Guenther*/ + +static const char *assume_own_realm(struct net_context *c) +{ + if (!c->opt_host && strequal(lp_workgroup(), c->opt_target_workgroup)) { + return lp_realm(); + } + + return NULL; +} + +#ifdef HAVE_JANSSON + +/* + * note: JSON output deliberately bypasses gettext so as to provide the same + * output irrespective of the locale. + */ + +static int output_json(const struct json_object *jsobj) +{ + TALLOC_CTX *ctx = NULL; + char *json = NULL; + + if (json_is_invalid(jsobj)) { + return -1; + } + + ctx = talloc_new(NULL); + if (ctx == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + return -1; + } + + json = json_to_string(ctx, jsobj); + if (!json) { + d_fprintf(stderr, _("error encoding to JSON\n")); + return -1; + } + + d_printf("%s\n", json); + TALLOC_FREE(ctx); + + return 0; +} + +static int net_ads_cldap_netlogon_json + (ADS_STRUCT *ads, + const char *addr, + const struct NETLOGON_SAM_LOGON_RESPONSE_EX *reply) +{ + struct json_object jsobj = json_new_object(); + struct json_object flagsobj = json_new_object(); + char response_type [32] = { '\0' }; + int ret = 0; + + if (json_is_invalid(&jsobj) || json_is_invalid(&flagsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + + goto failure; + } + + switch (reply->command) { + case LOGON_SAM_LOGON_USER_UNKNOWN_EX: + strncpy(response_type, + "LOGON_SAM_LOGON_USER_UNKNOWN_EX", + sizeof(response_type)); + break; + case LOGON_SAM_LOGON_RESPONSE_EX: + strncpy(response_type, "LOGON_SAM_LOGON_RESPONSE_EX", + sizeof(response_type)); + break; + default: + snprintf(response_type, sizeof(response_type), "0x%x", + reply->command); + break; + } + + ret = json_add_string(&jsobj, "Information for Domain Controller", + addr); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Response Type", response_type); + if (ret != 0) { + goto failure; + } + + ret = json_add_guid(&jsobj, "GUID", &reply->domain_uuid); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is a PDC", + reply->server_type & NBT_SERVER_PDC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is a GC of the forest", + reply->server_type & NBT_SERVER_GC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is an LDAP server", + reply->server_type & NBT_SERVER_LDAP); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Supports DS", + reply->server_type & NBT_SERVER_DS); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is running a KDC", + reply->server_type & NBT_SERVER_KDC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is running time services", + reply->server_type & NBT_SERVER_TIMESERV); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is the closest DC", + reply->server_type & NBT_SERVER_CLOSEST); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is writable", + reply->server_type & NBT_SERVER_WRITABLE); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Has a hardware clock", + reply->server_type & NBT_SERVER_GOOD_TIMESERV); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, + "Is a non-domain NC serviced by LDAP server", + reply->server_type & NBT_SERVER_NDNC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool + (&flagsobj, "Is NT6 DC that has some secrets", + reply->server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool + (&flagsobj, "Is NT6 DC that has all secrets", + reply->server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs Active Directory Web Services", + reply->server_type & NBT_SERVER_ADS_WEB_SERVICE); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs on Windows 2012 or later", + reply->server_type & NBT_SERVER_DS_8); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs on Windows 2012R2 or later", + reply->server_type & NBT_SERVER_DS_9); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Runs on Windows 2016 or later", + reply->server_type & NBT_SERVER_DS_10); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Has a DNS name", + reply->server_type & NBT_SERVER_HAS_DNS_NAME); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is a default NC", + reply->server_type & NBT_SERVER_IS_DEFAULT_NC); + if (ret != 0) { + goto failure; + } + + ret = json_add_bool(&flagsobj, "Is the forest root", + reply->server_type & NBT_SERVER_FOREST_ROOT); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Forest", reply->forest); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Domain", reply->dns_domain); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Domain Controller", reply->pdc_dns_name); + if (ret != 0) { + goto failure; + } + + + ret = json_add_string(&jsobj, "Pre-Win2k Domain", reply->domain_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Pre-Win2k Hostname", reply->pdc_name); + if (ret != 0) { + goto failure; + } + + if (*reply->user_name) { + ret = json_add_string(&jsobj, "User name", reply->user_name); + if (ret != 0) { + goto failure; + } + } + + ret = json_add_string(&jsobj, "Server Site Name", reply->server_site); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "Client Site Name", reply->client_site); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "NT Version", reply->nt_version); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "LMNT Token", reply->lmnt_token); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&jsobj, "LM20 Token", reply->lm20_token); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "Flags", &flagsobj); + if (ret != 0) { + goto failure; + } + + ret = output_json(&jsobj); + json_free(&jsobj); /* frees flagsobj recursively */ + + return ret; + +failure: + json_free(&flagsobj); + json_free(&jsobj); + + return ret; +} + +#else /* [HAVE_JANSSON] */ + +static int net_ads_cldap_netlogon_json + (ADS_STRUCT *ads, + const char *addr, + const struct NETLOGON_SAM_LOGON_RESPONSE_EX * reply) +{ + d_fprintf(stderr, _("JSON support not available\n")); + + return -1; +} + +#endif /* [HAVE_JANSSON] */ + +/* + do a cldap netlogon query +*/ +static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads) +{ + char addr[INET6_ADDRSTRLEN]; + struct NETLOGON_SAM_LOGON_RESPONSE_EX reply; + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + if ( !ads_cldap_netlogon_5(talloc_tos(), &ads->ldap.ss, ads->server.realm, &reply ) ) { + d_fprintf(stderr, _("CLDAP query failed!\n")); + return -1; + } + + if (c->opt_json) { + return net_ads_cldap_netlogon_json(ads, addr, &reply); + } + + d_printf(_("Information for Domain Controller: %s\n\n"), + addr); + + d_printf(_("Response Type: ")); + switch (reply.command) { + case LOGON_SAM_LOGON_USER_UNKNOWN_EX: + d_printf("LOGON_SAM_LOGON_USER_UNKNOWN_EX\n"); + break; + case LOGON_SAM_LOGON_RESPONSE_EX: + d_printf("LOGON_SAM_LOGON_RESPONSE_EX\n"); + break; + default: + d_printf("0x%x\n", reply.command); + break; + } + + d_printf(_("GUID: %s\n"), GUID_string(talloc_tos(),&reply.domain_uuid)); + + d_printf(_("Flags:\n" + "\tIs a PDC: %s\n" + "\tIs a GC of the forest: %s\n" + "\tIs an LDAP server: %s\n" + "\tSupports DS: %s\n" + "\tIs running a KDC: %s\n" + "\tIs running time services: %s\n" + "\tIs the closest DC: %s\n" + "\tIs writable: %s\n" + "\tHas a hardware clock: %s\n" + "\tIs a non-domain NC serviced by LDAP server: %s\n" + "\tIs NT6 DC that has some secrets: %s\n" + "\tIs NT6 DC that has all secrets: %s\n" + "\tRuns Active Directory Web Services: %s\n" + "\tRuns on Windows 2012 or later: %s\n" + "\tRuns on Windows 2012R2 or later: %s\n" + "\tRuns on Windows 2016 or later: %s\n" + "\tHas a DNS name: %s\n" + "\tIs a default NC: %s\n" + "\tIs the forest root: %s\n"), + (reply.server_type & NBT_SERVER_PDC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_GC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_LDAP) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_KDC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_TIMESERV) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_CLOSEST) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_WRITABLE) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_GOOD_TIMESERV) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_NDNC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_SELECT_SECRET_DOMAIN_6) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_FULL_SECRET_DOMAIN_6) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_ADS_WEB_SERVICE) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS_8) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS_9) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_DS_10) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_HAS_DNS_NAME) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_IS_DEFAULT_NC) ? _("yes") : _("no"), + (reply.server_type & NBT_SERVER_FOREST_ROOT) ? _("yes") : _("no")); + + + printf(_("Forest: %s\n"), reply.forest); + printf(_("Domain: %s\n"), reply.dns_domain); + printf(_("Domain Controller: %s\n"), reply.pdc_dns_name); + + printf(_("Pre-Win2k Domain: %s\n"), reply.domain_name); + printf(_("Pre-Win2k Hostname: %s\n"), reply.pdc_name); + + if (*reply.user_name) printf(_("User name: %s\n"), reply.user_name); + + printf(_("Server Site Name: %s\n"), reply.server_site); + printf(_("Client Site Name: %s\n"), reply.client_site); + + d_printf(_("NT Version: %d\n"), reply.nt_version); + d_printf(_("LMNT Token: %.2x\n"), reply.lmnt_token); + d_printf(_("LM20 Token: %.2x\n"), reply.lm20_token); + + return 0; +} + +/* + this implements the CLDAP based netlogon lookup requests + for finding the domain controller of a ADS domain +*/ +static int net_ads_lookup(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n" + "net ads lookup\n" + " %s", + _("Usage:"), + _("Find the ADS DC using CLDAP lookup.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the cldap server!\n")); + goto out; + } + + if (!ads->config.realm) { + ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup); + if (ads->config.realm == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + ads->ldap.port = 389; + } + + ret = net_ads_cldap_netlogon(c, ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + + +#ifdef HAVE_JANSSON + +static int net_ads_info_json(ADS_STRUCT *ads) +{ + int ret = 0; + char addr[INET6_ADDRSTRLEN]; + time_t pass_time; + struct json_object jsobj = json_new_object(); + + if (json_is_invalid(&jsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + + goto failure; + } + + pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup); + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + ret = json_add_string (&jsobj, "LDAP server", addr); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "LDAP server name", + ads->config.ldap_server_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "Realm", ads->config.realm); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "Bind Path", ads->config.bind_path); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "LDAP port", ads->ldap.port); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Server time", ads->config.current_time); + if (ret != 0) { + goto failure; + } + + ret = json_add_string (&jsobj, "KDC server", ads->auth.kdc_server); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Server time offset", + ads->auth.time_offset); + if (ret != 0) { + goto failure; + } + + ret = json_add_int (&jsobj, "Last machine account password change", + pass_time); + if (ret != 0) { + goto failure; + } + + ret = output_json(&jsobj); +failure: + json_free(&jsobj); + + return ret; +} + +#else /* [HAVE_JANSSON] */ + +static int net_ads_info_json(ADS_STRUCT *ads) +{ + d_fprintf(stderr, _("JSON support not available\n")); + + return -1; +} + +#endif /* [HAVE_JANSSON] */ + + + +static int net_ads_info(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + char addr[INET6_ADDRSTRLEN]; + time_t pass_time; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n" + "net ads info\n" + " %s", + _("Usage:"), + _("Display information about an Active Directory " + "server.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the ldap server!\n")); + goto out; + } + + if (!ads || !ads->config.realm) { + d_fprintf(stderr, _("Didn't find the ldap server!\n")); + goto out; + } + + /* Try to set the server's current time since we didn't do a full + TCP LDAP session initially */ + + if ( !ADS_ERR_OK(ads_current_time( ads )) ) { + d_fprintf( stderr, _("Failed to get server's current time!\n")); + } + + if (c->opt_json) { + ret = net_ads_info_json(ads); + goto out; + } + + pass_time = secrets_fetch_pass_last_set_time(ads->server.workgroup); + + print_sockaddr(addr, sizeof(addr), &ads->ldap.ss); + + d_printf(_("LDAP server: %s\n"), addr); + d_printf(_("LDAP server name: %s\n"), ads->config.ldap_server_name); + d_printf(_("Realm: %s\n"), ads->config.realm); + d_printf(_("Bind Path: %s\n"), ads->config.bind_path); + d_printf(_("LDAP port: %d\n"), ads->ldap.port); + d_printf(_("Server time: %s\n"), + http_timestring(tmp_ctx, ads->config.current_time)); + + d_printf(_("KDC server: %s\n"), ads->auth.kdc_server ); + d_printf(_("Server time offset: %d\n"), ads->auth.time_offset ); + + d_printf(_("Last machine account password change: %s\n"), + http_timestring(tmp_ctx, pass_time)); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static ADS_STATUS ads_startup_int(struct net_context *c, + bool only_own_domain, + uint32_t auth_flags, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads_ret) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool need_password = false; + bool second_time = false; + char *cp; + const char *realm = NULL; + bool tried_closest_dc = false; + enum credentials_use_kerberos krb5_state = + CRED_USE_KERBEROS_DISABLED; + + /* lp_realm() should be handled by a command line param, + However, the join requires that realm be set in smb.conf + and compares our realm with the remote server's so this is + ok until someone needs more flexibility */ + + *ads_ret = NULL; + +retry_connect: + if (only_own_domain) { + realm = lp_realm(); + } else { + realm = assume_own_realm(c); + } + + ads = ads_init(mem_ctx, + realm, + c->opt_target_workgroup, + c->opt_host, + ADS_SASL_PLAIN); + if (ads == NULL) { + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + if (!c->opt_user_name) { + c->opt_user_name = "administrator"; + } + + if (c->opt_user_specified) { + need_password = true; + } + +retry: + if (!c->opt_password && need_password && !c->opt_machine_pass) { + c->opt_password = net_prompt_pass(c, c->opt_user_name); + if (!c->opt_password) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + + if (c->opt_password) { + use_in_memory_ccache(); + ADS_TALLOC_CONST_FREE(ads->auth.password); + ads->auth.password = talloc_strdup(ads, c->opt_password); + if (ads->auth.password == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + } + + ADS_TALLOC_CONST_FREE(ads->auth.user_name); + ads->auth.user_name = talloc_strdup(ads, c->opt_user_name); + if (ads->auth.user_name == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR_NT(NT_STATUS_NO_MEMORY); + } + + ads->auth.flags |= auth_flags; + + /* The ADS code will handle FIPS mode */ + krb5_state = cli_credentials_get_kerberos_state(c->creds); + switch (krb5_state) { + case CRED_USE_KERBEROS_REQUIRED: + ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags &= ~ADS_AUTH_ALLOW_NTLMSSP; + break; + case CRED_USE_KERBEROS_DESIRED: + ads->auth.flags &= ~ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP; + break; + case CRED_USE_KERBEROS_DISABLED: + ads->auth.flags |= ADS_AUTH_DISABLE_KERBEROS; + ads->auth.flags |= ADS_AUTH_ALLOW_NTLMSSP; + break; + } + + /* + * If the username is of the form "name@realm", + * extract the realm and convert to upper case. + * This is only used to establish the connection. + */ + if ((cp = strchr_m(ads->auth.user_name, '@'))!=0) { + *cp++ = '\0'; + ADS_TALLOC_CONST_FREE(ads->auth.realm); + ads->auth.realm = talloc_asprintf_strupper_m(ads, "%s", cp); + if (ads->auth.realm == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } else if (ads->auth.realm == NULL) { + const char *c_realm = cli_credentials_get_realm(c->creds); + + if (c_realm != NULL) { + ads->auth.realm = talloc_strdup(ads, c_realm); + if (ads->auth.realm == NULL) { + TALLOC_FREE(ads); + return ADS_ERROR(LDAP_NO_MEMORY); + } + } + } + + status = ads_connect(ads); + + if (!ADS_ERR_OK(status)) { + + if (NT_STATUS_EQUAL(ads_ntstatus(status), + NT_STATUS_NO_LOGON_SERVERS)) { + DEBUG(0,("ads_connect: %s\n", ads_errstr(status))); + TALLOC_FREE(ads); + return status; + } + + if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) { + need_password = true; + second_time = true; + goto retry; + } else { + TALLOC_FREE(ads); + return status; + } + } + + /* when contacting our own domain, make sure we use the closest DC. + * This is done by reconnecting to ADS because only the first call to + * ads_connect will give us our own sitename */ + + if ((only_own_domain || !c->opt_host) && !tried_closest_dc) { + + tried_closest_dc = true; /* avoid loop */ + + if (!ads_closest_dc(ads)) { + + namecache_delete(ads->server.realm, 0x1C); + namecache_delete(ads->server.workgroup, 0x1C); + + TALLOC_FREE(ads); + + goto retry_connect; + } + } + + *ads_ret = talloc_move(mem_ctx, &ads); + return status; +} + +ADS_STATUS ads_startup(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads) +{ + return ads_startup_int(c, only_own_domain, 0, mem_ctx, ads); +} + +ADS_STATUS ads_startup_nobind(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + ADS_STRUCT **ads) +{ + return ads_startup_int(c, + only_own_domain, + ADS_AUTH_NO_BIND, + mem_ctx, + ads); +} + +/* + Check to see if connection can be made via ads. + ads_startup() stores the password in opt_password if it needs to so + that rpc or rap can use it without re-prompting. +*/ +static int net_ads_check_int(struct net_context *c, + const char *realm, + const char *workgroup, + const char *host) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads; + ADS_STATUS status; + int ret = -1; + + ads = ads_init(tmp_ctx, realm, workgroup, host, ADS_SASL_PLAIN); + if (ads == NULL) { + goto out; + } + + ads->auth.flags |= ADS_AUTH_NO_BIND; + + status = ads_connect(ads); + if ( !ADS_ERR_OK(status) ) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_check_our_domain(struct net_context *c) +{ + return net_ads_check_int(c, lp_realm(), lp_workgroup(), NULL); +} + +int net_ads_check(struct net_context *c) +{ + return net_ads_check_int(c, NULL, c->opt_workgroup, c->opt_host); +} + +/* + determine the netbios workgroup name for a domain + */ +static int net_ads_workgroup(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + struct NETLOGON_SAM_LOGON_RESPONSE_EX reply; + bool ok = false; + int ret = -1; + + if (c->display_usage) { + d_printf ("%s\n" + "net ads workgroup\n" + " %s\n", + _("Usage:"), + _("Print the workgroup name")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup_nobind(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Didn't find the cldap server!\n")); + goto out; + } + + if (!ads->config.realm) { + ads->config.realm = talloc_strdup(ads, c->opt_target_workgroup); + if (ads->config.realm == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + ads->ldap.port = 389; + } + + ok = ads_cldap_netlogon_5(tmp_ctx, + &ads->ldap.ss, ads->server.realm, &reply); + if (!ok) { + d_fprintf(stderr, _("CLDAP query failed!\n")); + goto out; + } + + d_printf(_("Workgroup: %s\n"), reply.domain_name); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +} + + + +static bool usergrp_display(ADS_STRUCT *ads, char *field, void **values, void *data_area) +{ + char **disp_fields = (char **) data_area; + + if (!field) { /* must be end of record */ + if (disp_fields[0]) { + if (!strchr_m(disp_fields[0], '$')) { + if (disp_fields[1]) + d_printf("%-21.21s %s\n", + disp_fields[0], disp_fields[1]); + else + d_printf("%s\n", disp_fields[0]); + } + } + SAFE_FREE(disp_fields[0]); + SAFE_FREE(disp_fields[1]); + return true; + } + if (!values) /* must be new field, indicate string field */ + return true; + if (strcasecmp_m(field, "sAMAccountName") == 0) { + disp_fields[0] = SMB_STRDUP((char *) values[0]); + } + if (strcasecmp_m(field, "description") == 0) + disp_fields[1] = SMB_STRDUP((char *) values[0]); + return true; +} + +static int net_ads_user_usage(struct net_context *c, int argc, const char **argv) +{ + return net_user_usage(c, argc, argv); +} + +static int ads_user_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + char *upn, *userdn; + LDAPMessage *res=NULL; + int rc = -1; + char *ou_str = NULL; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_user_add: %s\n"), ads_errstr(status)); + goto done; + } + + if (ads_count_replies(ads, res)) { + d_fprintf(stderr, _("ads_user_add: User %s already exists\n"), + argv[0]); + goto done; + } + + if (c->opt_container) { + ou_str = SMB_STRDUP(c->opt_container); + } else { + ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER); + } + + status = ads_add_user_acct(ads, argv[0], ou_str, c->opt_comment); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Could not add user %s: %s\n"), argv[0], + ads_errstr(status)); + goto done; + } + + /* if no password is to be set, we're done */ + if (argc == 1) { + d_printf(_("User %s added\n"), argv[0]); + rc = 0; + goto done; + } + + /* try setting the password */ + upn = talloc_asprintf(tmp_ctx, + "%s@%s", + argv[0], + ads->config.realm); + if (upn == NULL) { + goto done; + } + + status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], + ads->auth.time_offset); + if (ADS_ERR_OK(status)) { + d_printf(_("User %s added\n"), argv[0]); + rc = 0; + goto done; + } + TALLOC_FREE(upn); + + /* password didn't set, delete account */ + d_fprintf(stderr, _("Could not add user %s. " + "Error setting password %s\n"), + argv[0], ads_errstr(status)); + + ads_msgfree(ads, res); + res = NULL; + + status=ads_find_user_acct(ads, &res, argv[0]); + if (ADS_ERR_OK(status)) { + userdn = ads_get_dn(ads, tmp_ctx, res); + ads_del_dn(ads, userdn); + TALLOC_FREE(userdn); + } + + done: + ads_msgfree(ads, res); + SAFE_FREE(ou_str); + TALLOC_FREE(tmp_ctx); + return rc; +} + +static int ads_user_info(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + wbcErr wbc_status; + const char *attrs[] = {"memberOf", "primaryGroupID", NULL}; + char *searchstring = NULL; + char **grouplist = NULL; + char *primary_group = NULL; + char *escaped_user = NULL; + struct dom_sid primary_group_sid; + uint32_t group_rid; + enum wbcSidType type; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + escaped_user = escape_ldap_string(tmp_ctx, argv[0]); + if (!escaped_user) { + d_fprintf(stderr, + _("ads_user_info: failed to escape user %s\n"), + argv[0]); + goto out; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + searchstring = talloc_asprintf(tmp_ctx, + "(sAMAccountName=%s)", + escaped_user); + if (searchstring == NULL) { + goto out; + } + + status = ads_search(ads, &res, searchstring, attrs); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_search: %s\n"), ads_errstr(status)); + goto out; + } + + if (!ads_pull_uint32(ads, res, "primaryGroupID", &group_rid)) { + d_fprintf(stderr, _("ads_pull_uint32 failed\n")); + goto out; + } + + status = ads_domain_sid(ads, &primary_group_sid); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_domain_sid: %s\n"), ads_errstr(status)); + goto out; + } + + sid_append_rid(&primary_group_sid, group_rid); + + wbc_status = wbcLookupSid((struct wbcDomainSid *)&primary_group_sid, + NULL, /* don't look up domain */ + &primary_group, + &type); + if (!WBC_ERROR_IS_OK(wbc_status)) { + d_fprintf(stderr, "wbcLookupSid: %s\n", + wbcErrorString(wbc_status)); + goto out; + } + + d_printf("%s\n", primary_group); + + wbcFreeMemory(primary_group); + + grouplist = ldap_get_values((LDAP *)ads->ldap.ld, + (LDAPMessage *)res, "memberOf"); + + if (grouplist) { + int i; + char **groupname; + for (i=0;grouplist[i];i++) { + groupname = ldap_explode_dn(grouplist[i], 1); + d_printf("%s\n", groupname[0]); + ldap_value_free(groupname); + } + ldap_value_free(grouplist); + } + + ret = 0; +out: + TALLOC_FREE(escaped_user); + TALLOC_FREE(searchstring); + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int ads_user_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + char *userdn = NULL; + int ret = -1; + + if (argc < 1) { + TALLOC_FREE(tmp_ctx); + return net_ads_user_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) { + d_printf(_("User %s does not exist.\n"), argv[0]); + goto out; + } + + userdn = ads_get_dn(ads, tmp_ctx, res); + if (userdn == NULL) { + goto out; + } + + status = ads_del_dn(ads, userdn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Error deleting user %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + d_printf(_("User %s deleted\n"), argv[0]); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_user(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + ads_user_add, + NET_TRANSPORT_ADS, + N_("Add an AD user"), + N_("net ads user add\n" + " Add an AD user") + }, + { + "info", + ads_user_info, + NET_TRANSPORT_ADS, + N_("Display information about an AD user"), + N_("net ads user info\n" + " Display information about an AD user") + }, + { + "delete", + ads_user_delete, + NET_TRANSPORT_ADS, + N_("Delete an AD user"), + N_("net ads user delete\n" + " Delete an AD user") + }, + {NULL, NULL, 0, NULL, NULL} + }; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *shortattrs[] = {"sAMAccountName", NULL}; + const char *longattrs[] = {"sAMAccountName", "description", NULL}; + char *disp_fields[2] = {NULL, NULL}; + int ret = -1; + + if (argc > 0) { + TALLOC_FREE(tmp_ctx); + return net_run_function(c, argc, argv, "net ads user", func); + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads user\n" + " %s\n", + _("Usage:"), + _("List AD users")); + net_display_usage_from_functable(func); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (c->opt_long_list_entries) + d_printf(_("\nUser name Comment" + "\n-----------------------------\n")); + + status = ads_do_search_all_fn(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + "(objectCategory=user)", + c->opt_long_list_entries ? + longattrs : shortattrs, + usergrp_display, + disp_fields); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_group_usage(struct net_context *c, int argc, const char **argv) +{ + return net_group_usage(c, argc, argv); +} + +static int ads_group_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + char *ou_str = NULL; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_group_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_group_add: %s\n"), ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res)) { + d_fprintf(stderr, _("ads_group_add: Group %s already exists\n"), argv[0]); + goto out; + } + + if (c->opt_container) { + ou_str = SMB_STRDUP(c->opt_container); + } else { + ou_str = ads_default_ou_string(ads, DS_GUID_USERS_CONTAINER); + } + + status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Could not add group %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + d_printf(_("Group %s added\n"), argv[0]); + + ret = 0; + out: + ads_msgfree(ads, res); + SAFE_FREE(ou_str); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int ads_group_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + char *groupdn = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_group_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(status) || ads_count_replies(ads, res) != 1) { + d_printf(_("Group %s does not exist.\n"), argv[0]); + goto out; + } + + groupdn = ads_get_dn(ads, tmp_ctx, res); + if (groupdn == NULL) { + goto out; + } + + status = ads_del_dn(ads, groupdn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Error deleting group %s: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + d_printf(_("Group %s deleted\n"), argv[0]); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_group(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + ads_group_add, + NET_TRANSPORT_ADS, + N_("Add an AD group"), + N_("net ads group add\n" + " Add an AD group") + }, + { + "delete", + ads_group_delete, + NET_TRANSPORT_ADS, + N_("Delete an AD group"), + N_("net ads group delete\n" + " Delete an AD group") + }, + {NULL, NULL, 0, NULL, NULL} + }; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *shortattrs[] = {"sAMAccountName", NULL}; + const char *longattrs[] = {"sAMAccountName", "description", NULL}; + char *disp_fields[2] = {NULL, NULL}; + int ret = -1; + + if (argc >= 0) { + TALLOC_FREE(tmp_ctx); + return net_run_function(c, argc, argv, "net ads group", func); + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads group\n" + " %s\n", + _("Usage:"), + _("List AD groups")); + net_display_usage_from_functable(func); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (c->opt_long_list_entries) + d_printf(_("\nGroup name Comment" + "\n-----------------------------\n")); + + status = ads_do_search_all_fn(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + "(objectCategory=group)", + c->opt_long_list_entries ? + longattrs : shortattrs, + usergrp_display, + disp_fields); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_status(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads status\n" + " %s\n", + _("Usage:"), + _("Display machine account details")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_machine_acct(ads, &res, lp_netbios_name()); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_machine_acct: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("No machine account for '%s' found\n"), + lp_netbios_name()); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +/******************************************************************* + Leave an AD domain. Windows XP disables the machine account. + We'll try the same. The old code would do an LDAP delete. + That only worked using the machine creds because added the machine + with full control to the computer object's ACL. +*******************************************************************/ + +static int net_ads_leave(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct libnet_UnjoinCtx *r = NULL; + WERROR werr; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads leave [--keep-account]\n" + " %s\n", + _("Usage:"), + _("Leave an AD domain")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!*lp_realm()) { + d_fprintf(stderr, _("No realm set, are we joined ?\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!c->opt_kerberos) { + use_in_memory_ccache(); + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + goto done; + } + + werr = libnet_init_UnjoinCtx(tmp_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("Could not initialise unjoin context.\n")); + goto done; + } + + r->in.debug = true; + r->in.use_kerberos = c->opt_kerberos; + r->in.dc_name = c->opt_host; + r->in.domain_name = lp_realm(); + r->in.admin_account = c->opt_user_name; + r->in.admin_password = net_prompt_pass(c, c->opt_user_name); + r->in.modify_config = lp_config_backend_is_registry(); + + /* Try to delete it, but if that fails, disable it. The + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable */ + r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE; + if (c->opt_keep_account) { + r->in.delete_machine_account = false; + } else { + r->in.delete_machine_account = true; + } + + r->in.msg_ctx = c->msg_ctx; + + werr = libnet_Unjoin(tmp_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + d_printf(_("Failed to leave domain: %s\n"), + r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + goto done; + } + + if (r->out.deleted_machine_account) { + d_printf(_("Deleted account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + ret = 0; + goto done; + } + + /* We couldn't delete it - see if the disable succeeded. */ + if (r->out.disabled_machine_account) { + d_printf(_("Disabled account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + ret = 0; + goto done; + } + + /* Based on what we requested, we shouldn't get here, but if + we did, it means the secrets were removed, and therefore + we have left the domain */ + d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + + ret = 0; + done: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static ADS_STATUS net_ads_join_ok(struct net_context *c) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + fstring dc_name; + struct sockaddr_storage dcip; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + TALLOC_FREE(tmp_ctx); + return ADS_ERROR_NT(NT_STATUS_ACCESS_DENIED); + } + + net_warn_member_options(); + + net_use_krb_machine_account(c); + + get_dc_name(lp_workgroup(), lp_realm(), dc_name, &dcip); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ADS_ERROR_NT(NT_STATUS_OK); +out: + TALLOC_FREE(tmp_ctx); + return status; +} + +/* + check that an existing join is OK + */ +int net_ads_testjoin(struct net_context *c, int argc, const char **argv) +{ + ADS_STATUS status; + use_in_memory_ccache(); + + if (c->display_usage) { + d_printf( "%s\n" + "net ads testjoin\n" + " %s\n", + _("Usage:"), + _("Test if the existing join is ok")); + return -1; + } + + net_warn_member_options(); + + /* Display success or failure */ + status = net_ads_join_ok(c); + if (!ADS_ERR_OK(status)) { + fprintf(stderr, _("Join to domain is not valid: %s\n"), + get_friendly_nt_error_msg(ads_ntstatus(status))); + return -1; + } + + printf(_("Join is OK\n")); + return 0; +} + +/******************************************************************* + Simple config checks before beginning the join + ********************************************************************/ + +static WERROR check_ads_config( void ) +{ + if (lp_server_role() != ROLE_DOMAIN_MEMBER ) { + d_printf(_("Host is not configured as a member server.\n")); + return WERR_INVALID_DOMAIN_ROLE; + } + + if (strlen(lp_netbios_name()) > 15) { + d_printf(_("Our netbios name can be at most 15 chars long, " + "\"%s\" is %u chars long\n"), lp_netbios_name(), + (unsigned int)strlen(lp_netbios_name())); + return WERR_INVALID_COMPUTERNAME; + } + + if ( lp_security() == SEC_ADS && !*lp_realm()) { + d_fprintf(stderr, _("realm must be set in %s for ADS " + "join to succeed.\n"), get_dyn_CONFIGFILE()); + return WERR_INVALID_PARAMETER; + } + + return WERR_OK; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_join_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net ads join [--no-dns-updates] [options]\n" + "Valid options:\n")); + d_printf(_(" dnshostname=FQDN Set the dnsHostName attribute during the join.\n" + " The default is in the form netbiosname.dnsdomain\n")); + d_printf(_(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n" + " The default UPN is in the form host/netbiosname@REALM.\n")); + d_printf(_(" createcomputer=OU Precreate the computer account in a specific OU.\n" + " The OU string read from top to bottom without RDNs\n" + " and delimited by a '/'.\n" + " E.g. \"createcomputer=Computers/Servers/Unix\"\n" + " NB: A backslash '\\' is used as escape at multiple\n" + " levels and may need to be doubled or even\n" + " quadrupled. It is not used as a separator.\n")); + d_printf(_(" machinepass=PASS Set the machine password to a specific value during\n" + " the join. The default password is random.\n")); + d_printf(_(" osName=string Set the operatingSystem attribute during the join.\n")); + d_printf(_(" osVer=string Set the operatingSystemVersion attribute during join.\n" + " NB: osName and osVer must be specified together for\n" + " either to take effect. The operatingSystemService\n" + " attribute is then also set along with the two\n" + " other attributes.\n")); + d_printf(_(" osServicePack=string Set the operatingSystemServicePack attribute\n" + " during the join.\n" + " NB: If not specified then by default the samba\n" + " version string is used instead.\n")); + return -1; +} + + +int net_ads_join(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct libnet_JoinCtx *r = NULL; + const char *domain = lp_realm(); + WERROR werr = WERR_NERR_SETUPNOTJOINED; + bool createupn = false; + const char *dnshostname = NULL; + const char *machineupn = NULL; + const char *machine_password = NULL; + const char *create_in_ou = NULL; + int i; + const char *os_name = NULL; + const char *os_version = NULL; + const char *os_servicepack = NULL; + bool modify_config = lp_config_backend_is_registry(); + enum libnetjoin_JoinDomNameType domain_name_type = JoinDomNameTypeDNS; + int ret = -1; + + if (c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_join_usage(c, argc, argv); + } + + net_warn_member_options(); + + if (!modify_config) { + werr = check_ads_config(); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("Invalid configuration. Exiting....\n")); + goto fail; + } + } + + if (!c->opt_kerberos) { + use_in_memory_ccache(); + } + + werr = libnet_init_JoinCtx(tmp_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* process additional command line args */ + + for ( i=0; i<argc; i++ ) { + if ( !strncasecmp_m(argv[i], "dnshostname", strlen("dnshostname")) ) { + dnshostname = get_string_param(argv[i]); + } + else if ( !strncasecmp_m(argv[i], "createupn", strlen("createupn")) ) { + createupn = true; + machineupn = get_string_param(argv[i]); + } + else if ( !strncasecmp_m(argv[i], "createcomputer", strlen("createcomputer")) ) { + if ( (create_in_ou = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid OU path.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osName", strlen("osName")) ) { + if ( (os_name = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a operating system name.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osVer", strlen("osVer")) ) { + if ( (os_version = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid operating system version.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "osServicePack", strlen("osServicePack")) ) { + if ( (os_servicepack = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid servicepack identifier.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } + else if ( !strncasecmp_m(argv[i], "machinepass", strlen("machinepass")) ) { + if ( (machine_password = get_string_param(argv[i])) == NULL ) { + d_fprintf(stderr, _("Please supply a valid password to set as trust account password.\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + } else { + domain = argv[i]; + if (strchr(domain, '.') == NULL) { + domain_name_type = JoinDomNameTypeUnknown; + } else { + domain_name_type = JoinDomNameTypeDNS; + } + } + } + + if (!*domain) { + d_fprintf(stderr, _("Please supply a valid domain name\n")); + werr = WERR_INVALID_PARAMETER; + goto fail; + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + werr = WERR_ACCESS_DENIED; + goto fail; + } + + /* Do the domain join here */ + + r->in.domain_name = domain; + r->in.domain_name_type = domain_name_type; + r->in.create_upn = createupn; + r->in.upn = machineupn; + r->in.dnshostname = dnshostname; + r->in.account_ou = create_in_ou; + r->in.os_name = os_name; + r->in.os_version = os_version; + r->in.os_servicepack = os_servicepack; + r->in.dc_name = c->opt_host; + r->in.admin_account = c->opt_user_name; + r->in.admin_password = net_prompt_pass(c, c->opt_user_name); + r->in.machine_password = machine_password; + r->in.debug = true; + r->in.use_kerberos = c->opt_kerberos; + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + r->in.msg_ctx = c->msg_ctx; + + werr = libnet_Join(tmp_ctx, r); + if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND) && + strequal(domain, lp_realm())) { + r->in.domain_name = lp_workgroup(); + r->in.domain_name_type = JoinDomNameTypeNBT; + werr = libnet_Join(tmp_ctx, r); + } + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* Check the short name of the domain */ + + if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { + d_printf(_("The workgroup in %s does not match the short\n" + "domain name obtained from the server.\n" + "Using the name [%s] from the server.\n" + "You should set \"workgroup = %s\" in %s.\n"), + get_dyn_CONFIGFILE(), r->out.netbios_domain_name, + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + d_printf(_("Using short domain name -- %s\n"), r->out.netbios_domain_name); + + if (r->out.dns_domain_name) { + d_printf(_("Joined '%s' to dns domain '%s'\n"), r->in.machine_name, + r->out.dns_domain_name); + } else { + d_printf(_("Joined '%s' to domain '%s'\n"), r->in.machine_name, + r->out.netbios_domain_name); + } + + /* print out informative error string in case there is one */ + if (r->out.error_string != NULL) { + d_printf("%s\n", r->out.error_string); + } + + /* + * We try doing the dns update (if it was compiled in + * and if it was not disabled on the command line). + * If the dns update fails, we still consider the join + * operation as succeeded if we came this far. + */ + if (!c->opt_no_dns_updates) { + net_ads_join_dns_updates(c, tmp_ctx, r); + } + + ret = 0; + +fail: + if (ret != 0) { + /* issue an overall failure message at the end. */ + d_printf(_("Failed to join domain: %s\n"), + r && r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + } + + TALLOC_FREE(tmp_ctx); + + return ret; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_dns_register(struct net_context *c, int argc, const char **argv) +{ +#if defined(HAVE_KRB5) + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + NTSTATUS ntstatus; + const char *hostname = NULL; + const char **addrs_list = NULL; + struct sockaddr_storage *addrs = NULL; + int num_addrs = 0; + int count; + int ret = -1; + +#ifdef DEVELOPER + talloc_enable_leak_report(); +#endif + + if (argc <= 1 && lp_clustering() && lp_cluster_addresses() == NULL) { + d_fprintf(stderr, _("Refusing DNS updates with automatic " + "detection of addresses in a clustered " + "setup.\n")); + c->display_usage = true; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads dns register [hostname [IP [IP...]]] " + "[--force] [--dns-ttl TTL]\n" + " %s\n", + _("Usage:"), + _("Register hostname with DNS\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (argc >= 1) { + hostname = argv[0]; + } + + if (argc > 1) { + num_addrs = argc - 1; + addrs_list = &argv[1]; + } else if (lp_clustering()) { + addrs_list = lp_cluster_addresses(); + num_addrs = str_list_length(addrs_list); + } + + if (num_addrs > 0) { + addrs = talloc_zero_array(tmp_ctx, + struct sockaddr_storage, + num_addrs); + if (addrs == NULL) { + d_fprintf(stderr, _("Error allocating memory!\n")); + goto out; + } + } + + for (count = 0; count < num_addrs; count++) { + if (!interpret_string_addr(&addrs[count], addrs_list[count], 0)) { + d_fprintf(stderr, "%s '%s'.\n", + _("Cannot interpret address"), + addrs_list[count]); + goto out; + } + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if ( !ADS_ERR_OK(status) ) { + DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status))); + goto out; + } + + ntstatus = net_update_dns_ext(c, + tmp_ctx, + ads, + hostname, + addrs, + num_addrs, + false); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf( stderr, _("DNS update failed!\n") ); + goto out; + } + + d_fprintf( stderr, _("Successfully registered hostname with DNS\n") ); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +#else + d_fprintf(stderr, + _("DNS update support not enabled at compile time!\n")); + return -1; +#endif +} + +static int net_ads_dns_unregister(struct net_context *c, + int argc, + const char **argv) +{ +#if defined(HAVE_KRB5) + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + NTSTATUS ntstatus; + const char *hostname = NULL; + int ret = -1; + +#ifdef DEVELOPER + talloc_enable_leak_report(); +#endif + + if (argc != 1) { + c->display_usage = true; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net ads dns unregister [hostname]\n" + " %s\n", + _("Usage:"), + _("Remove all IP Address entries for a given\n" + " hostname from the Active Directory server.\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + /* Get the hostname for un-registering */ + hostname = argv[0]; + + status = ads_startup(c, true, tmp_ctx, &ads); + if ( !ADS_ERR_OK(status) ) { + DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status))); + goto out; + } + + ntstatus = net_update_dns_ext(c, + tmp_ctx, + ads, + hostname, + NULL, + 0, + true); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf( stderr, _("DNS update failed!\n") ); + goto out; + } + + d_fprintf( stderr, _("Successfully un-registered hostname from DNS\n")); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +#else + d_fprintf(stderr, + _("DNS update support not enabled at compile time!\n")); + return -1; +#endif +} + + +static int net_ads_dns_async(struct net_context *c, int argc, const char **argv) +{ + size_t num_names = 0; + char **hostnames = NULL; + size_t i = 0; + struct samba_sockaddr *addrs = NULL; + NTSTATUS status; + + if (argc != 1 || c->display_usage) { + d_printf( "%s\n" + " %s\n" + " %s\n", + _("Usage:"), + _("net ads dns async <name>\n"), + _(" Async look up hostname from the DNS server\n" + " hostname\tName to look up\n")); + return -1; + } + + status = ads_dns_lookup_a(talloc_tos(), + argv[0], + &num_names, + &hostnames, + &addrs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Looking up A record for %s got error %s\n", + argv[0], + nt_errstr(status)); + return -1; + } + d_printf("Async A record lookup - got %u names for %s\n", + (unsigned int)num_names, + argv[0]); + for (i = 0; i < num_names; i++) { + char addr_buf[INET6_ADDRSTRLEN]; + print_sockaddr(addr_buf, + sizeof(addr_buf), + &addrs[i].u.ss); + d_printf("hostname[%u] = %s, IPv4addr = %s\n", + (unsigned int)i, + hostnames[i], + addr_buf); + } + +#if defined(HAVE_IPV6) + status = ads_dns_lookup_aaaa(talloc_tos(), + argv[0], + &num_names, + &hostnames, + &addrs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Looking up AAAA record for %s got error %s\n", + argv[0], + nt_errstr(status)); + return -1; + } + d_printf("Async AAAA record lookup - got %u names for %s\n", + (unsigned int)num_names, + argv[0]); + for (i = 0; i < num_names; i++) { + char addr_buf[INET6_ADDRSTRLEN]; + print_sockaddr(addr_buf, + sizeof(addr_buf), + &addrs[i].u.ss); + d_printf("hostname[%u] = %s, IPv6addr = %s\n", + (unsigned int)i, + hostnames[i], + addr_buf); + } +#endif + return 0; +} + + +static int net_ads_dns(struct net_context *c, int argc, const char *argv[]) +{ + struct functable func[] = { + { + "register", + net_ads_dns_register, + NET_TRANSPORT_ADS, + N_("Add host dns entry to AD"), + N_("net ads dns register\n" + " Add host dns entry to AD") + }, + { + "unregister", + net_ads_dns_unregister, + NET_TRANSPORT_ADS, + N_("Remove host dns entry from AD"), + N_("net ads dns unregister\n" + " Remove host dns entry from AD") + }, + { + "async", + net_ads_dns_async, + NET_TRANSPORT_ADS, + N_("Look up host"), + N_("net ads dns async\n" + " Look up host using async DNS") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads dns", func); +} + +/******************************************************************* + ********************************************************************/ + +int net_ads_printer_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( +"\nnet ads printer search <printer>" +"\n\tsearch for a printer in the directory\n" +"\nnet ads printer info <printer> <server>" +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish <printername>" +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove <printername>" +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n")); + return -1; +} + +/******************************************************************* + ********************************************************************/ + +static int net_ads_printer_search(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads printer search\n" + " %s\n", + _("Usage:"), + _("List printers in the AD")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_printers(ads, &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_printer: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("No results found\n")); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer_info(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + const char *printername = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer info [printername [servername]]\n" + " Display printer info from AD\n" + " printername\tPrinter name or wildcard\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 0) { + printername = argv[0]; + } else { + printername = "*"; + } + + if (argc > 1) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + status = ads_find_printer_on_server(ads, &res, printername, servername); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Server '%s' not found: %s\n"), + servername, ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Printer '%s' not found\n"), printername); + goto out; + } + + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer_publish(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + const char *printername = NULL; + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + struct sockaddr_storage server_ss = { 0 }; + NTSTATUS nt_status; + ADS_MODLIST mods = NULL; + char *prt_dn = NULL; + char *srv_dn = NULL; + char **srv_cn = NULL; + char *srv_cn_escaped = NULL; + char *printername_escaped = NULL; + LDAPMessage *res = NULL; + bool ok; + int ret = -1; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer publish <printername> [servername]\n" + " Publish printer in AD\n" + " printername\tName of the printer\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + mods = ads_init_mods(tmp_ctx); + if (mods == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + printername = argv[0]; + + if (argc == 2) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + /* Get printer data from SPOOLSS */ + + ok = resolve_name(servername, &server_ss, 0x20, false); + if (!ok) { + d_fprintf(stderr, _("Could not find server %s\n"), + servername); + goto out; + } + + cli_credentials_set_kerberos_state(c->creds, + CRED_USE_KERBEROS_REQUIRED, + CRED_SPECIFIED); + + nt_status = cli_full_connection_creds(&cli, lp_netbios_name(), servername, + &server_ss, 0, + "IPC$", "IPC", + c->creds, + CLI_FULL_CONNECTION_IPC); + + if (NT_STATUS_IS_ERR(nt_status)) { + d_fprintf(stderr, _("Unable to open a connection to %s to " + "obtain data for %s\n"), + servername, printername); + goto out; + } + + /* Publish on AD server */ + + ads_find_machine_acct(ads, &res, servername); + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Could not find machine account for server " + "%s\n"), + servername); + goto out; + } + + srv_dn = ldap_get_dn((LDAP *)ads->ldap.ld, (LDAPMessage *)res); + srv_cn = ldap_explode_dn(srv_dn, 1); + + srv_cn_escaped = escape_rdn_val_string_alloc(srv_cn[0]); + printername_escaped = escape_rdn_val_string_alloc(printername); + if (!srv_cn_escaped || !printername_escaped) { + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + d_fprintf(stderr, _("Internal error, out of memory!")); + goto out; + } + + prt_dn = talloc_asprintf(tmp_ctx, + "cn=%s-%s,%s", + srv_cn_escaped, + printername_escaped, + srv_dn); + if (prt_dn == NULL) { + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + d_fprintf(stderr, _("Internal error, out of memory!")); + goto out; + } + + SAFE_FREE(srv_cn_escaped); + SAFE_FREE(printername_escaped); + + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_spoolss, &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("Unable to open a connection to the spoolss pipe on %s\n"), + servername); + goto out; + } + + if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, + tmp_ctx, + &mods, + printername))) { + goto out; + } + + status = ads_add_printer_entry(ads, prt_dn, tmp_ctx, &mods); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, "ads_publish_printer: %s\n", + ads_errstr(status)); + goto out; + } + + d_printf("published printer\n"); + + ret = 0; +out: + talloc_destroy(tmp_ctx); + + return ret; +} + +static int net_ads_printer_remove(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *servername = NULL; + char *prt_dn = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads printer remove <printername> [servername]\n" + " Remove a printer from the AD\n" + " printername\tName of the printer\n" + " servername\tName of the print server\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + servername = argv[1]; + } else { + servername = lp_netbios_name(); + } + + status = ads_find_printer_on_server(ads, &res, argv[0], servername); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_find_printer_on_server: %s\n"), + ads_errstr(status)); + goto out; + } + + if (ads_count_replies(ads, res) == 0) { + d_fprintf(stderr, _("Printer '%s' not found\n"), argv[1]); + goto out; + } + + prt_dn = ads_get_dn(ads, tmp_ctx, res); + if (prt_dn == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = ads_del_dn(ads, prt_dn); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("ads_del_dn: %s\n"), ads_errstr(status)); + goto out; + } + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_printer(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "search", + net_ads_printer_search, + NET_TRANSPORT_ADS, + N_("Search for a printer"), + N_("net ads printer search\n" + " Search for a printer") + }, + { + "info", + net_ads_printer_info, + NET_TRANSPORT_ADS, + N_("Display printer information"), + N_("net ads printer info\n" + " Display printer information") + }, + { + "publish", + net_ads_printer_publish, + NET_TRANSPORT_ADS, + N_("Publish a printer"), + N_("net ads printer publish\n" + " Publish a printer") + }, + { + "remove", + net_ads_printer_remove, + NET_TRANSPORT_ADS, + N_("Delete a printer"), + N_("net ads printer remove\n" + " Delete a printer") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads printer", func); +} + + +static int net_ads_password(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + const char *auth_principal = cli_credentials_get_username(c->creds); + const char *auth_password = cli_credentials_get_password(c->creds); + const char *realm = NULL; + char *new_password = NULL; + char *chr = NULL; + char *prompt = NULL; + const char *user = NULL; + char pwd[256] = {0}; + ADS_STATUS status; + int ret = 0; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads password <username>\n" + " Change password for user\n" + " username\tName of user to change password for\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (auth_principal == NULL || auth_password == NULL) { + d_fprintf(stderr, _("You must supply an administrator " + "username/password\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (argc < 1) { + d_fprintf(stderr, _("ERROR: You must say which username to " + "change password for\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (strchr_m(argv[0], '@')) { + user = talloc_strdup(tmp_ctx, argv[0]); + } else { + user = talloc_asprintf(tmp_ctx, "%s@%s", argv[0], lp_realm()); + } + if (user == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + use_in_memory_ccache(); + chr = strchr_m(auth_principal, '@'); + if (chr) { + realm = ++chr; + } else { + realm = lp_realm(); + } + + /* use the realm so we can eventually change passwords for users + in realms other than default */ + ads = ads_init(tmp_ctx, + realm, + c->opt_workgroup, + c->opt_host, + ADS_SASL_PLAIN); + if (ads == NULL) { + goto out; + } + + /* we don't actually need a full connect, but it's the easy way to + fill in the KDC's address */ + ads_connect(ads); + + if (!ads->config.realm) { + d_fprintf(stderr, _("Didn't find the kerberos server!\n")); + goto out; + } + + if (argv[1] != NULL) { + new_password = talloc_strdup(tmp_ctx, argv[1]); + } else { + int rc; + + prompt = talloc_asprintf(tmp_ctx, _("Enter new password for %s:"), user); + if (prompt == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + rc = samba_getpass(prompt, pwd, sizeof(pwd), false, true); + if (rc < 0) { + goto out; + } + new_password = talloc_strdup(tmp_ctx, pwd); + memset(pwd, '\0', sizeof(pwd)); + } + + if (new_password == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + status = kerberos_set_password(ads->auth.kdc_server, + auth_principal, + auth_password, + user, + new_password, + ads->auth.time_offset); + memset(new_password, '\0', strlen(new_password)); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Password change failed: %s\n"), + ads_errstr(status)); + goto out; + } + + d_printf(_("Password change for %s completed.\n"), user); + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + char *host_principal = NULL; + char *my_name = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads changetrustpw\n" + " %s\n", + _("Usage:"), + _("Change the machine account's trust password")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + goto out; + } + + net_warn_member_options(); + + net_use_krb_machine_account(c); + + use_in_memory_ccache(); + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + my_name = talloc_asprintf_strlower_m(tmp_ctx, "%s", lp_netbios_name()); + if (my_name == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + host_principal = talloc_asprintf(tmp_ctx, "%s$@%s", my_name, ads->config.realm); + if (host_principal == NULL) { + d_fprintf(stderr, _("Out of memory\n")); + goto out; + } + + d_printf(_("Changing password for principal: %s\n"), host_principal); + + status = ads_change_trust_account_password(ads, host_principal); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("Password change failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Password change for principal %s succeeded.\n"), host_principal); + + if (USE_SYSTEM_KEYTAB) { + d_printf(_("Attempting to update system keytab with new password.\n")); + if (ads_keytab_create_default(ads)) { + d_printf(_("Failed to update system keytab.\n")); + } + } + + ret = 0; +out: + TALLOC_FREE(tmp_ctx); + + return ret; +} + +/* + help for net ads search +*/ +static int net_ads_search_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads search <expression> <attributes...>\n" + "\nPerform a raw LDAP search on a ADS server and dump the results.\n" + "The expression is a standard LDAP search expression, and the\n" + "attributes are a list of LDAP fields to show in the results.\n\n" + "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_search(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *ldap_exp = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_search_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ldap_exp = argv[0]; + attrs = (argv + 1); + + status = ads_do_search_retry(ads, + ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + ldap_exp, + attrs, + &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + + +/* + help for net ads search +*/ +static int net_ads_dn_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads dn <dn> <attributes...>\n" + "\nperform a raw LDAP search on a ADS server and dump the results\n" + "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n" + "to show in the results\n\n" + "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n" + "Note: the DN must be provided properly escaped. See RFC 4514 for details\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_dn(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *dn = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_dn_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + dn = argv[0]; + attrs = (argv + 1); + + status = ads_do_search_all(ads, + dn, + LDAP_SCOPE_BASE, + "(objectclass=*)", + attrs, + &res); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf("Got %d replies\n\n", ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +/* + help for net ads sid search +*/ +static int net_ads_sid_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet ads sid <sid> <attributes...>\n" + "\nperform a raw LDAP search on a ADS server and dump the results\n" + "The SID is in string format, and the attributes are a list of LDAP fields \n" + "to show in the results\n\n" + "Example: net ads sid 'S-1-5-32' distinguishedName\n\n" + )); + net_common_flags_usage(c, argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_sid(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + const char *sid_string = NULL; + const char **attrs = NULL; + LDAPMessage *res = NULL; + struct dom_sid sid = { 0 }; + int ret = -1; + + if (argc < 1 || c->display_usage) { + TALLOC_FREE(tmp_ctx); + return net_ads_sid_usage(c, argc, argv); + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + sid_string = argv[0]; + attrs = (argv + 1); + + if (!string_to_sid(&sid, sid_string)) { + d_fprintf(stderr, _("could not convert sid\n")); + goto out; + } + + status = ads_search_retry_sid(ads, &res, &sid, attrs); + if (!ADS_ERR_OK(status)) { + d_fprintf(stderr, _("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + d_printf(_("Got %d replies\n\n"), ads_count_replies(ads, res)); + + /* dump the results */ + ads_dump(ads, res); + + ret = 0; +out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_flush(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads keytab flush\n" + " %s\n", + _("Usage:"), + _("Delete the whole keytab")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = ads_keytab_flush(ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_add(struct net_context *c, + int argc, + const char **argv, + bool update_ads) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int i; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads keytab add <principal> [principal ...]\n" + " Add principals to local keytab\n" + " principal\tKerberos principal to add to " + "keytab\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + d_printf(_("Processing principals to add...\n")); + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + for (ret = 0, i = 0; i < argc; i++) { + ret |= ads_keytab_add_entry(ads, argv[i], update_ads); + } +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_add_default(struct net_context *c, + int argc, + const char **argv) +{ + return net_ads_keytab_add(c, argc, argv, false); +} + +static int net_ads_keytab_add_update_ads(struct net_context *c, + int argc, + const char **argv) +{ + return net_ads_keytab_add(c, argc, argv, true); +} + +static int net_ads_keytab_delete(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int i; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads keytab delete <principal> [principal ...]\n" + " Remove entries for service principal, " + " from the keytab file only." + " Remove principals from local keytab\n" + " principal\tKerberos principal to remove from " + "keytab\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + d_printf(_("Processing principals to delete...\n")); + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + for (ret = 0, i = 0; i < argc; i++) { + ret |= ads_keytab_delete_entry(ads, argv[i]); + } +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads keytab create\n" + " %s\n", + _("Usage:"), + _("Create new default keytab")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + net_warn_member_options(); + + if (!c->opt_user_specified && c->opt_password == NULL) { + net_use_krb_machine_account(c); + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = ads_keytab_create_default(ads); +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_keytab_list(struct net_context *c, int argc, const char **argv) +{ + const char *keytab = NULL; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads keytab list [keytab]\n" + " List a local keytab\n" + " keytab\tKeytab to list\n")); + return -1; + } + + if (argc >= 1) { + keytab = argv[0]; + } + + return ads_keytab_list(keytab); +} + + +int net_ads_keytab(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + net_ads_keytab_add_default, + NET_TRANSPORT_ADS, + N_("Add a service principal"), + N_("net ads keytab add\n" + " Add a service principal, updates keytab file only.") + }, + { + "delete", + net_ads_keytab_delete, + NET_TRANSPORT_ADS, + N_("Delete a service principal"), + N_("net ads keytab delete\n" + " Remove entries for service principal, from the keytab file only.") + }, + { + "add_update_ads", + net_ads_keytab_add_update_ads, + NET_TRANSPORT_ADS, + N_("Add a service principal"), + N_("net ads keytab add_update_ads\n" + " Add a service principal, depending on the param passed may update ADS computer object in addition to the keytab file.") + }, + { + "create", + net_ads_keytab_create, + NET_TRANSPORT_ADS, + N_("Create a fresh keytab"), + N_("net ads keytab create\n" + " Create a fresh keytab or update existing one.") + }, + { + "flush", + net_ads_keytab_flush, + NET_TRANSPORT_ADS, + N_("Remove all keytab entries"), + N_("net ads keytab flush\n" + " Remove all keytab entries") + }, + { + "list", + net_ads_keytab_list, + NET_TRANSPORT_ADS, + N_("List a keytab"), + N_("net ads keytab list\n" + " List a keytab") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (!USE_KERBEROS_KEYTAB) { + d_printf(_("\nWarning: \"kerberos method\" must be set to a " + "keytab method to use keytab functions.\n")); + } + + return net_run_function(c, argc, argv, "net ads keytab", func); +} + +static int net_ads_kerberos_renew(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos renew\n" + " %s\n", + _("Usage:"), + _("Renew TGT from existing credential cache")); + return -1; + } + + ret = smb_krb5_renew_ticket(NULL, NULL, NULL, NULL); + if (ret) { + d_printf(_("failed to renew kerberos ticket: %s\n"), + error_message(ret)); + } + return ret; +} + +static int net_ads_kerberos_pac_common(struct net_context *c, int argc, const char **argv, + struct PAC_DATA_CTR **pac_data_ctr) +{ + NTSTATUS status; + int ret = -1; + const char *impersonate_princ_s = NULL; + const char *local_service = NULL; + int i; + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "impersonate", strlen("impersonate"))) { + impersonate_princ_s = get_string_param(argv[i]); + if (impersonate_princ_s == NULL) { + return -1; + } + } + if (strnequal(argv[i], "local_service", strlen("local_service"))) { + local_service = get_string_param(argv[i]); + if (local_service == NULL) { + return -1; + } + } + } + + if (local_service == NULL) { + local_service = talloc_asprintf(c, "%s$@%s", + lp_netbios_name(), lp_realm()); + if (local_service == NULL) { + goto out; + } + } + + c->opt_password = net_prompt_pass(c, c->opt_user_name); + + status = kerberos_return_pac(c, + c->opt_user_name, + c->opt_password, + 0, + NULL, + NULL, + NULL, + true, + true, + 2592000, /* one month */ + impersonate_princ_s, + local_service, + NULL, + NULL, + pac_data_ctr); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed to query kerberos PAC: %s\n"), + nt_errstr(status)); + goto out; + } + + ret = 0; + out: + return ret; +} + +static int net_ads_kerberos_pac_dump(struct net_context *c, int argc, const char **argv) +{ + struct PAC_DATA_CTR *pac_data_ctr = NULL; + int i, num_buffers; + int ret = -1; + enum PAC_TYPE type = 0; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos pac dump [impersonate=string] [local_service=string] [pac_buffer_type=int]\n" + " %s\n", + _("Usage:"), + _("Dump the Kerberos PAC")); + return -1; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "pac_buffer_type", strlen("pac_buffer_type"))) { + type = get_int_param(argv[i]); + } + } + + ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr); + if (ret) { + return ret; + } + + if (type == 0) { + + char *s = NULL; + + s = NDR_PRINT_STRUCT_STRING(c, PAC_DATA, + pac_data_ctr->pac_data); + if (s != NULL) { + d_printf(_("The Pac: %s\n"), s); + talloc_free(s); + } + + return 0; + } + + num_buffers = pac_data_ctr->pac_data->num_buffers; + + for (i=0; i<num_buffers; i++) { + + char *s = NULL; + + if (pac_data_ctr->pac_data->buffers[i].type != type) { + continue; + } + + s = NDR_PRINT_UNION_STRING(c, PAC_INFO, type, + pac_data_ctr->pac_data->buffers[i].info); + if (s != NULL) { + d_printf(_("The Pac: %s\n"), s); + talloc_free(s); + } + break; + } + + return 0; +} + +static int net_ads_kerberos_pac_save(struct net_context *c, int argc, const char **argv) +{ + struct PAC_DATA_CTR *pac_data_ctr = NULL; + char *filename = NULL; + int ret = -1; + int i; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos pac save [impersonate=string] [local_service=string] [filename=string]\n" + " %s\n", + _("Usage:"), + _("Save the Kerberos PAC")); + return -1; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "filename", strlen("filename"))) { + filename = get_string_param(argv[i]); + if (filename == NULL) { + return -1; + } + } + } + + ret = net_ads_kerberos_pac_common(c, argc, argv, &pac_data_ctr); + if (ret) { + return ret; + } + + if (filename == NULL) { + d_printf(_("please define \"filename=<filename>\" to save the PAC\n")); + return -1; + } + + /* save the raw format */ + if (!file_save(filename, pac_data_ctr->pac_blob.data, pac_data_ctr->pac_blob.length)) { + d_printf(_("failed to save PAC in %s\n"), filename); + return -1; + } + + return 0; +} + +static int net_ads_kerberos_pac(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "dump", + net_ads_kerberos_pac_dump, + NET_TRANSPORT_ADS, + N_("Dump Kerberos PAC"), + N_("net ads kerberos pac dump\n" + " Dump a Kerberos PAC to stdout") + }, + { + "save", + net_ads_kerberos_pac_save, + NET_TRANSPORT_ADS, + N_("Save Kerberos PAC"), + N_("net ads kerberos pac save\n" + " Save a Kerberos PAC in a file") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads kerberos pac", func); +} + +static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + NTSTATUS status; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads kerberos kinit\n" + " %s\n", + _("Usage:"), + _("Get Ticket Granting Ticket (TGT) for the user")); + return -1; + } + + c->opt_password = net_prompt_pass(c, c->opt_user_name); + + ret = kerberos_kinit_password_ext(c->opt_user_name, + c->opt_password, + 0, + NULL, + NULL, + NULL, + true, + true, + 2592000, /* one month */ + NULL, + NULL, + NULL, + &status); + if (ret) { + d_printf(_("failed to kinit password: %s\n"), + nt_errstr(status)); + } + return ret; +} + +int net_ads_kerberos(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "kinit", + net_ads_kerberos_kinit, + NET_TRANSPORT_ADS, + N_("Retrieve Ticket Granting Ticket (TGT)"), + N_("net ads kerberos kinit\n" + " Receive Ticket Granting Ticket (TGT)") + }, + { + "renew", + net_ads_kerberos_renew, + NET_TRANSPORT_ADS, + N_("Renew Ticket Granting Ticket from credential cache"), + N_("net ads kerberos renew\n" + " Renew Ticket Granting Ticket (TGT) from " + "credential cache") + }, + { + "pac", + net_ads_kerberos_pac, + NET_TRANSPORT_ADS, + N_("Dump Kerberos PAC"), + N_("net ads kerberos pac\n" + " Dump Kerberos PAC") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads kerberos", func); +} + +static int net_ads_setspn_list(struct net_context *c, + int argc, + const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn list <machinename>\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc) { + ok = ads_setspn_list(ads, argv[0]); + } else { + ok = ads_setspn_list(ads, lp_netbios_name()); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_setspn_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage || argc < 1) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn add <machinename> SPN\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + ok = ads_setspn_add(ads, argv[0], argv[1]); + } else { + ok = ads_setspn_add(ads, lp_netbios_name(), argv[0]); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_setspn_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + bool ok = false; + int ret = -1; + + if (c->display_usage || argc < 1) { + d_printf("%s\n%s", + _("Usage:"), + _("net ads setspn delete <machinename> SPN\n")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, true, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (argc > 1) { + ok = ads_setspn_delete(ads, argv[0], argv[1]); + } else { + ok = ads_setspn_delete(ads, lp_netbios_name(), argv[0]); + } + + ret = ok ? 0 : -1; +out: + TALLOC_FREE(tmp_ctx); + return ret; +} + +int net_ads_setspn(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_ads_setspn_list, + NET_TRANSPORT_ADS, + N_("List Service Principal Names (SPN)"), + N_("net ads setspn list machine\n" + " List Service Principal Names (SPN)") + }, + { + "add", + net_ads_setspn_add, + NET_TRANSPORT_ADS, + N_("Add Service Principal Names (SPN)"), + N_("net ads setspn add machine spn\n" + " Add Service Principal Names (SPN)") + }, + { + "delete", + net_ads_setspn_delete, + NET_TRANSPORT_ADS, + N_("Delete Service Principal Names (SPN)"), + N_("net ads setspn delete machine spn\n" + " Delete Service Principal Names (SPN)") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads setspn", func); +} + +static int net_ads_enctype_lookup_account(struct net_context *c, + ADS_STRUCT *ads, + const char *account, + LDAPMessage **res, + const char **enctype_str) +{ + const char *filter; + const char *attrs[] = { + "msDS-SupportedEncryptionTypes", + NULL + }; + int count; + int ret = -1; + ADS_STATUS status; + + filter = talloc_asprintf(c, "(&(objectclass=user)(sAMAccountName=%s))", + account); + if (filter == NULL) { + goto done; + } + + status = ads_search(ads, res, filter, attrs); + if (!ADS_ERR_OK(status)) { + d_printf(_("no account found with filter: %s\n"), filter); + goto done; + } + + count = ads_count_replies(ads, *res); + switch (count) { + case 1: + break; + case 0: + d_printf(_("no account found with filter: %s\n"), filter); + goto done; + default: + d_printf(_("multiple accounts found with filter: %s\n"), filter); + goto done; + } + + if (enctype_str) { + *enctype_str = ads_pull_string(ads, c, *res, + "msDS-SupportedEncryptionTypes"); + if (*enctype_str == NULL) { + d_printf(_("no msDS-SupportedEncryptionTypes attribute found\n")); + goto done; + } + } + + ret = 0; + done: + return ret; +} + +static void net_ads_enctype_dump_enctypes(const char *username, + const char *enctype_str) +{ + int enctypes = atoi(enctype_str); + + d_printf(_("'%s' uses \"msDS-SupportedEncryptionTypes\": %d (0x%08x)\n"), + username, enctypes, enctypes); + + printf("[%s] 0x%08x DES-CBC-CRC\n", + enctypes & ENC_CRC32 ? "X" : " ", + ENC_CRC32); + printf("[%s] 0x%08x DES-CBC-MD5\n", + enctypes & ENC_RSA_MD5 ? "X" : " ", + ENC_RSA_MD5); + printf("[%s] 0x%08x RC4-HMAC\n", + enctypes & ENC_RC4_HMAC_MD5 ? "X" : " ", + ENC_RC4_HMAC_MD5); + printf("[%s] 0x%08x AES128-CTS-HMAC-SHA1-96\n", + enctypes & ENC_HMAC_SHA1_96_AES128 ? "X" : " ", + ENC_HMAC_SHA1_96_AES128); + printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96\n", + enctypes & ENC_HMAC_SHA1_96_AES256 ? "X" : " ", + ENC_HMAC_SHA1_96_AES256); + printf("[%s] 0x%08x AES256-CTS-HMAC-SHA1-96-SK\n", + enctypes & ENC_HMAC_SHA1_96_AES256_SK ? "X" : " ", + ENC_HMAC_SHA1_96_AES256_SK); + printf("[%s] 0x%08x RESOURCE-SID-COMPRESSION-DISABLED\n", + enctypes & KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED ? "X" : " ", + KERB_ENCTYPE_RESOURCE_SID_COMPRESSION_DISABLED); +} + +static int net_ads_enctypes_list(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *str = NULL; + int ret = -1; + + if (c->display_usage || (argc < 1)) { + d_printf( "%s\n" + "net ads enctypes list\n" + " %s\n", + _("Usage:"), + _("List supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str); + if (ret) { + goto out; + } + + net_ads_enctype_dump_enctypes(argv[0], str); + + ret = 0; + out: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes_set(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + int ret = -1; + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *etype_list_str = NULL; + const char *dn = NULL; + ADS_MODLIST mods = NULL; + uint32_t etype_list; + const char *str = NULL; + + if (c->display_usage || argc < 1) { + d_printf( "%s\n" + "net ads enctypes set <sAMAccountName> [enctypes]\n" + " %s\n", + _("Usage:"), + _("Set supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL); + if (ret) { + goto done; + } + + dn = ads_get_dn(ads, tmp_ctx, res); + if (dn == NULL) { + goto done; + } + + etype_list = 0; + etype_list |= ENC_RC4_HMAC_MD5; + etype_list |= ENC_HMAC_SHA1_96_AES128; + etype_list |= ENC_HMAC_SHA1_96_AES256; + + if (argv[1] != NULL) { + sscanf(argv[1], "%i", &etype_list); + } + + etype_list_str = talloc_asprintf(tmp_ctx, "%d", etype_list); + if (!etype_list_str) { + goto done; + } + + mods = ads_init_mods(tmp_ctx); + if (!mods) { + goto done; + } + + status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes", + etype_list_str); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_gen_mod(ads, dn, mods); + if (!ADS_ERR_OK(status)) { + d_printf(_("failed to add msDS-SupportedEncryptionTypes: %s\n"), + ads_errstr(status)); + goto done; + } + + ads_msgfree(ads, res); + res = NULL; + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, &str); + if (ret) { + goto done; + } + + net_ads_enctype_dump_enctypes(argv[0], str); + + ret = 0; + done: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes_delete(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + int ret = -1; + ADS_STATUS status; + ADS_STRUCT *ads = NULL; + LDAPMessage *res = NULL; + const char *dn = NULL; + ADS_MODLIST mods = NULL; + + if (c->display_usage || argc < 1) { + d_printf( "%s\n" + "net ads enctypes delete <sAMAccountName>\n" + " %s\n", + _("Usage:"), + _("Delete supported enctypes")); + TALLOC_FREE(tmp_ctx); + return -1; + } + + status = ads_startup(c, false, tmp_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto done; + } + + ret = net_ads_enctype_lookup_account(c, ads, argv[0], &res, NULL); + if (ret) { + goto done; + } + + dn = ads_get_dn(ads, tmp_ctx, res); + if (dn == NULL) { + goto done; + } + + mods = ads_init_mods(tmp_ctx); + if (!mods) { + goto done; + } + + status = ads_mod_str(tmp_ctx, &mods, "msDS-SupportedEncryptionTypes", NULL); + if (!ADS_ERR_OK(status)) { + goto done; + } + + status = ads_gen_mod(ads, dn, mods); + if (!ADS_ERR_OK(status)) { + d_printf(_("failed to remove msDS-SupportedEncryptionTypes: %s\n"), + ads_errstr(status)); + goto done; + } + + ret = 0; + + done: + ads_msgfree(ads, res); + TALLOC_FREE(tmp_ctx); + return ret; +} + +static int net_ads_enctypes(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_ads_enctypes_list, + NET_TRANSPORT_ADS, + N_("List the supported encryption types"), + N_("net ads enctypes list\n" + " List the supported encryption types") + }, + { + "set", + net_ads_enctypes_set, + NET_TRANSPORT_ADS, + N_("Set the supported encryption types"), + N_("net ads enctypes set\n" + " Set the supported encryption types") + }, + { + "delete", + net_ads_enctypes_delete, + NET_TRANSPORT_ADS, + N_("Delete the supported encryption types"), + N_("net ads enctypes delete\n" + " Delete the supported encryption types") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads enctypes", func); +} + + +int net_ads(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "info", + net_ads_info, + NET_TRANSPORT_ADS, + N_("Display details on remote ADS server"), + N_("net ads info\n" + " Display details on remote ADS server") + }, + { + "join", + net_ads_join, + NET_TRANSPORT_ADS, + N_("Join the local machine to ADS realm"), + N_("net ads join\n" + " Join the local machine to ADS realm") + }, + { + "testjoin", + net_ads_testjoin, + NET_TRANSPORT_ADS, + N_("Validate machine account"), + N_("net ads testjoin\n" + " Validate machine account") + }, + { + "leave", + net_ads_leave, + NET_TRANSPORT_ADS, + N_("Remove the local machine from ADS"), + N_("net ads leave\n" + " Remove the local machine from ADS") + }, + { + "status", + net_ads_status, + NET_TRANSPORT_ADS, + N_("Display machine account details"), + N_("net ads status\n" + " Display machine account details") + }, + { + "user", + net_ads_user, + NET_TRANSPORT_ADS, + N_("List/modify users"), + N_("net ads user\n" + " List/modify users") + }, + { + "group", + net_ads_group, + NET_TRANSPORT_ADS, + N_("List/modify groups"), + N_("net ads group\n" + " List/modify groups") + }, + { + "dns", + net_ads_dns, + NET_TRANSPORT_ADS, + N_("Issue dynamic DNS update"), + N_("net ads dns\n" + " Issue dynamic DNS update") + }, + { + "password", + net_ads_password, + NET_TRANSPORT_ADS, + N_("Change user passwords"), + N_("net ads password\n" + " Change user passwords") + }, + { + "changetrustpw", + net_ads_changetrustpw, + NET_TRANSPORT_ADS, + N_("Change trust account password"), + N_("net ads changetrustpw\n" + " Change trust account password") + }, + { + "printer", + net_ads_printer, + NET_TRANSPORT_ADS, + N_("List/modify printer entries"), + N_("net ads printer\n" + " List/modify printer entries") + }, + { + "search", + net_ads_search, + NET_TRANSPORT_ADS, + N_("Issue LDAP search using filter"), + N_("net ads search\n" + " Issue LDAP search using filter") + }, + { + "dn", + net_ads_dn, + NET_TRANSPORT_ADS, + N_("Issue LDAP search by DN"), + N_("net ads dn\n" + " Issue LDAP search by DN") + }, + { + "sid", + net_ads_sid, + NET_TRANSPORT_ADS, + N_("Issue LDAP search by SID"), + N_("net ads sid\n" + " Issue LDAP search by SID") + }, + { + "workgroup", + net_ads_workgroup, + NET_TRANSPORT_ADS, + N_("Display workgroup name"), + N_("net ads workgroup\n" + " Display the workgroup name") + }, + { + "lookup", + net_ads_lookup, + NET_TRANSPORT_ADS, + N_("Perform CLDAP query on DC"), + N_("net ads lookup\n" + " Find the ADS DC using CLDAP lookups") + }, + { + "keytab", + net_ads_keytab, + NET_TRANSPORT_ADS, + N_("Manage local keytab file"), + N_("net ads keytab\n" + " Manage local keytab file") + }, + { + "setspn", + net_ads_setspn, + NET_TRANSPORT_ADS, + N_("Manage Service Principal Names (SPN)s"), + N_("net ads spnset\n" + " Manage Service Principal Names (SPN)s") + }, + { + "gpo", + net_ads_gpo, + NET_TRANSPORT_ADS, + N_("Manage group policy objects"), + N_("net ads gpo\n" + " Manage group policy objects") + }, + { + "kerberos", + net_ads_kerberos, + NET_TRANSPORT_ADS, + N_("Manage kerberos keytab"), + N_("net ads kerberos\n" + " Manage kerberos keytab") + }, + { + "enctypes", + net_ads_enctypes, + NET_TRANSPORT_ADS, + N_("List/modify supported encryption types"), + N_("net ads enctypes\n" + " List/modify enctypes") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads", func); +} + +#else + +static int net_ads_noads(void) +{ + d_fprintf(stderr, _("ADS support not compiled in\n")); + return -1; +} + +int net_ads_keytab(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_kerberos(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_setspn(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_join(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_user(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_group(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_gpo(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +/* this one shouldn't display a message */ +int net_ads_check(struct net_context *c) +{ + return -1; +} + +int net_ads_check_our_domain(struct net_context *c) +{ + return -1; +} + +int net_ads(struct net_context *c, int argc, const char **argv) +{ + return net_ads_noads(); +} + +#endif /* HAVE_ADS */ diff --git a/source3/utils/net_ads_gpo.c b/source3/utils/net_ads_gpo.c new file mode 100644 index 0000000..12d8e22 --- /dev/null +++ b/source3/utils/net_ads_gpo.c @@ -0,0 +1,428 @@ +/* + Samba Unix/Linux SMB client library + net ads commands for Group Policy + Copyright (C) 2005-2008 Guenther Deschner (gd@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "ads.h" +#include "../libgpo/gpo.h" +#include "libgpo/gpo_proto.h" +#include "../libds/common/flags.h" + +#ifdef HAVE_ADS + +static int net_ads_gpo_list_all(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + LDAPMessage *res = NULL; + int num_reply = 0; + LDAPMessage *msg = NULL; + struct GROUP_POLICY_OBJECT gpo; + TALLOC_CTX *mem_ctx; + char *dn; + const char *attrs[] = { + "versionNumber", + "flags", + "gPCFileSysPath", + "displayName", + "name", + "gPCMachineExtensionNames", + "gPCUserExtensionNames", + "ntSecurityDescriptor", + NULL + }; + + if (c->display_usage) { + d_printf( "%s\n" + "net ads gpo listall\n" + " %s\n", + _("Usage:"), + _("List all GPOs on the DC")); + return 0; + } + + mem_ctx = talloc_init("net_ads_gpo_list_all"); + if (mem_ctx == NULL) { + return -1; + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_do_search_all_sd_flags(ads, ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + "(objectclass=groupPolicyContainer)", + attrs, + SECINFO_DACL, + &res); + + if (!ADS_ERR_OK(status)) { + d_printf(_("search failed: %s\n"), ads_errstr(status)); + goto out; + } + + num_reply = ads_count_replies(ads, res); + + d_printf(_("Got %d replies\n\n"), num_reply); + + /* dump the results */ + for (msg = ads_first_entry(ads, res); + msg; + msg = ads_next_entry(ads, msg)) { + + if ((dn = ads_get_dn(ads, mem_ctx, msg)) == NULL) { + goto out; + } + + status = ads_parse_gpo(ads, mem_ctx, msg, dn, &gpo); + + if (!ADS_ERR_OK(status)) { + d_printf(_("ads_parse_gpo failed: %s\n"), + ads_errstr(status)); + goto out; + } + + dump_gpo(&gpo, 0); + } + +out: + ads_msgfree(ads, res); + + TALLOC_FREE(mem_ctx); + + return 0; +} + +static int net_ads_gpo_list(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS status; + LDAPMessage *res = NULL; + TALLOC_CTX *mem_ctx; + const char *dn = NULL; + uint32_t uac = 0; + uint32_t flags = 0; + struct GROUP_POLICY_OBJECT *gpo_list; + struct security_token *token = NULL; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s\n%s", + _("Usage:"), + _("net ads gpo list <username|machinename>"), + _(" Lists all GPOs for machine/user\n" + " username\tUser to list GPOs for\n" + " machinename\tMachine to list GPOs for\n")); + return -1; + } + + mem_ctx = talloc_init("net_ads_gpo_list"); + if (mem_ctx == NULL) { + goto out; + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_find_samaccount(ads, mem_ctx, argv[0], &uac, &dn); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (uac & UF_WORKSTATION_TRUST_ACCOUNT) { + flags |= GPO_LIST_FLAG_MACHINE; + } + + d_printf(_("%s: '%s' has dn: '%s'\n"), + (uac & UF_WORKSTATION_TRUST_ACCOUNT) ? _("machine") : _("user"), + argv[0], dn); + + if (uac & UF_WORKSTATION_TRUST_ACCOUNT) { + status = gp_get_machine_token(ads, mem_ctx, dn, &token); + } else { + status = ads_get_sid_token(ads, mem_ctx, dn, &token); + } + + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_get_gpo_list(ads, mem_ctx, dn, flags, token, &gpo_list); + if (!ADS_ERR_OK(status)) { + goto out; + } + + dump_gpo_list(gpo_list, 0); + +out: + ads_msgfree(ads, res); + + talloc_destroy(mem_ctx); + + return 0; +} + +static int net_ads_gpo_link_get(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + TALLOC_CTX *mem_ctx; + struct GP_LINK gp_link; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s\n%s", + _("Usage:"), + _("net ads gpo linkget <container>"), + _(" Lists gPLink of a container\n" + " container\tContainer to get link for\n")); + return -1; + } + + mem_ctx = talloc_init("add_gpo_link"); + if (mem_ctx == NULL) { + return -1; + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_get_gpo_link(ads, mem_ctx, argv[0], &gp_link); + if (!ADS_ERR_OK(status)) { + d_printf(_("get link for %s failed: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + dump_gplink(&gp_link); + +out: + talloc_destroy(mem_ctx); + + return 0; +} + +static int net_ads_gpo_link_add(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + uint32_t gpo_opt = 0; + TALLOC_CTX *mem_ctx; + + if (argc < 2 || c->display_usage) { + d_printf("%s\n%s\n%s", + _("Usage:"), + _("net ads gpo linkadd <linkdn> <gpodn> [options]"), + _(" Link a container to a GPO\n" + " linkdn\tContainer to link to a GPO\n" + " gpodn\tGPO to link container to\n")); + d_printf(_("note: DNs must be provided properly escaped.\n" + "See RFC 4514 for details\n")); + return -1; + } + + mem_ctx = talloc_init("add_gpo_link"); + if (mem_ctx == NULL) { + return -1; + } + + if (argc == 3) { + gpo_opt = atoi(argv[2]); + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_add_gpo_link(ads, mem_ctx, argv[0], argv[1], gpo_opt); + if (!ADS_ERR_OK(status)) { + d_printf(_("link add failed: %s\n"), ads_errstr(status)); + goto out; + } + +out: + talloc_destroy(mem_ctx); + + return 0; +} + +#if 0 /* broken */ + +static int net_ads_gpo_link_delete(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + TALLOC_CTX *mem_ctx; + + if (argc < 2 || c->display_usage) { + d_printf("Usage:\n" + "net ads gpo linkdelete <linkdn> <gpodn>\n" + " Delete a GPO link\n" + " <linkdn>\tContainer to delete GPO from\n" + " <gpodn>\tGPO to delete from container\n"); + return -1; + } + + mem_ctx = talloc_init("delete_gpo_link"); + if (mem_ctx == NULL) { + return -1; + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + status = ads_delete_gpo_link(ads, mem_ctx, argv[0], argv[1]); + if (!ADS_ERR_OK(status)) { + d_printf("delete link failed: %s\n", ads_errstr(status)); + goto out; + } + +out: + talloc_destroy(mem_ctx); + + return 0; +} + +#endif + +/* +Arguments: +- struct net_context *: Pointer to net_context* +- argc: Number of command line arguments passed to 'net ads gpo getgpo' command +- **argv: Command line argument string passed to 'net ads gpo getgpo' command + +This function performs following operations: +1. Create talloc context using talloc_init +2. Perform ads_startup() +3. Call ads_get_gpo() to retrieve gpo details inside 'struct GROUP_POLICY_OBJECT' +4. Call dumps_gpo() to dump GPO on stdout +*/ +static int net_ads_gpo_get_gpo(struct net_context *c, int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + TALLOC_CTX *mem_ctx; + struct GROUP_POLICY_OBJECT gpo; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s\n%s", + _("Usage:"), + _("net ads gpo getgpo <gpo>"), + _(" List specified GPO\n" + " gpo\t\tGPO to list\n")); + return -1; + } + + mem_ctx = talloc_init("ads_gpo_get_gpo"); + if (mem_ctx == NULL) { + return -1; + } + + status = ads_startup(c, false, mem_ctx, &ads); + if (!ADS_ERR_OK(status)) { + goto out; + } + + if (strnequal(argv[0], "CN={", strlen("CN={"))) { + status = ads_get_gpo(ads, mem_ctx, argv[0], NULL, NULL, &gpo); + } else { + status = ads_get_gpo(ads, mem_ctx, NULL, argv[0], NULL, &gpo); + } + + if (!ADS_ERR_OK(status)) { + d_printf(_("get gpo for [%s] failed: %s\n"), argv[0], + ads_errstr(status)); + goto out; + } + + dump_gpo(&gpo, 0); + +out: + talloc_destroy(mem_ctx); + + return 0; +} + +int net_ads_gpo(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "getgpo", + net_ads_gpo_get_gpo, + NET_TRANSPORT_ADS, + N_("List specified GPO"), + N_("net ads gpo getgpo\n" + " List specified GPO") + }, + { + "linkadd", + net_ads_gpo_link_add, + NET_TRANSPORT_ADS, + N_("Link a container to a GPO"), + N_("net ads gpo linkadd\n" + " Link a container to a GPO") + }, +#if 0 + { + "linkdelete", + net_ads_gpo_link_delete, + NET_TRANSPORT_ADS, + "Delete GPO link from a container", + "net ads gpo linkdelete\n" + " Delete GPO link from a container" + }, +#endif + { + "linkget", + net_ads_gpo_link_get, + NET_TRANSPORT_ADS, + N_("Lists gPLink of container"), + N_("net ads gpo linkget\n" + " Lists gPLink of container") + }, + { + "list", + net_ads_gpo_list, + NET_TRANSPORT_ADS, + N_("Lists all GPOs for machine/user"), + N_("net ads gpo list\n" + " Lists all GPOs for machine/user") + }, + { + "listall", + net_ads_gpo_list_all, + NET_TRANSPORT_ADS, + N_("Lists all GPOs on a DC"), + N_("net ads gpo listall\n" + " Lists all GPOs on a DC") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net ads gpo", func); +} + +#endif /* HAVE_ADS */ diff --git a/source3/utils/net_ads_join_dns.c b/source3/utils/net_ads_join_dns.c new file mode 100644 index 0000000..3437f96 --- /dev/null +++ b/source3/utils/net_ads_join_dns.c @@ -0,0 +1,342 @@ +/* + Samba Unix/Linux SMB client library + net ads dns internal functions + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2006 Gerald (Jerry) Carter (jerry@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "../lib/addns/dnsquery.h" +#include "secrets.h" +#include "krb5_env.h" +#include "utils/net_dns.h" +#include "lib/util/string_wrappers.h" + +#ifdef HAVE_ADS + +/******************************************************************* + Send a DNS update request +*******************************************************************/ + +#if defined(HAVE_KRB5) +#include "../lib/addns/dns.h" + +void use_in_memory_ccache(void) { + /* Use in-memory credentials cache so we do not interfere with + * existing credentials */ + setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1); +} + +static NTSTATUS net_update_dns_internal(struct net_context *c, + TALLOC_CTX *ctx, ADS_STRUCT *ads, + const char *machine_name, + const struct sockaddr_storage *addrs, + int num_addrs, bool remove_host) +{ + struct dns_rr_ns *nameservers = NULL; + size_t ns_count = 0, i; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + DNS_ERROR dns_err; + fstring dns_server; + const char *dnsdomain = NULL; + char *root_domain = NULL; + uint32_t ttl = 3600; + + if (c->opt_dns_ttl > 0) { + ttl = MIN(c->opt_dns_ttl, UINT32_MAX); + } + + if ( (dnsdomain = strchr_m( machine_name, '.')) == NULL ) { + d_printf(_("No DNS domain configured for %s. " + "Unable to perform DNS Update.\n"), machine_name); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + dnsdomain++; + + status = ads_dns_lookup_ns(ctx, + dnsdomain, + &nameservers, + &ns_count); + if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) { + /* Child domains often do not have NS records. Look + for the NS record for the forest root domain + (rootDomainNamingContext in therootDSE) */ + + const char *rootname_attrs[] = { "rootDomainNamingContext", NULL }; + LDAPMessage *msg = NULL; + char *root_dn; + ADS_STATUS ads_status; + + if ( !ads->ldap.ld ) { + ads_status = ads_connect( ads ); + if ( !ADS_ERR_OK(ads_status) ) { + DEBUG(0,("net_update_dns_internal: Failed to connect to our DC!\n")); + goto done; + } + } + + ads_status = ads_do_search(ads, "", LDAP_SCOPE_BASE, + "(objectclass=*)", rootname_attrs, &msg); + if (!ADS_ERR_OK(ads_status)) { + goto done; + } + + root_dn = ads_pull_string(ads, ctx, msg, "rootDomainNamingContext"); + if ( !root_dn ) { + ads_msgfree( ads, msg ); + goto done; + } + + root_domain = ads_build_domain( root_dn ); + + /* cleanup */ + ads_msgfree( ads, msg ); + + /* try again for NS servers */ + + status = ads_dns_lookup_ns(ctx, + root_domain, + &nameservers, + &ns_count); + + if ( !NT_STATUS_IS_OK(status) || (ns_count == 0)) { + DEBUG(3,("net_update_dns_internal: Failed to find name server for the %s " + "realm\n", ads->config.realm)); + if (ns_count == 0) { + status = NT_STATUS_UNSUCCESSFUL; + } + goto done; + } + + dnsdomain = root_domain; + + } + + for (i=0; i < ns_count; i++) { + + uint32_t flags = DNS_UPDATE_SIGNED | + DNS_UPDATE_UNSIGNED | + DNS_UPDATE_UNSIGNED_SUFFICIENT | + DNS_UPDATE_PROBE | + DNS_UPDATE_PROBE_SUFFICIENT; + + if (c->opt_force) { + flags &= ~DNS_UPDATE_PROBE_SUFFICIENT; + flags &= ~DNS_UPDATE_UNSIGNED_SUFFICIENT; + } + + /* + * Do not return after PROBE completion if this function + * is called for DNS removal. + */ + if (remove_host) { + flags &= ~DNS_UPDATE_PROBE_SUFFICIENT; + } + + status = NT_STATUS_UNSUCCESSFUL; + + /* Now perform the dns update - we'll try non-secure and if we fail, + we'll follow it up with a secure update */ + + fstrcpy( dns_server, nameservers[i].hostname ); + + dns_err = DoDNSUpdate(dns_server, + dnsdomain, + machine_name, + addrs, + num_addrs, + flags, + ttl, + remove_host); + if (ERR_DNS_IS_OK(dns_err)) { + status = NT_STATUS_OK; + goto done; + } + + if (ERR_DNS_EQUAL(dns_err, ERROR_DNS_INVALID_NAME_SERVER) || + ERR_DNS_EQUAL(dns_err, ERROR_DNS_CONNECTION_FAILED) || + ERR_DNS_EQUAL(dns_err, ERROR_DNS_SOCKET_ERROR)) { + DEBUG(1,("retrying DNS update with next nameserver after receiving %s\n", + dns_errstr(dns_err))); + continue; + } + + d_printf(_("DNS Update for %s failed: %s\n"), + machine_name, dns_errstr(dns_err)); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + +done: + + SAFE_FREE( root_domain ); + + return status; +} + +NTSTATUS net_update_dns_ext(struct net_context *c, + TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, + const char *hostname, + struct sockaddr_storage *iplist, + int num_addrs, bool remove_host) +{ + struct sockaddr_storage *iplist_alloc = NULL; + fstring machine_name; + NTSTATUS status; + + if (hostname) { + fstrcpy(machine_name, hostname); + } else { + name_to_fqdn( machine_name, lp_netbios_name() ); + } + if (!strlower_m( machine_name )) { + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * If remove_host is true, then remove all IP addresses associated with + * this hostname from the AD server. + */ + if (!remove_host && (num_addrs == 0 || iplist == NULL)) { + /* + * Get our ip address + * (not the 127.0.0.x address but a real ip address) + */ + num_addrs = get_my_ip_address(&iplist_alloc); + if ( num_addrs <= 0 ) { + DEBUG(4, ("net_update_dns_ext: Failed to find my " + "non-loopback IP addresses!\n")); + return NT_STATUS_INVALID_PARAMETER; + } + iplist = iplist_alloc; + } + + status = net_update_dns_internal(c, mem_ctx, ads, machine_name, + iplist, num_addrs, remove_host); + + SAFE_FREE(iplist_alloc); + return status; +} + +static NTSTATUS net_update_dns(struct net_context *c, TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, const char *hostname) +{ + NTSTATUS status; + + status = net_update_dns_ext(c, mem_ctx, ads, hostname, NULL, 0, false); + return status; +} +#endif + +void net_ads_join_dns_updates(struct net_context *c, TALLOC_CTX *ctx, struct libnet_JoinCtx *r) +{ +#if defined(HAVE_KRB5) + ADS_STRUCT *ads_dns = NULL; + int ret; + NTSTATUS status; + char *machine_password = NULL; + + /* + * In a clustered environment, don't do dynamic dns updates: + * Registering the set of ip addresses that are assigned to + * the interfaces of the node that performs the join does usually + * not have the desired effect, since the local interfaces do not + * carry the complete set of the cluster's public IP addresses. + * And it can also contain internal addresses that should not + * be visible to the outside at all. + * In order to do dns updates in a clustererd setup, use + * net ads dns register. + */ + if (lp_clustering()) { + d_fprintf(stderr, _("Not doing automatic DNS update in a " + "clustered setup.\n")); + return; + } + + if (!r->out.domain_is_ad) { + return; + } + + /* + * We enter this block with user creds. + * kinit with the machine password to do dns update. + */ + + ads_dns = ads_init(ctx, + lp_realm(), + NULL, + r->in.dc_name, + ADS_SASL_PLAIN); + if (ads_dns == NULL) { + d_fprintf(stderr, _("DNS update failed: out of memory!\n")); + goto done; + } + + use_in_memory_ccache(); + + ads_dns->auth.user_name = talloc_asprintf(ads_dns, + "%s$", + lp_netbios_name()); + if (ads_dns->auth.user_name == NULL) { + d_fprintf(stderr, _("DNS update failed: out of memory\n")); + goto done; + } + + machine_password = secrets_fetch_machine_password( + r->out.netbios_domain_name, NULL, NULL); + if (machine_password != NULL) { + ads_dns->auth.password = talloc_strdup(ads_dns, + machine_password); + SAFE_FREE(machine_password); + if (ads_dns->auth.password == NULL) { + d_fprintf(stderr, + _("DNS update failed: out of memory\n")); + goto done; + } + } + + ads_dns->auth.realm = talloc_asprintf_strupper_m(ads_dns, "%s", r->out.dns_domain_name); + if (ads_dns->auth.realm == NULL) { + d_fprintf(stderr, _("talloc_asprintf_strupper_m %s failed\n"), + ads_dns->auth.realm); + goto done; + } + + ret = ads_kinit_password(ads_dns); + if (ret != 0) { + d_fprintf(stderr, + _("DNS update failed: kinit failed: %s\n"), + error_message(ret)); + goto done; + } + + status = net_update_dns(c, ctx, ads_dns, NULL); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf( stderr, _("DNS update failed: %s\n"), + nt_errstr(status)); + } + +done: + TALLOC_FREE(ads_dns); +#endif + + return; +} + +#endif /* HAVE_ADS */ diff --git a/source3/utils/net_afs.c b/source3/utils/net_afs.c new file mode 100644 index 0000000..36d4310 --- /dev/null +++ b/source3/utils/net_afs.c @@ -0,0 +1,127 @@ +/* + Samba Unix/Linux SMB client library + net afs commands + Copyright (C) 2003 Volker Lendecke (vl@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "utils/net_afs.h" +#include "secrets.h" +#include "system/filesys.h" +#include "lib/afs/afs_funcs.h" +#include "lib/afs/afs_settoken.h" + +#ifdef WITH_FAKE_KASERVER + +int net_afs_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_(" net afs key filename\n" + "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n")); + d_printf(_(" net afs impersonate <user> <cell>\n" + "\tCreates a token for user@cell\n\n")); + return -1; +} + +int net_afs_key(struct net_context *c, int argc, const char **argv) +{ + int fd; + struct afs_keyfile keyfile; + + if (argc != 2) { + d_printf("%s net afs key <keyfile> cell\n", _("Usage:")); + return -1; + } + + if (!secrets_init()) { + d_fprintf(stderr, _("Could not open secrets.tdb\n")); + return -1; + } + + if ((fd = open(argv[0], O_RDONLY, 0)) < 0) { + d_fprintf(stderr, _("Could not open %s\n"), argv[0]); + return -1; + } + + if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) { + d_fprintf(stderr, _("Could not read keyfile\n")); + close(fd); + return -1; + } + close(fd); + + if (!secrets_store_afs_keyfile(argv[1], &keyfile)) { + d_fprintf(stderr, _("Could not write keyfile to secrets.tdb\n")); + ZERO_STRUCT(keyfile); + return -1; + } + + ZERO_STRUCT(keyfile); + return 0; +} + +int net_afs_impersonate(struct net_context *c, int argc, + const char **argv) +{ + char *token; + + if (argc != 2) { + d_fprintf(stderr, "%s net afs impersonate <user> <cell>\n", + _("Usage:")); + exit(1); + } + + token = afs_createtoken_str(argv[0], argv[1]); + + if (token == NULL) { + fprintf(stderr, _("Could not create token\n")); + exit(1); + } + + if (!afs_settoken_str(token)) { + fprintf(stderr, _("Could not set token into kernel\n")); + exit(1); + } + + printf(_("Success: %s@%s\n"), argv[0], argv[1]); + return 0; +} + +int net_afs(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "key", + net_afs_key, + NET_TRANSPORT_LOCAL, + N_("Import an OpenAFS keyfile"), + N_("net afs key <filename>\n" + " Import kefile from <filename>.") + }, + { + "impersonate", + net_afs_impersonate, + NET_TRANSPORT_LOCAL, + N_("Get a user token"), + N_("net afs impersonate <user> <cell>\n" + " Create token for user@cell") + }, + {NULL, NULL, 0, NULL, NULL} + }; + return net_run_function(c, argc, argv, "net afs", func); +} + +#endif /* WITH_FAKE_KASERVER */ diff --git a/source3/utils/net_afs.h b/source3/utils/net_afs.h new file mode 100644 index 0000000..31606dd --- /dev/null +++ b/source3/utils/net_afs.h @@ -0,0 +1,29 @@ +/* + Samba Unix/Linux SMB client library + net afs commands + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _NET_AFS_H_ +#define _NET_AFS_H_ + +int net_afs_usage(struct net_context *c, int argc, const char **argv); +int net_afs_key(struct net_context *c, int argc, const char **argv); +int net_afs_impersonate(struct net_context *c, int argc, + const char **argv); +int net_afs(struct net_context *c, int argc, const char **argv); + +#endif /*_NET_AFS_H_*/ diff --git a/source3/utils/net_cache.c b/source3/utils/net_cache.c new file mode 100644 index 0000000..a5a67ea --- /dev/null +++ b/source3/utils/net_cache.c @@ -0,0 +1,652 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) Rafal Szczesniak 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "net.h" +#include "libsmb/samlogon_cache.h" +#include "../librpc/gen_ndr/netlogon.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "libcli/security/dom_sid.h" +#include "lib/util/strv.h" +#include "lib/gencache.h" + +/** + * @file net_cache.c + * @brief This is part of the net tool which is basically command + * line wrapper for gencache.c functions (mainly for testing) + * + **/ + + +/* + * These routines are used via gencache_iterate() to display the cache's contents + * (print_cache_entry) and to flush it (delete_cache_entry). + * Both of them are defined by first arg of gencache_iterate() routine. + */ +static void print_cache_entry(const char* keystr, DATA_BLOB value, + const time_t timeout, void* dptr) +{ + char *timeout_str; + char *alloc_str = NULL; + const char *datastr; + char *datastr_free = NULL; + time_t now_t = time(NULL); + struct tm timeout_tm, now_tm; + struct tm *ptimeout_tm, *pnow_tm; + + ptimeout_tm = localtime_r(&timeout, &timeout_tm); + if (ptimeout_tm == NULL) { + return; + } + pnow_tm = localtime_r(&now_t, &now_tm); + if (pnow_tm == NULL) { + return; + } + + /* form up timeout string depending whether it's today's date or not */ + if (timeout_tm.tm_year != now_tm.tm_year || + timeout_tm.tm_mon != now_tm.tm_mon || + timeout_tm.tm_mday != now_tm.tm_mday) { + + timeout_str = asctime(&timeout_tm); + if (!timeout_str) { + return; + } + timeout_str[strlen(timeout_str) - 1] = '\0'; /* remove tailing CR */ + } else { + if (asprintf(&alloc_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour, + timeout_tm.tm_min, timeout_tm.tm_sec) == -1) { + return; + } + timeout_str = alloc_str; + } + + datastr = (char *)value.data; + + if (strnequal(keystr, "NAME2SID/", strlen("NAME2SID/"))) { + const char *strv = (char *)value.data; + size_t strv_len = value.length; + const char *sid = strv_len_next(strv, strv_len, NULL); + const char *type = strv_len_next(strv, strv_len, sid); + datastr = talloc_asprintf(talloc_tos(), "%s (%s)", sid, type); + } + + if (strnequal(keystr, "SID2NAME/", strlen("SID2NAME/"))) { + const char *strv = (char *)value.data; + size_t strv_len = value.length; + const char *domain = strv_len_next(strv, strv_len, NULL); + const char *name = strv_len_next(strv, strv_len, domain); + const char *type = strv_len_next(strv, strv_len, name); + datastr = talloc_asprintf(talloc_tos(), "%s\\%s (%s)", + domain, name, type); + } + + if ((value.length > 0) && (value.data[value.length-1] != '\0')) { + datastr_free = talloc_asprintf( + talloc_tos(), "<binary length %d>", + (int)value.length); + datastr = datastr_free; + if (datastr == NULL) { + datastr = "<binary>"; + } + } + + d_printf(_("Key: %s\t Timeout: %s\t Value: %s %s\n"), keystr, + timeout_str, datastr, timeout > now_t ? "": _("(expired)")); + + SAFE_FREE(alloc_str); +} + +static void delete_cache_entry(const char* keystr, const char* datastr, + const time_t timeout, void* dptr) +{ + if (!gencache_del(keystr)) + d_fprintf(stderr, _("Couldn't delete entry! key = %s\n"), + keystr); +} + + +/** + * Parse text representation of timeout value + * + * @param timeout_str string containing text representation of the timeout + * @return numeric timeout of time_t type + **/ +static time_t parse_timeout(const char* timeout_str) +{ + char sign = '\0', *number = NULL, unit = '\0'; + int len, number_begin, number_end; + time_t timeout; + + /* sign detection */ + if (timeout_str[0] == '!' || timeout_str[0] == '+') { + sign = timeout_str[0]; + number_begin = 1; + } else { + number_begin = 0; + } + + /* unit detection */ + len = strlen(timeout_str); + switch (timeout_str[len - 1]) { + case 's': + case 'm': + case 'h': + case 'd': + case 'w': unit = timeout_str[len - 1]; + } + + /* number detection */ + len = (sign) ? strlen(&timeout_str[number_begin]) : len; + number_end = (unit) ? len - 1 : len; + number = SMB_STRNDUP(&timeout_str[number_begin], number_end); + + /* calculate actual timeout value */ + timeout = (time_t)atoi(number); + + switch (unit) { + case 'm': timeout *= 60; break; + case 'h': timeout *= 60*60; break; + case 'd': timeout *= 60*60*24; break; + case 'w': timeout *= 60*60*24*7; break; /* that's fair enough, I think :) */ + } + + switch (sign) { + case '!': timeout = time(NULL) - timeout; break; + case '+': + default: timeout += time(NULL); break; + } + + if (number) SAFE_FREE(number); + return timeout; +} + + +/** + * Add an entry to the cache. If it does exist, then set it. + * + * @param c A net_context structure + * @param argv key, value and timeout are passed in command line + * @return 0 on success, otherwise failure + **/ +static int net_cache_add(struct net_context *c, int argc, const char **argv) +{ + const char *keystr, *datastr, *timeout_str; + time_t timeout; + + if (argc < 3 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net cache add <key string> <data string> " + "<timeout>\n")); + return -1; + } + + keystr = argv[0]; + datastr = argv[1]; + timeout_str = argv[2]; + + /* parse timeout given in command line */ + timeout = parse_timeout(timeout_str); + if (!timeout) { + d_fprintf(stderr, _("Invalid timeout argument.\n")); + return -1; + } + + if (gencache_set(keystr, datastr, timeout)) { + d_printf(_("New cache entry stored successfully.\n")); + return 0; + } + + d_fprintf(stderr, _("Entry couldn't be added. Perhaps there's already such a key.\n")); + return -1; +} + +/** + * Delete an entry in the cache + * + * @param c A net_context structure + * @param argv key to delete an entry of + * @return 0 on success, otherwise failure + **/ +static int net_cache_del(struct net_context *c, int argc, const char **argv) +{ + const char *keystr = argv[0]; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _(" net cache del <key string>\n")); + return -1; + } + + if(gencache_del(keystr)) { + d_printf(_("Entry deleted.\n")); + return 0; + } + + d_fprintf(stderr, _("Couldn't delete specified entry\n")); + return -1; +} + + +/** + * Get and display an entry from the cache + * + * @param c A net_context structure + * @param argv key to search an entry of + * @return 0 on success, otherwise failure + **/ +static int net_cache_get(struct net_context *c, int argc, const char **argv) +{ + const char* keystr = argv[0]; + DATA_BLOB value; + time_t timeout; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _(" net cache get <key>\n")); + return -1; + } + + if (gencache_get_data_blob(keystr, NULL, &value, &timeout, NULL)) { + print_cache_entry(keystr, value, timeout, NULL); + data_blob_free(&value); + return 0; + } + + d_fprintf(stderr, _("Failed to find entry\n")); + return -1; +} + + +/** + * Search an entry/entries in the cache + * + * @param c A net_context structure + * @param argv key pattern to match the entries to + * @return 0 on success, otherwise failure + **/ +static int net_cache_search(struct net_context *c, int argc, const char **argv) +{ + const char* pattern; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _(" net cache search <pattern>\n")); + return -1; + } + + pattern = argv[0]; + gencache_iterate_blobs(print_cache_entry, NULL, pattern); + return 0; +} + + +/** + * List the contents of the cache + * + * @param c A net_context structure + * @param argv ignored in this functionality + * @return always returns 0 + **/ +static int net_cache_list(struct net_context *c, int argc, const char **argv) +{ + const char* pattern = "*"; + + if (c->display_usage) { + d_printf( "%s\n" + "net cache list\n" + " %s\n", + _("Usage:"), + _("List all cache entries.")); + return 0; + } + gencache_iterate_blobs(print_cache_entry, NULL, pattern); + return 0; +} + + +/** + * Flush the whole cache + * + * @param c A net_context structure + * @param argv ignored in this functionality + * @return always returns 0 + **/ +static int net_cache_flush(struct net_context *c, int argc, const char **argv) +{ + const char* pattern = "*"; + if (c->display_usage) { + d_printf( "%s\n" + "net cache flush\n" + " %s", + _("Usage:"), + _("Delete all cache entries.")); + return 0; + } + gencache_iterate(delete_cache_entry, NULL, pattern); + return 0; +} + +static int netsamlog_cache_for_all_cb(const char *sid_str, + time_t when_cached, + struct netr_SamInfo3 *info3, + void *private_data) +{ + struct net_context *c = (struct net_context *)private_data; + char *name = NULL; + + name = talloc_asprintf(c, "%s\\%s", + info3->base.logon_domain.string, + info3->base.account_name.string); + if (name == NULL) { + return -1; + } + + d_printf("%-50s %-40s %s\n", + sid_str, + name, + timestring(c, when_cached)); + + return 0; +} + +static int net_cache_samlogon_list(struct net_context *c, + int argc, + const char **argv) +{ + int ret; + + d_printf("%-50s %-40s When cached\n", "SID", "Name"); + d_printf("------------------------------------------------------------" + "------------------------------------------------------------" + "----\n"); + + ret = netsamlog_cache_for_all(netsamlog_cache_for_all_cb, c); + if (ret == -1) { + return -1; + } + + return 0; +} + +static int net_cache_samlogon_show(struct net_context *c, + int argc, + const char **argv) +{ + const char *sid_str = argv[0]; + struct dom_sid sid; + struct dom_sid *user_sids = NULL; + uint32_t num_user_sids; + struct netr_SamInfo3 *info3 = NULL; + char *name = NULL; + uint32_t i; + NTSTATUS status; + bool ok; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n" + "net cache samlogon show SID\n" + " %s\n", + _("Usage:"), + _("Show samlogon cache entry for SID.")); + return 0; + } + + ok = string_to_sid(&sid, sid_str); + if (!ok) { + d_printf("String to SID failed for %s\n", sid_str); + return -1; + } + + info3 = netsamlogon_cache_get(c, &sid); + if (info3 == NULL) { + d_printf("SID %s not found in samlogon cache\n", sid_str); + return -1; + } + + name = talloc_asprintf(c, "%s\\%s", + info3->base.logon_domain.string, + info3->base.account_name.string); + if (name == NULL) { + return -1; + } + + d_printf("Name: %s\n", name); + + status = sid_array_from_info3(c, + info3, + &user_sids, + &num_user_sids, + true); + if (!NT_STATUS_IS_OK(status)) { + TALLOC_FREE(user_sids); + d_printf("sid_array_from_info3 failed for %s\n", sid_str); + return -1; + } + + for (i = 0; i < num_user_sids; i++) { + struct dom_sid_buf buf; + d_printf("SID %2" PRIu32 ": %s\n", + i, + dom_sid_str_buf(&user_sids[i], &buf)); + } + + TALLOC_FREE(user_sids); + + return 0; +} + +static int net_cache_samlogon_ndrdump(struct net_context *c, + int argc, + const char **argv) +{ + const char *sid_str = NULL; + struct dom_sid sid; + struct netr_SamInfo3 *info3 = NULL; + struct ndr_print *ndr_print = NULL; + bool ok; + + if (argc != 1 || c->display_usage) { + d_printf( "%s\n" + "net cache samlogon ndrdump SID\n" + " %s\n", + _("Usage:"), + _("Show samlogon cache entry for SID.")); + return 0; + } + + sid_str = argv[0]; + + ok = string_to_sid(&sid, sid_str); + if (!ok) { + d_printf("String to SID failed for %s\n", sid_str); + return -1; + } + + info3 = netsamlogon_cache_get(c, &sid); + if (info3 == NULL) { + d_printf("SID %s not found in samlogon cache\n", sid_str); + return -1; + } + + ndr_print = talloc_zero(c, struct ndr_print); + if (ndr_print == NULL) { + d_printf("Could not allocate memory.\n"); + return -1; + } + + ndr_print->print = ndr_print_printf_helper; + ndr_print->depth = 1; + ndr_print_netr_SamInfo3(ndr_print, "netr_SamInfo3", info3); + TALLOC_FREE(ndr_print); + + return 0; +} + +static int net_cache_samlogon_delete(struct net_context *c, + int argc, + const char **argv) +{ + const char *sid_str = argv[0]; + struct dom_sid sid; + bool ok; + + if (argc != 1 || c->display_usage) { + d_printf( "%s\n" + "net cache samlogon delete SID\n" + " %s\n", + _("Usage:"), + _("Delete samlogon cache entry for SID.")); + return 0; + } + + ok = string_to_sid(&sid, sid_str); + if (!ok) { + d_printf("String to SID failed for %s\n", sid_str); + return -1; + } + + netsamlogon_clear_cached_user(&sid); + + return 0; +} + +static int net_cache_samlogon(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_cache_samlogon_list, + NET_TRANSPORT_LOCAL, + N_("List samlogon cache"), + N_("net cache samlogon list\n" + " List samlogon cachen\n") + }, + { + "show", + net_cache_samlogon_show, + NET_TRANSPORT_LOCAL, + N_("Show samlogon cache entry"), + N_("net cache samlogon show SID\n" + " Show samlogon cache entry\n") + }, + { + "ndrdump", + net_cache_samlogon_ndrdump, + NET_TRANSPORT_LOCAL, + N_("Dump the samlogon cache entry NDR blob"), + N_("net cache samlogon ndrdump SID\n" + " Dump the samlogon cache entry NDR blob\n") + }, + { + "delete", + net_cache_samlogon_delete, + NET_TRANSPORT_LOCAL, + N_("Delete samlogon cache entry"), + N_("net cache samlogon delete SID\n" + " Delete samlogon cache entry\n") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net cache samlogon", func); +} + +/** + * Entry point to 'net cache' subfunctionality + * + * @param c A net_context structure + * @param argv arguments passed to further called functions + * @return whatever further functions return + **/ +int net_cache(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + net_cache_add, + NET_TRANSPORT_LOCAL, + N_("Add new cache entry"), + N_("net cache add <key string> <data string> <timeout>\n" + " Add new cache entry.\n" + " key string\tKey string to add cache data under.\n" + " data string\tData to store under given key.\n" + " timeout\tTimeout for cache data.") + }, + { + "del", + net_cache_del, + NET_TRANSPORT_LOCAL, + N_("Delete existing cache entry by key"), + N_("net cache del <key string>\n" + " Delete existing cache entry by key.\n" + " key string\tKey string to delete.") + }, + { + "get", + net_cache_get, + NET_TRANSPORT_LOCAL, + N_("Get cache entry by key"), + N_("net cache get <key string>\n" + " Get cache entry by key.\n" + " key string\tKey string to look up cache entry for.") + + }, + { + "search", + net_cache_search, + NET_TRANSPORT_LOCAL, + N_("Search entry by pattern"), + N_("net cache search <pattern>\n" + " Search entry by pattern.\n" + " pattern\tPattern to search for in cache.") + }, + { + "list", + net_cache_list, + NET_TRANSPORT_LOCAL, + N_("List all cache entries"), + N_("net cache list\n" + " List all cache entries") + }, + { + "flush", + net_cache_flush, + NET_TRANSPORT_LOCAL, + N_("Delete all cache entries"), + N_("net cache flush\n" + " Delete all cache entries") + }, + { + "samlogon", + net_cache_samlogon, + NET_TRANSPORT_LOCAL, + N_("List contents of the samlogon cache"), + N_("net cache samlogon\n" + " List contents of the samlogon cache") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net cache", func); +} diff --git a/source3/utils/net_conf.c b/source3/utils/net_conf.c new file mode 100644 index 0000000..c16f240 --- /dev/null +++ b/source3/utils/net_conf.c @@ -0,0 +1,1305 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Local configuration interface + * Copyright (C) Michael Adam 2007-2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * This is an interface to Samba's configuration as made available + * by the libsmbconf interface (source/lib/smbconf/smbconf.c). + * + * This currently supports local interaction with the configuration + * stored in the registry. But other backends and remote access via + * rpc might get implemented in the future. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "utils/net.h" +#include "utils/net_conf_util.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_init.h" +#include "lib/smbconf/smbconf_reg.h" +#include "lib/param/loadparm.h" + +/********************************************************************** + * + * usage functions + * + **********************************************************************/ + +static int net_conf_list_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s net conf list\n", _("Usage:")); + return -1; +} + +static int net_conf_import_usage(struct net_context *c, int argc, + const char**argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf import [--test|-T] <filename> " + "[<servicename>]\n" + "\t[--test|-T] testmode - do not act, just print " + "what would be done\n" + "\t<servicename> only import service <servicename>, " + "ignore the rest\n")); + return -1; +} + +static int net_conf_listshares_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet conf listshares\n", _("Usage:")); + return -1; +} + +static int net_conf_drop_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet conf drop\n", _("Usage:")); + return -1; +} + +static int net_conf_showshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net conf showshare <sharename>\n")); + return -1; +} + +static int net_conf_addshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf addshare <sharename> <path> " + "[writeable={y|N} [guest_ok={y|N} [<comment>]]]\n" + "\t<sharename> the new share name.\n" + "\t<path> the path on the filesystem to export.\n" + "\twriteable={y|N} set \"writeable to \"yes\" or " + "\"no\" (default) on this share.\n" + "\tguest_ok={y|N} set \"guest ok\" to \"yes\" or " + "\"no\" (default) on this share.\n" + "\t<comment> optional comment for the new share.\n")); + return -1; +} + +static int net_conf_delshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net conf delshare <sharename>\n")); + return -1; +} + +static int net_conf_setparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf setparm <section> <param> <value>\n")); + return -1; +} + +static int net_conf_getparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf getparm <section> <param>\n")); + return -1; +} + +static int net_conf_delparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf delparm <section> <param>\n")); + return -1; +} + +static int net_conf_getincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf getincludes <section>\n")); + return -1; +} + +static int net_conf_setincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf setincludes <section> [<filename>]*\n")); + return -1; +} + +static int net_conf_delincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net conf delincludes <section>\n")); + return -1; +} + + +/********************************************************************** + * + * Helper functions + * + **********************************************************************/ + +/** + * This functions process a service previously loaded with libsmbconf. + */ +static sbcErr import_process_service(struct net_context *c, + struct smbconf_ctx *conf_ctx, + struct smbconf_service *service) +{ + sbcErr err = SBC_ERR_OK; + + if (c->opt_testmode) { + uint32_t idx; + const char *indent = ""; + if (service->name != NULL) { + d_printf("[%s]\n", service->name); + indent = "\t"; + } + for (idx = 0; idx < service->num_params; idx++) { + d_printf("%s%s = %s\n", indent, + service->param_names[idx], + service->param_values[idx]); + } + d_printf("\n"); + goto done; + } + + if (smbconf_share_exists(conf_ctx, service->name)) { + err = smbconf_delete_share(conf_ctx, service->name); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + } + + err = smbconf_create_set_share(conf_ctx, service); + +done: + return err; +} + + +/********************************************************************** + * + * the main conf functions + * + **********************************************************************/ + +static int net_conf_list(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + sbcErr err; + int ret = -1; + TALLOC_CTX *mem_ctx; + uint32_t num_shares; + uint32_t share_count, param_count; + struct smbconf_service **shares = NULL; + + mem_ctx = talloc_stackframe(); + + if (argc != 0 || c->display_usage) { + net_conf_list_usage(c, argc, argv); + goto done; + } + + err = smbconf_get_config(conf_ctx, mem_ctx, &num_shares, &shares); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error getting config: %s\n"), + sbcErrorString(err)); + goto done; + } + + for (share_count = 0; share_count < num_shares; share_count++) { + const char *indent = ""; + if (shares[share_count]->name != NULL) { + d_printf("[%s]\n", shares[share_count]->name); + indent = "\t"; + } + for (param_count = 0; + param_count < shares[share_count]->num_params; + param_count++) + { + d_printf("%s%s = %s\n", + indent, + shares[share_count]->param_names[param_count], + shares[share_count]->param_values[param_count]); + } + d_printf("\n"); + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_import(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + int ret = -1; + const char *filename = NULL; + const char *servicename = NULL; + char *conf_source = NULL; + TALLOC_CTX *mem_ctx; + struct smbconf_ctx *txt_ctx; + sbcErr err; + + if (c->display_usage) + return net_conf_import_usage(c, argc, argv); + + mem_ctx = talloc_stackframe(); + + switch (argc) { + case 0: + default: + net_conf_import_usage(c, argc, argv); + goto done; + case 2: + servicename = talloc_strdup(mem_ctx, argv[1]); + if (servicename == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + FALL_THROUGH; + case 1: + filename = argv[0]; + break; + } + + DEBUG(3,("net_conf_import: reading configuration from file %s.\n", + filename)); + + conf_source = talloc_asprintf(mem_ctx, "file:%s", filename); + if (conf_source == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + err = smbconf_init(mem_ctx, &txt_ctx, conf_source); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error loading file '%s': %s\n"), filename, + sbcErrorString(err)); + goto done; + } + + if (c->opt_testmode) { + d_printf(_("\nTEST MODE - " + "would import the following configuration:\n\n")); + } + + if (servicename != NULL) { + struct smbconf_service *service = NULL; + + err = smbconf_get_share(txt_ctx, mem_ctx, + servicename, + &service); + if (!SBC_ERROR_IS_OK(err)) { + goto cancel; + } + + err = smbconf_transaction_start(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error starting transaction: %s\n"), + sbcErrorString(err)); + goto done; + } + + err = import_process_service(c, conf_ctx, service); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error importing service %s: %s\n"), + servicename, sbcErrorString(err)); + goto cancel; + } + } else { + struct smbconf_service **services = NULL; + uint32_t num_shares, sidx; + + err = smbconf_get_config(txt_ctx, mem_ctx, + &num_shares, + &services); + if (!SBC_ERROR_IS_OK(err)) { + goto cancel; + } + if (!c->opt_testmode) { + if (!SBC_ERROR_IS_OK(smbconf_drop(conf_ctx))) { + goto cancel; + } + } + + /* + * Wrap the importing of shares into a transaction, + * but only 100 at a time, in order to save memory. + * The allocated memory accumulates across the actions + * within the transaction, and for me, some 1500 + * imported shares, the MAX_TALLOC_SIZE of 256 MB + * was exceeded. + */ + err = smbconf_transaction_start(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error starting transaction: %s\n"), + sbcErrorString(err)); + goto done; + } + + for (sidx = 0; sidx < num_shares; sidx++) { + err = import_process_service(c, conf_ctx, + services[sidx]); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error importing service %s: %s\n"), + services[sidx]->name, + sbcErrorString(err)); + goto cancel; + } + + if (sidx % 100) { + continue; + } + + err = smbconf_transaction_commit(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error committing transaction: " + "%s\n"), + sbcErrorString(err)); + goto done; + } + err = smbconf_transaction_start(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error starting transaction: %s\n"), + sbcErrorString(err)); + goto done; + } + } + } + + err = smbconf_transaction_commit(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error committing transaction: %s\n"), + sbcErrorString(err)); + } else { + ret = 0; + } + + goto done; + +cancel: + err = smbconf_transaction_cancel(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error cancelling transaction: %s\n"), + sbcErrorString(err)); + } + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_listshares(struct net_context *c, + struct smbconf_ctx *conf_ctx, int argc, + const char **argv) +{ + sbcErr err; + int ret = -1; + uint32_t count, num_shares = 0; + char **share_names = NULL; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_stackframe(); + + if (argc != 0 || c->display_usage) { + net_conf_listshares_usage(c, argc, argv); + goto done; + } + + err = smbconf_get_share_names(conf_ctx, mem_ctx, &num_shares, + &share_names); + if (!SBC_ERROR_IS_OK(err)) { + goto done; + } + + for (count = 0; count < num_shares; count++) + { + d_printf("%s\n", share_names[count]); + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_drop(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + int ret = -1; + sbcErr err; + + if (argc != 0 || c->display_usage) { + net_conf_drop_usage(c, argc, argv); + goto done; + } + + err = smbconf_drop(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error deleting configuration: %s\n"), + sbcErrorString(err)); + goto done; + } + + ret = 0; + +done: + return ret; +} + +static int net_conf_showshare(struct net_context *c, + struct smbconf_ctx *conf_ctx, int argc, + const char **argv) +{ + int ret = -1; + sbcErr err; + const char *sharename = NULL; + TALLOC_CTX *mem_ctx; + uint32_t count; + struct smbconf_service *service = NULL; + + mem_ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + net_conf_showshare_usage(c, argc, argv); + goto done; + } + + sharename = talloc_strdup(mem_ctx, argv[0]); + if (sharename == NULL) { + d_printf("error: out of memory!\n"); + goto done; + } + + err = smbconf_get_share(conf_ctx, mem_ctx, sharename, &service); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error getting share parameters: %s\n"), + sbcErrorString(err)); + goto done; + } + + d_printf("[%s]\n", service->name); + + for (count = 0; count < service->num_params; count++) { + d_printf("\t%s = %s\n", service->param_names[count], + service->param_values[count]); + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +/** + * Add a share, with a couple of standard parameters, partly optional. + * + * This is a high level utility function of the net conf utility, + * not a direct frontend to the smbconf API. + */ +static int net_conf_addshare(struct net_context *c, + struct smbconf_ctx *conf_ctx, int argc, + const char **argv) +{ + int ret = -1; + sbcErr err; + char *sharename = NULL; + const char *path = NULL; + const char *comment = NULL; + const char *guest_ok = "no"; + const char *writeable = "no"; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (c->display_usage) { + net_conf_addshare_usage(c, argc, argv); + ret = 0; + goto done; + } + + switch (argc) { + case 0: + case 1: + default: + net_conf_addshare_usage(c, argc, argv); + goto done; + case 5: + comment = argv[4]; + + FALL_THROUGH; + case 4: + if (!strnequal(argv[3], "guest_ok=", 9)) { + net_conf_addshare_usage(c, argc, argv); + goto done; + } + switch (argv[3][9]) { + case 'y': + case 'Y': + guest_ok = "yes"; + break; + case 'n': + case 'N': + guest_ok = "no"; + break; + default: + net_conf_addshare_usage(c, argc, argv); + goto done; + } + + FALL_THROUGH; + case 3: + if (!strnequal(argv[2], "writeable=", 10)) { + net_conf_addshare_usage(c, argc, argv); + goto done; + } + switch (argv[2][10]) { + case 'y': + case 'Y': + writeable = "yes"; + break; + case 'n': + case 'N': + writeable = "no"; + break; + default: + net_conf_addshare_usage(c, argc, argv); + goto done; + } + + FALL_THROUGH; + case 2: + path = argv[1]; + sharename = talloc_strdup(mem_ctx, argv[0]); + if (sharename == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + break; + } + + /* + * validate arguments + */ + + /* validate share name */ + + if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, + strlen(sharename))) + { + d_fprintf(stderr, _("ERROR: share name %s contains " + "invalid characters (any of %s)\n"), + sharename, INVALID_SHARENAME_CHARS); + goto done; + } + + if (strequal(sharename, GLOBAL_NAME)) { + d_fprintf(stderr, + _("ERROR: 'global' is not a valid share name.\n")); + goto done; + } + + if (smbconf_share_exists(conf_ctx, sharename)) { + d_fprintf(stderr, _("ERROR: share %s already exists.\n"), + sharename); + goto done; + } + + /* validate path */ + + if (path[0] != '/') { + bool ok = false; + + if (strequal(sharename, HOMES_NAME) && path[0] == '\0') { + /* The homes share can be an empty path. */ + ok = true; + } + if (!ok) { + d_fprintf(stderr, + _("Error: path '%s' is not an absolute path.\n"), + path); + goto done; + } + } + + /* + * start a transaction + */ + + err = smbconf_transaction_start(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf("error starting transaction: %s\n", + sbcErrorString(err)); + goto done; + } + + /* + * create the share + */ + + err = smbconf_create_share(conf_ctx, sharename); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error creating share %s: %s\n"), + sharename, sbcErrorString(err)); + goto cancel; + } + + /* + * fill the share with parameters + */ + + err = smbconf_set_parameter(conf_ctx, sharename, "path", path); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error setting parameter %s: %s\n"), + "path", sbcErrorString(err)); + goto cancel; + } + + if (comment != NULL) { + err = smbconf_set_parameter(conf_ctx, sharename, "comment", + comment); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error setting parameter %s: %s\n"), + "comment", sbcErrorString(err)); + goto cancel; + } + } + + err = smbconf_set_parameter(conf_ctx, sharename, "guest ok", guest_ok); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error setting parameter %s: %s\n"), + "'guest ok'", sbcErrorString(err)); + goto cancel; + } + + err = smbconf_set_parameter(conf_ctx, sharename, "writeable", + writeable); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error setting parameter %s: %s\n"), + "writeable", sbcErrorString(err)); + goto cancel; + } + + /* + * commit the whole thing + */ + + err = smbconf_transaction_commit(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf("error committing transaction: %s\n", + sbcErrorString(err)); + } else { + ret = 0; + } + + goto done; + +cancel: + err = smbconf_transaction_cancel(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf("error cancelling transaction: %s\n", + sbcErrorString(err)); + } + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_delshare(struct net_context *c, + struct smbconf_ctx *conf_ctx, int argc, + const char **argv) +{ + int ret = -1; + const char *sharename = NULL; + sbcErr err; + NTSTATUS status; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + net_conf_delshare_usage(c, argc, argv); + goto done; + } + sharename = talloc_strdup(mem_ctx, argv[0]); + if (sharename == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + err = smbconf_delete_share(conf_ctx, sharename); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error deleting share %s: %s\n"), + sharename, sbcErrorString(err)); + goto done; + } + + status = delete_share_security(sharename); + if (!NT_STATUS_IS_OK(status) && + !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + d_fprintf(stderr, _("deleting share acl failed for %s: %s\n"), + sharename, nt_errstr(status)); + goto done; + } + + ret = 0; +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_setparm(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + int ret = -1; + sbcErr err; + char *service = NULL; + char *param = NULL; + const char *value_str = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc != 3 || c->display_usage) { + net_conf_setparm_usage(c, argc, argv); + goto done; + } + /* + * NULL service name means "dangling parameters" to libsmbconf. + * We use the empty string from the command line for this purpose. + */ + if (strlen(argv[0]) != 0) { + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + } + param = strlower_talloc(mem_ctx, argv[1]); + if (param == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + value_str = argv[2]; + + if (!net_conf_param_valid(service,param, value_str)) { + goto done; + } + + err = smbconf_transaction_start(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error starting transaction: %s\n"), + sbcErrorString(err)); + goto done; + } + + if (!smbconf_share_exists(conf_ctx, service)) { + err = smbconf_create_share(conf_ctx, service); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error creating share '%s': %s\n"), + service, sbcErrorString(err)); + goto cancel; + } + } + + err = smbconf_set_parameter(conf_ctx, service, param, value_str); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error setting value '%s': %s\n"), + param, sbcErrorString(err)); + goto cancel; + } + + err = smbconf_transaction_commit(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error committing transaction: %s\n"), + sbcErrorString(err)); + } else { + ret = 0; + } + + goto done; + +cancel: + err = smbconf_transaction_cancel(conf_ctx); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error cancelling transaction: %s\n"), + sbcErrorString(err)); + } + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_getparm(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + int ret = -1; + sbcErr err; + char *service = NULL; + char *param = NULL; + char *valstr = NULL; + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_stackframe(); + + if (argc != 2 || c->display_usage) { + net_conf_getparm_usage(c, argc, argv); + goto done; + } + /* + * NULL service name means "dangling parameters" to libsmbconf. + * We use the empty string from the command line for this purpose. + */ + if (strlen(argv[0]) != 0) { + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + } + param = strlower_talloc(mem_ctx, argv[1]); + if (param == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + err = smbconf_get_parameter(conf_ctx, mem_ctx, service, param, &valstr); + if (SBC_ERROR_EQUAL(err, SBC_ERR_NO_SUCH_SERVICE)) { + d_fprintf(stderr, + _("Error: given service '%s' does not exist.\n"), + service); + goto done; + } else if (SBC_ERROR_EQUAL(err, SBC_ERR_INVALID_PARAM)) { + d_fprintf(stderr, + _("Error: given parameter '%s' is not set.\n"), + param); + goto done; + } else if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error getting value '%s': %s.\n"), + param, sbcErrorString(err)); + goto done; + } + + d_printf("%s\n", valstr); + + ret = 0; +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_delparm(struct net_context *c, struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + int ret = -1; + sbcErr err; + char *service = NULL; + char *param = NULL; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc != 2 || c->display_usage) { + net_conf_delparm_usage(c, argc, argv); + goto done; + } + /* + * NULL service name means "dangling parameters" to libsmbconf. + * We use the empty string from the command line for this purpose. + */ + if (strlen(argv[0]) != 0) { + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + } + param = strlower_talloc(mem_ctx, argv[1]); + if (param == NULL) { + d_printf("error: out of memory!\n"); + goto done; + } + + err = smbconf_delete_parameter(conf_ctx, service, param); + if (SBC_ERROR_EQUAL(err, SBC_ERR_NO_SUCH_SERVICE)) { + d_fprintf(stderr, + _("Error: given service '%s' does not exist.\n"), + service); + goto done; + } else if (SBC_ERROR_EQUAL(err, SBC_ERR_INVALID_PARAM)) { + d_fprintf(stderr, + _("Error: given parameter '%s' is not set.\n"), + param); + goto done; + } else if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("Error deleting value '%s': %s.\n"), + param, sbcErrorString(err)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_getincludes(struct net_context *c, + struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + sbcErr err; + uint32_t num_includes; + uint32_t count; + char *service; + char **includes = NULL; + int ret = -1; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + net_conf_getincludes_usage(c, argc, argv); + goto done; + } + + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + err = smbconf_get_includes(conf_ctx, mem_ctx, service, + &num_includes, &includes); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error getting includes: %s\n"), sbcErrorString(err)); + goto done; + } + + for (count = 0; count < num_includes; count++) { + d_printf("include = %s\n", includes[count]); + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_setincludes(struct net_context *c, + struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + sbcErr err; + char *service; + uint32_t num_includes; + const char **includes; + int ret = -1; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc < 1 || c->display_usage) { + net_conf_setincludes_usage(c, argc, argv); + goto done; + } + + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + num_includes = argc - 1; + if (num_includes == 0) { + includes = NULL; + } else { + includes = argv + 1; + } + + err = smbconf_set_includes(conf_ctx, service, num_includes, includes); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error setting includes: %s\n"), sbcErrorString(err)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_conf_delincludes(struct net_context *c, + struct smbconf_ctx *conf_ctx, + int argc, const char **argv) +{ + sbcErr err; + char *service; + int ret = -1; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + net_conf_delincludes_usage(c, argc, argv); + goto done; + } + + service = talloc_strdup(mem_ctx, argv[0]); + if (service == NULL) { + d_printf(_("error: out of memory!\n")); + goto done; + } + + err = smbconf_delete_includes(conf_ctx, service); + if (!SBC_ERROR_IS_OK(err)) { + d_printf(_("error deleting includes: %s\n"), sbcErrorString(err)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + + +/********************************************************************** + * + * Wrapper and net_conf_run_function mechanism. + * + **********************************************************************/ + +/** + * Wrapper function to call the main conf functions. + * The wrapper calls handles opening and closing of the + * configuration. + */ +static int net_conf_wrap_function(struct net_context *c, + int (*fn)(struct net_context *, + struct smbconf_ctx *, + int, const char **), + int argc, const char **argv) +{ + sbcErr err; + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct smbconf_ctx *conf_ctx; + int ret = -1; + + err = smbconf_init(mem_ctx, &conf_ctx, "registry:"); + if (!SBC_ERROR_IS_OK(err)) { + talloc_free(mem_ctx); + return -1; + } + + ret = fn(c, conf_ctx, argc, argv); + + smbconf_shutdown(conf_ctx); + + talloc_free(mem_ctx); + return ret; +} + +/* + * We need a functable struct of our own, because the + * functions are called through a wrapper that handles + * the opening and closing of the configuration, and so on. + */ +struct conf_functable { + const char *funcname; + int (*fn)(struct net_context *c, struct smbconf_ctx *ctx, int argc, + const char **argv); + int valid_transports; + const char *description; + const char *usage; +}; + +/** + * This imitates net_run_function but calls the main functions + * through the wrapper net_conf_wrap_function(). + */ +static int net_conf_run_function(struct net_context *c, int argc, + const char **argv, const char *whoami, + struct conf_functable *table) +{ + int i; + + if (argc != 0) { + for (i=0; table[i].funcname; i++) { + if (strcasecmp_m(argv[0], table[i].funcname) == 0) + return net_conf_wrap_function(c, table[i].fn, + argc-1, + argv+1); + } + } + + d_printf(_("Usage:\n")); + for (i=0; table[i].funcname; i++) { + if (c->display_usage == false) + d_printf("%s %-15s %s\n", whoami, table[i].funcname, + table[i].description); + else + d_printf("%s\n", table[i].usage); + } + + return c->display_usage?0:-1; +} + +/* + * Entry-point for all the CONF functions. + */ + +int net_conf(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + struct conf_functable func_table[] = { + { + "list", + net_conf_list, + NET_TRANSPORT_LOCAL, + N_("Dump the complete configuration in smb.conf like " + "format."), + N_("net conf list\n" + " Dump the complete configuration in smb.conf " + "like format.") + + }, + { + "import", + net_conf_import, + NET_TRANSPORT_LOCAL, + N_("Import configuration from file in smb.conf " + "format."), + N_("net conf import\n" + " Import configuration from file in smb.conf " + "format.") + }, + { + "listshares", + net_conf_listshares, + NET_TRANSPORT_LOCAL, + N_("List the share names."), + N_("net conf listshares\n" + " List the share names.") + }, + { + "drop", + net_conf_drop, + NET_TRANSPORT_LOCAL, + N_("Delete the complete configuration."), + N_("net conf drop\n" + " Delete the complete configuration.") + }, + { + "showshare", + net_conf_showshare, + NET_TRANSPORT_LOCAL, + N_("Show the definition of a share."), + N_("net conf showshare\n" + " Show the definition of a share.") + }, + { + "addshare", + net_conf_addshare, + NET_TRANSPORT_LOCAL, + N_("Create a new share."), + N_("net conf addshare\n" + " Create a new share.") + }, + { + "delshare", + net_conf_delshare, + NET_TRANSPORT_LOCAL, + N_("Delete a share."), + N_("net conf delshare\n" + " Delete a share.") + }, + { + "setparm", + net_conf_setparm, + NET_TRANSPORT_LOCAL, + N_("Store a parameter."), + N_("net conf setparm\n" + " Store a parameter.") + }, + { + "getparm", + net_conf_getparm, + NET_TRANSPORT_LOCAL, + N_("Retrieve the value of a parameter."), + N_("net conf getparm\n" + " Retrieve the value of a parameter.") + }, + { + "delparm", + net_conf_delparm, + NET_TRANSPORT_LOCAL, + N_("Delete a parameter."), + N_("net conf delparm\n" + " Delete a parameter.") + }, + { + "getincludes", + net_conf_getincludes, + NET_TRANSPORT_LOCAL, + N_("Show the includes of a share definition."), + N_("net conf getincludes\n" + " Show the includes of a share definition.") + }, + { + "setincludes", + net_conf_setincludes, + NET_TRANSPORT_LOCAL, + N_("Set includes for a share."), + N_("net conf setincludes\n" + " Set includes for a share.") + }, + { + "delincludes", + net_conf_delincludes, + NET_TRANSPORT_LOCAL, + N_("Delete includes from a share definition."), + N_("net conf delincludes\n" + " Delete includes from a share definition.") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + ret = net_conf_run_function(c, argc, argv, "net conf", func_table); + + return ret; +} + diff --git a/source3/utils/net_conf_util.c b/source3/utils/net_conf_util.c new file mode 100644 index 0000000..ec6a479 --- /dev/null +++ b/source3/utils/net_conf_util.c @@ -0,0 +1,69 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Configuration interface + * + * Copyright (C) Michael Adam 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Utility functions for net conf and net rpc conf. + */ + +#include "includes.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_reg.h" +#include "lib/param/loadparm.h" +#include "net_conf_util.h" + +bool net_conf_param_valid(const char *service, + const char *param, + const char *valstr) +{ + const char *canon_param, *canon_valstr; + + if (!lp_parameter_is_valid(param)) { + d_fprintf(stderr, "Invalid parameter '%s' given.\n", param); + return false; + } + + if (!smbconf_reg_parameter_is_valid(param)) { + d_fprintf(stderr, "Parameter '%s' not allowed in registry.\n", + param); + return false; + } + + if (!strequal(service, GLOBAL_NAME) && lp_parameter_is_global(param)) { + d_fprintf(stderr, "Global parameter '%s' not allowed in " + "service definition ('%s').\n", param, service); + return false; + } + + if (!lp_canonicalize_parameter_with_value(param, valstr, + &canon_param, + &canon_valstr)) + { + /* + * We already know the parameter name is valid. + * So the value must be invalid. + */ + d_fprintf(stderr, "invalid value '%s' given for " + "parameter '%s'\n", valstr, param); + return false; + } + + return true; +} diff --git a/source3/utils/net_conf_util.h b/source3/utils/net_conf_util.h new file mode 100644 index 0000000..798b399 --- /dev/null +++ b/source3/utils/net_conf_util.h @@ -0,0 +1,33 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Configuration interface + * + * Copyright (C) Michael Adam 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NET_CONF_UTIL_H__ +#define __NET_CONF_UTIL_H__ + +/* + * Utility functions for net conf and net rpc conf. + */ + +bool net_conf_param_valid(const char *service, + const char *param, + const char *valstr); + +#endif /* __NET_CONF_UTIL_H__ */ diff --git a/source3/utils/net_dns.c b/source3/utils/net_dns.c new file mode 100644 index 0000000..9850ba4 --- /dev/null +++ b/source3/utils/net_dns.c @@ -0,0 +1,224 @@ +/* + Samba Unix/Linux Dynamic DNS Update + net ads commands + + Copyright (C) Krishna Ganugapati (krishnag@centeris.com) 2006 + Copyright (C) Gerald Carter 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "../lib/addns/dns.h" +#include "utils/net_dns.h" + +#if defined(HAVE_KRB5) + +/********************************************************************* +*********************************************************************/ + +DNS_ERROR DoDNSUpdate(char *pszServerName, + const char *pszDomainName, + const char *pszHostName, + const struct sockaddr_storage *sslist, + size_t num_addrs, + uint32_t flags, + uint32_t ttl, + bool remove_host) +{ + DNS_ERROR err; + struct dns_connection *conn; + TALLOC_CTX *mem_ctx; + OM_uint32 minor; + struct dns_update_request *req, *resp; + + DEBUG(10,("DoDNSUpdate called with flags: 0x%08x\n", flags)); + + if (!(flags & DNS_UPDATE_SIGNED) && + !(flags & DNS_UPDATE_UNSIGNED) && + !(flags & DNS_UPDATE_PROBE)) { + return ERROR_DNS_INVALID_PARAMETER; + } + + if ( !remove_host && ((num_addrs <= 0) || !sslist) ) { + return ERROR_DNS_INVALID_PARAMETER; + } + + if (!(mem_ctx = talloc_init("DoDNSUpdate"))) { + return ERROR_DNS_NO_MEMORY; + } + + err = dns_open_connection( pszServerName, DNS_TCP, mem_ctx, &conn ); + if (!ERR_DNS_IS_OK(err)) { + goto error; + } + + if (flags & DNS_UPDATE_PROBE) { + + /* + * Probe if everything's fine + */ + + err = dns_create_probe(mem_ctx, pszDomainName, pszHostName, + num_addrs, sslist, &req); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_update_transaction(mem_ctx, conn, req, &resp); + + if (!ERR_DNS_IS_OK(err)) { + DEBUG(3,("DoDNSUpdate: failed to probe DNS\n")); + goto error; + } + + if ((dns_response_code(resp->flags) == DNS_NO_ERROR) && + (flags & DNS_UPDATE_PROBE_SUFFICIENT)) { + TALLOC_FREE(mem_ctx); + return ERROR_DNS_SUCCESS; + } + } + + if (flags & DNS_UPDATE_UNSIGNED) { + + /* + * First try without signing + */ + + err = dns_create_update_request(mem_ctx, + pszDomainName, + pszHostName, + sslist, + num_addrs, + ttl, + &req); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_update_transaction(mem_ctx, conn, req, &resp); + if (!ERR_DNS_IS_OK(err)) { + DEBUG(3,("DoDNSUpdate: unsigned update failed\n")); + goto error; + } + + if ((dns_response_code(resp->flags) == DNS_NO_ERROR) && + (flags & DNS_UPDATE_UNSIGNED_SUFFICIENT)) { + TALLOC_FREE(mem_ctx); + return ERROR_DNS_SUCCESS; + } + } + + /* + * Okay, we have to try with signing + */ + if (flags & DNS_UPDATE_SIGNED) { + gss_ctx_id_t gss_context; + char *keyname; + + err = dns_create_update_request(mem_ctx, + pszDomainName, + pszHostName, + sslist, + num_addrs, + ttl, + &req); + if (!ERR_DNS_IS_OK(err)) goto error; + + if (!(keyname = dns_generate_keyname( mem_ctx ))) { + err = ERROR_DNS_NO_MEMORY; + goto error; + } + + err = dns_negotiate_sec_ctx( pszDomainName, pszServerName, + keyname, &gss_context, DNS_SRV_ANY ); + + /* retry using the Windows 2000 DNS hack */ + if (!ERR_DNS_IS_OK(err)) { + err = dns_negotiate_sec_ctx( pszDomainName, pszServerName, + keyname, &gss_context, + DNS_SRV_WIN2000 ); + } + + if (!ERR_DNS_IS_OK(err)) + goto error; + + err = dns_sign_update(req, gss_context, keyname, + "gss.microsoft.com", time(NULL), 3600); + + gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER); + + if (!ERR_DNS_IS_OK(err)) goto error; + + err = dns_update_transaction(mem_ctx, conn, req, &resp); + if (!ERR_DNS_IS_OK(err)) goto error; + + err = (dns_response_code(resp->flags) == DNS_NO_ERROR) ? + ERROR_DNS_SUCCESS : ERROR_DNS_UPDATE_FAILED; + + if (!ERR_DNS_IS_OK(err)) { + DEBUG(3,("DoDNSUpdate: signed update failed\n")); + } + } + + +error: + TALLOC_FREE(mem_ctx); + return err; +} + +/********************************************************************* +*********************************************************************/ + +int get_my_ip_address( struct sockaddr_storage **pp_ss ) + +{ + int i, n; + struct sockaddr_storage *list = NULL; + int count = 0; + + /* Honor the configured list of interfaces to register */ + + load_interfaces(); + n = iface_count(); + + if (n <= 0) { + return -1; + } + + if ( (list = SMB_MALLOC_ARRAY( struct sockaddr_storage, n )) == NULL ) { + return -1; + } + + for ( i=0; i<n; i++ ) { + const struct sockaddr_storage *nic_sa_storage = NULL; + + if ((nic_sa_storage = iface_n_sockaddr_storage(i)) == NULL) + continue; + + /* Don't register loopback addresses */ + if (is_loopback_addr((const struct sockaddr *)nic_sa_storage)) { + continue; + } + + /* Don't register link-local addresses */ + if (is_linklocal_addr(nic_sa_storage)) { + continue; + } + + memcpy(&list[count++], nic_sa_storage, sizeof(struct sockaddr_storage)); + } + *pp_ss = list; + + return count; +} + +#endif /* defined(HAVE_KRB5) */ diff --git a/source3/utils/net_dns.h b/source3/utils/net_dns.h new file mode 100644 index 0000000..4569e1c --- /dev/null +++ b/source3/utils/net_dns.h @@ -0,0 +1,44 @@ +/* + Samba Unix/Linux Dynamic DNS Update + net ads commands + + Copyright (C) Krishna Ganugapati (krishnag@centeris.com) 2006 + Copyright (C) Gerald Carter 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* flags for DoDNSUpdate */ + +#define DNS_UPDATE_SIGNED 0x01 +#define DNS_UPDATE_SIGNED_SUFFICIENT 0x02 +#define DNS_UPDATE_UNSIGNED 0x04 +#define DNS_UPDATE_UNSIGNED_SUFFICIENT 0x08 +#define DNS_UPDATE_PROBE 0x10 +#define DNS_UPDATE_PROBE_SUFFICIENT 0x20 + +#if defined(HAVE_KRB5) + +#include "../lib/addns/dns.h" + +DNS_ERROR DoDNSUpdate(char *pszServerName, + const char *pszDomainName, + const char *pszHostName, + const struct sockaddr_storage *sslist, + size_t num_addrs, + uint32_t flags, + uint32_t ttl, + bool remove_host); + +#endif /* defined(HAVE_KRB5) */ diff --git a/source3/utils/net_dom.c b/source3/utils/net_dom.c new file mode 100644 index 0000000..4342990 --- /dev/null +++ b/source3/utils/net_dom.c @@ -0,0 +1,385 @@ +/* + Samba Unix/Linux SMB client library + net dom commands for remote join/unjoin + Copyright (C) 2007,2009 Günther Deschner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "../librpc/gen_ndr/ndr_initshutdown.h" +#include "../librpc/gen_ndr/ndr_winreg.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_net.h" +#include "libsmb/libsmb.h" + +int net_dom_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net dom join " + "<domain=DOMAIN> <ou=OU> <account=ACCOUNT> " + "<password=PASSWORD> <reboot>\n Join a remote machine\n")); + d_printf("%s\n%s", + _("Usage:"), + _("net dom unjoin " + "<account=ACCOUNT> <password=PASSWORD> <reboot>\n" + " Unjoin a remote machine\n")); + d_printf("%s\n%s", + _("Usage:"), + _("net dom renamecomputer " + "<newname=NEWNAME> " + "<account=ACCOUNT> <password=PASSWORD> <reboot>\n" + " Rename joined computer\n")); + + return -1; +} + +static int net_dom_unjoin(struct net_context *c, int argc, const char **argv) +{ + const char *server_name = NULL; + const char *account = NULL; + const char *password = NULL; + uint32_t unjoin_flags = NETSETUP_ACCT_DELETE | + NETSETUP_JOIN_DOMAIN | + NETSETUP_IGNORE_UNSUPPORTED_FLAGS; + struct cli_state *cli = NULL; + bool do_reboot = false; + NTSTATUS ntstatus; + NET_API_STATUS status; + int ret = -1; + int i; + + if (argc < 1 || c->display_usage) { + return net_dom_usage(c, argc, argv); + } + + if (c->opt_host) { + server_name = c->opt_host; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "account", strlen("account"))) { + account = get_string_param(argv[i]); + if (!account) { + return -1; + } + } + if (strnequal(argv[i], "password", strlen("password"))) { + password = get_string_param(argv[i]); + if (!password) { + return -1; + } + } + if (strequal(argv[i], "reboot")) { + do_reboot = true; + } + } + + if (do_reboot) { + ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup, + server_name, NULL, 0, + &cli); + if (!NT_STATUS_IS_OK(ntstatus)) { + return -1; + } + } + + status = NetUnjoinDomain(server_name, account, password, unjoin_flags); + if (status != 0) { + printf(_("Failed to unjoin domain: %s\n"), + libnetapi_get_error_string(c->netapi_ctx, status)); + goto done; + } + + if (do_reboot) { + c->opt_comment = _("Shutting down due to a domain membership " + "change"); + c->opt_reboot = true; + c->opt_timeout = 30; + + ret = run_rpc_command(c, cli, + &ndr_table_initshutdown, + 0, rpc_init_shutdown_internals, + argc, argv); + if (ret == 0) { + goto done; + } + + ret = run_rpc_command(c, cli, &ndr_table_winreg, 0, + rpc_reg_shutdown_internals, + argc, argv); + goto done; + } + + ret = 0; + + done: + if (cli) { + cli_shutdown(cli); + } + + return ret; +} + +static int net_dom_join(struct net_context *c, int argc, const char **argv) +{ + const char *server_name = NULL; + const char *domain_name = NULL; + const char *account_ou = NULL; + const char *Account = NULL; + const char *password = NULL; + uint32_t join_flags = NETSETUP_ACCT_CREATE | + NETSETUP_JOIN_DOMAIN; + struct cli_state *cli = NULL; + bool do_reboot = false; + NTSTATUS ntstatus; + NET_API_STATUS status; + int ret = -1; + int i; + + if (argc < 1 || c->display_usage) { + return net_dom_usage(c, argc, argv); + } + + net_warn_member_options(); + + if (c->opt_host) { + server_name = c->opt_host; + } + + if (c->opt_force) { + join_flags |= NETSETUP_DOMAIN_JOIN_IF_JOINED; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "ou", strlen("ou"))) { + account_ou = get_string_param(argv[i]); + if (!account_ou) { + return -1; + } + } + if (strnequal(argv[i], "domain", strlen("domain"))) { + domain_name = get_string_param(argv[i]); + if (!domain_name) { + return -1; + } + } + if (strnequal(argv[i], "account", strlen("account"))) { + Account = get_string_param(argv[i]); + if (!Account) { + return -1; + } + } + if (strnequal(argv[i], "password", strlen("password"))) { + password = get_string_param(argv[i]); + if (!password) { + return -1; + } + } + if (strequal(argv[i], "reboot")) { + do_reboot = true; + } + } + + if (do_reboot) { + ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup, + server_name, NULL, 0, + &cli); + if (!NT_STATUS_IS_OK(ntstatus)) { + return -1; + } + } + + /* check if domain is a domain or a workgroup */ + + status = NetJoinDomain(server_name, domain_name, account_ou, + Account, password, join_flags); + if (status != 0) { + printf(_("Failed to join domain: %s\n"), + libnetapi_get_error_string(c->netapi_ctx, status)); + goto done; + } + + if (do_reboot) { + c->opt_comment = _("Shutting down due to a domain membership " + "change"); + c->opt_reboot = true; + c->opt_timeout = 30; + + ret = run_rpc_command(c, cli, &ndr_table_initshutdown, 0, + rpc_init_shutdown_internals, + argc, argv); + if (ret == 0) { + goto done; + } + + ret = run_rpc_command(c, cli, &ndr_table_winreg, 0, + rpc_reg_shutdown_internals, + argc, argv); + goto done; + } + + ret = 0; + + done: + if (cli) { + cli_shutdown(cli); + } + + return ret; +} + +static int net_dom_renamecomputer(struct net_context *c, int argc, const char **argv) +{ + const char *server_name = NULL; + const char *account = NULL; + const char *password = NULL; + const char *newname = NULL; + uint32_t rename_options = NETSETUP_ACCT_CREATE; + struct cli_state *cli = NULL; + bool do_reboot = false; + NTSTATUS ntstatus; + NET_API_STATUS status; + int ret = -1; + int i; + + if (argc < 1 || c->display_usage) { + return net_dom_usage(c, argc, argv); + } + + if (c->opt_host) { + server_name = c->opt_host; + } + + for (i=0; i<argc; i++) { + if (strnequal(argv[i], "account", strlen("account"))) { + account = get_string_param(argv[i]); + if (!account) { + return -1; + } + } + if (strnequal(argv[i], "password", strlen("password"))) { + password = get_string_param(argv[i]); + if (!password) { + return -1; + } + } + if (strnequal(argv[i], "newname", strlen("newname"))) { + newname = get_string_param(argv[i]); + if (!newname) { + return -1; + } + } + if (strequal(argv[i], "reboot")) { + do_reboot = true; + } + } + + if (do_reboot) { + ntstatus = net_make_ipc_connection_ex(c, c->opt_workgroup, + server_name, NULL, 0, + &cli); + if (!NT_STATUS_IS_OK(ntstatus)) { + return -1; + } + } + + status = NetRenameMachineInDomain(server_name, newname, + account, password, rename_options); + if (status != 0) { + printf(_("Failed to rename machine: ")); + if (status == W_ERROR_V(WERR_NERR_SETUPNOTJOINED)) { + printf(_("Computer is not joined to a Domain\n")); + goto done; + } + printf("%s\n", + libnetapi_get_error_string(c->netapi_ctx, status)); + goto done; + } + + if (do_reboot) { + c->opt_comment = _("Shutting down due to a computer rename"); + c->opt_reboot = true; + c->opt_timeout = 30; + + ret = run_rpc_command(c, cli, + &ndr_table_initshutdown, + 0, rpc_init_shutdown_internals, + argc, argv); + if (ret == 0) { + goto done; + } + + ret = run_rpc_command(c, cli, &ndr_table_winreg, 0, + rpc_reg_shutdown_internals, + argc, argv); + goto done; + } + + ret = 0; + + done: + if (cli) { + cli_shutdown(cli); + } + + return ret; +} + +int net_dom(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "join", + net_dom_join, + NET_TRANSPORT_LOCAL, + N_("Join a remote machine"), + N_("net dom join <domain=DOMAIN> <ou=OU> " + "<account=ACCOUNT> <password=PASSWORD> <reboot>\n" + " Join a remote machine") + }, + { + "unjoin", + net_dom_unjoin, + NET_TRANSPORT_LOCAL, + N_("Unjoin a remote machine"), + N_("net dom unjoin <account=ACCOUNT> " + "<password=PASSWORD> <reboot>\n" + " Unjoin a remote machine") + }, + { + "renamecomputer", + net_dom_renamecomputer, + NET_TRANSPORT_LOCAL, + N_("Rename a computer that is joined to a domain"), + N_("net dom renamecomputer <newname=NEWNAME> " + "<account=ACCOUNT> <password=PASSWORD> " + "<reboot>\n" + " Rename joined computer") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + return net_run_function(c, argc, argv, "net dom", func); +} diff --git a/source3/utils/net_eventlog.c b/source3/utils/net_eventlog.c new file mode 100644 index 0000000..24dbab9 --- /dev/null +++ b/source3/utils/net_eventlog.c @@ -0,0 +1,275 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Local win32 eventlog interface + * + * Copyright (C) Guenther Deschner 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "utils/net.h" +#include "lib/eventlog/eventlog.h" + +/** + * Dump an *evt win32 eventlog file + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int net_eventlog_dump(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + TALLOC_CTX *ctx = talloc_stackframe(); + enum ndr_err_code ndr_err; + DATA_BLOB blob; + struct EVENTLOG_EVT_FILE evt; + char *s; + + if (argc < 1 || c->display_usage) { + d_fprintf(stderr, "%s\nnet eventlog dump <file.evt>\n", + _("Usage:")); + goto done; + } + + blob.data = (uint8_t *)file_load(argv[0], &blob.length, 0, ctx); + if (!blob.data) { + d_fprintf(stderr, _("failed to load evt file: %s\n"), argv[0]); + goto done; + } + + ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt, + (ndr_pull_flags_fn_t)ndr_pull_EVENTLOG_EVT_FILE); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("evt pull failed: %s\n"), + ndr_errstr(ndr_err)); + goto done; + } + + s = NDR_PRINT_STRUCT_STRING(ctx, EVENTLOG_EVT_FILE, &evt); + if (s) { + printf("%s\n", s); + } + + ret = 0; + done: + TALLOC_FREE(ctx); + return ret; +} + +/** + * Import an *evt win32 eventlog file to internal tdb representation + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int net_eventlog_import(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + TALLOC_CTX *ctx = talloc_stackframe(); + NTSTATUS status; + enum ndr_err_code ndr_err; + DATA_BLOB blob; + uint32_t num_records = 0; + uint32_t i; + ELOG_TDB *etdb = NULL; + + struct EVENTLOGHEADER evt_header; + struct EVENTLOG_EVT_FILE evt; + + if (argc < 2 || c->display_usage) { + d_fprintf(stderr, + "%s\nnet eventlog import <file> <eventlog>\n", + _("Usage:")); + goto done; + } + + blob.data = (uint8_t *)file_load(argv[0], &blob.length, 0, ctx); + if (!blob.data) { + d_fprintf(stderr, _("failed to load evt file: %s\n"), argv[0]); + goto done; + } + + /* dump_data(0, blob.data, blob.length); */ + ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt_header, + (ndr_pull_flags_fn_t)ndr_pull_EVENTLOGHEADER); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("evt header pull failed: %s\n"), + ndr_errstr(ndr_err)); + goto done; + } + + if (evt_header.Flags & ELF_LOGFILE_HEADER_WRAP) { + d_fprintf(stderr, _("input file is wrapped, cannot proceed\n")); + goto done; + } + + ndr_err = ndr_pull_struct_blob(&blob, ctx, &evt, + (ndr_pull_flags_fn_t)ndr_pull_EVENTLOG_EVT_FILE); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("evt pull failed: %s\n"), + ndr_errstr(ndr_err)); + goto done; + } + + /* NDR_PRINT_DEBUG(EVENTLOG_EVT_FILE, &evt); */ + + etdb = elog_open_tdb(argv[1], false, false); + if (!etdb) { + d_fprintf(stderr, _("can't open the eventlog TDB (%s)\n"), + argv[1]); + goto done; + } + + num_records = evt.hdr.CurrentRecordNumber - evt.hdr.OldestRecordNumber; + + for (i=0; i<num_records; i++) { + uint32_t record_number; + struct eventlog_Record_tdb e; + + status = evlog_evt_entry_to_tdb_entry(ctx, &evt.records[i], &e); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = evlog_push_record_tdb(ctx, ELOG_TDB_CTX(etdb), + &e, &record_number); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("can't write to the eventlog: %s\n"), + nt_errstr(status)); + goto done; + } + } + + printf(_("wrote %d entries to tdb\n"), i); + + ret = 0; + done: + + elog_close_tdb(etdb, false); + + TALLOC_FREE(ctx); + return ret; +} + +/** + * Export internal eventlog tdb representation to an *evt win32 eventlog file + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int net_eventlog_export(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + NTSTATUS status; + TALLOC_CTX *ctx = talloc_stackframe(); + DATA_BLOB blob; + uint32_t num_records = 0; + ELOG_TDB *etdb = NULL; + + if (argc < 2 || c->display_usage) { + d_fprintf(stderr, + "%s\nnet eventlog export <file> <eventlog>\n", + _("Usage:")); + goto done; + } + + etdb = elog_open_tdb(argv[1], false, true); + if (!etdb) { + d_fprintf(stderr, _("can't open the eventlog TDB (%s)\n"), + argv[1]); + goto done; + } + + status = evlog_convert_tdb_to_evt(ctx, etdb, &blob, &num_records); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (!file_save(argv[0], blob.data, blob.length)) { + d_fprintf(stderr, _("failed to save evt file: %s\n"), argv[0]); + goto done; + } + + ret = 0; + done: + + elog_close_tdb(etdb, false); + + TALLOC_FREE(ctx); + return ret; +} + +/** + * 'net rpc eventlog' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_eventlog(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + + struct functable func[] = { + { + "dump", + net_eventlog_dump, + NET_TRANSPORT_LOCAL, + N_("Dump eventlog"), + N_("net eventlog dump\n" + " Dump win32 *.evt eventlog file") + }, + { + "import", + net_eventlog_import, + NET_TRANSPORT_LOCAL, + N_("Import eventlog"), + N_("net eventlog import\n" + " Import win32 *.evt eventlog file") + }, + { + "export", + net_eventlog_export, + NET_TRANSPORT_LOCAL, + N_("Export eventlog"), + N_("net eventlog export\n" + " Export win32 *.evt eventlog file") + }, + + + { NULL, NULL, 0, NULL, NULL } + }; + + ret = net_run_function(c, argc, argv, "net eventlog", func); + + return ret; +} diff --git a/source3/utils/net_file.c b/source3/utils/net_file.c new file mode 100644 index 0000000..71a7e05 --- /dev/null +++ b/source3/utils/net_file.c @@ -0,0 +1,57 @@ +/* + Samba Unix/Linux SMB client library + net file commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2002 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_file_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net [<method>] file [misc. options] [targets]\n" + "\tlists all open files on file server\n")); + d_printf(_("net [<method>] file USER <username> " + "[misc. options] [targets]" + "\n\tlists all files opened by username on file server\n")); + d_printf(_("net [<method>] file CLOSE <id> [misc. options] [targets]\n" + "\tcloses specified file on target server\n")); + d_printf(_("net [rap] file INFO <id> [misc. options] [targets]\n" + "\tdisplays information about the specified open file\n")); + + net_common_methods_usage(c, argc, argv); + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_file(struct net_context *c, int argc, const char **argv) +{ + if (argc < 1) + return net_file_usage(c, argc, argv); + + if (strcasecmp_m(argv[0], "HELP") == 0) { + net_file_usage(c, argc, argv); + return 0; + } + + if (net_rpc_check(c, 0)) + return net_rpc_file(c, argc, argv); + return net_rap_file(c, argc, argv); +} + + diff --git a/source3/utils/net_g_lock.c b/source3/utils/net_g_lock.c new file mode 100644 index 0000000..a100028 --- /dev/null +++ b/source3/utils/net_g_lock.c @@ -0,0 +1,267 @@ +/* + * Samba Unix/Linux SMB client library + * Interface to the g_lock facility + * Copyright (C) Volker Lendecke 2009 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "net.h" +#include "lib/util/server_id.h" +#include "g_lock.h" +#include "messages.h" +#include "lib/util/util_tdb.h" + +static bool net_g_lock_init(TALLOC_CTX *mem_ctx, + struct tevent_context **pev, + struct messaging_context **pmsg, + struct g_lock_ctx **pg_ctx) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *g_ctx = NULL; + + ev = samba_tevent_context_init(mem_ctx); + if (ev == NULL) { + d_fprintf(stderr, "ERROR: could not init event context\n"); + goto fail; + } + msg = messaging_init(mem_ctx, ev); + if (msg == NULL) { + d_fprintf(stderr, "ERROR: could not init messaging context\n"); + goto fail; + } + g_ctx = g_lock_ctx_init(mem_ctx, msg); + if (g_ctx == NULL) { + d_fprintf(stderr, "ERROR: could not init g_lock context\n"); + goto fail; + } + + *pev = ev; + *pmsg = msg; + *pg_ctx = g_ctx; + return true; +fail: + TALLOC_FREE(g_ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return false; +} + +static int net_g_lock_do(struct net_context *c, int argc, const char **argv) +{ + struct g_lock_ctx *ctx = NULL; + TDB_DATA key = {0}; + const char *cmd = NULL; + int timeout; + NTSTATUS status; + int result = -1; + + if (argc != 3) { + d_printf("Usage: net g_lock do <lockname> <timeout> " + "<command>\n"); + return -1; + } + key = string_term_tdb_data(argv[0]); + timeout = atoi(argv[1]); + cmd = argv[2]; + + ctx = g_lock_ctx_init(c, c->msg_ctx); + if (ctx == NULL) { + d_fprintf(stderr, _("g_lock_ctx_init failed\n")); + return -1; + } + status = g_lock_lock( + ctx, + key, + G_LOCK_WRITE, + timeval_set(timeout / 1000, timeout % 1000), + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("g_lock_lock failed: %s\n"), + nt_errstr(status)); + goto done; + } + + result = system(cmd); + + g_lock_unlock(ctx, key); + + if (result == -1) { + d_fprintf(stderr, "ERROR: system() returned %s\n", + strerror(errno)); + goto done; + } + d_fprintf(stderr, "command returned %d\n", result); + +done: + TALLOC_FREE(ctx); + return result; +} + +static void net_g_lock_dump_fn(struct server_id exclusive, + size_t num_shared, + const struct server_id *shared, + const uint8_t *data, + size_t datalen, + void *private_data) +{ + struct server_id_buf idbuf; + + if (exclusive.pid != 0) { + d_printf("%s: WRITE\n", + server_id_str_buf(exclusive, &idbuf)); + } else { + size_t i; + for (i=0; i<num_shared; i++) { + d_printf("%s: READ\n", + server_id_str_buf(shared[i], &idbuf)); + } + } + dump_data_file(data, datalen, true, stdout); +} + +static int net_g_lock_dump(struct net_context *c, int argc, const char **argv) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *g_ctx = NULL; + int ret = -1; + + if (argc != 1) { + d_printf("Usage: net g_lock dump <lockname>\n"); + return -1; + } + + if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) { + goto done; + } + + (void)g_lock_dump(g_ctx, string_term_tdb_data(argv[0]), + net_g_lock_dump_fn, NULL); + + ret = 0; +done: + TALLOC_FREE(g_ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret; +} + +static int net_g_lock_dumpall_fn(TDB_DATA key, void *private_data) +{ + struct g_lock_ctx *g_ctx = talloc_get_type_abort( + private_data, struct g_lock_ctx); + + dump_data_file(key.dptr, key.dsize, true, stdout); + g_lock_dump(g_ctx, key, net_g_lock_dump_fn, NULL); + printf("\n"); + + return 0; +} + +static int net_g_lock_dumpall( + struct net_context *c, int argc, const char **argv) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *g_ctx = NULL; + int ret = -1; + + if (argc != 0) { + d_printf("Usage: net g_lock locks\n"); + return -1; + } + + if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) { + goto done; + } + + ret = g_lock_locks(g_ctx, net_g_lock_dumpall_fn, g_ctx); +done: + TALLOC_FREE(g_ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret < 0 ? -1 : ret; +} + +static int net_g_lock_locks_fn(TDB_DATA key, void *private_data) +{ + dump_data_file(key.dptr, key.dsize, true, stdout); + return 0; +} + +static int net_g_lock_locks(struct net_context *c, int argc, const char **argv) +{ + struct tevent_context *ev = NULL; + struct messaging_context *msg = NULL; + struct g_lock_ctx *g_ctx = NULL; + int ret = -1; + + if (argc != 0) { + d_printf("Usage: net g_lock locks\n"); + return -1; + } + + if (!net_g_lock_init(talloc_tos(), &ev, &msg, &g_ctx)) { + goto done; + } + + ret = g_lock_locks(g_ctx, net_g_lock_locks_fn, NULL); +done: + TALLOC_FREE(g_ctx); + TALLOC_FREE(msg); + TALLOC_FREE(ev); + return ret < 0 ? -1 : ret; +} + +int net_g_lock(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "do", + net_g_lock_do, + NET_TRANSPORT_LOCAL, + N_("Execute a shell command under a lock"), + N_("net g_lock do <lock name> <timeout> <command>\n") + }, + { + "locks", + net_g_lock_locks, + NET_TRANSPORT_LOCAL, + N_("List all locknames"), + N_("net g_lock locks\n") + }, + { + "dump", + net_g_lock_dump, + NET_TRANSPORT_LOCAL, + N_("Dump a g_lock locking table"), + N_("net g_lock dump <lock name>\n") + }, + { + "dumpall", + net_g_lock_dumpall, + NET_TRANSPORT_LOCAL, + N_("Dump all g_lock locking tables"), + N_("net g_lock dumpall\n") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net g_lock", func); +} diff --git a/source3/utils/net_group.c b/source3/utils/net_group.c new file mode 100644 index 0000000..505856a --- /dev/null +++ b/source3/utils/net_group.c @@ -0,0 +1,68 @@ +/* + Samba Unix/Linux SMB client library + net group commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2002 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_group_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net [<method>] group [misc. options] [targets]" + "\n\tList user groups\n\n")); + d_printf(_("net rpc group LIST [global|local|builtin]* [misc. options]" + "\n\tList specific user groups\n\n")); + d_printf(_("net [<method>] group DELETE <name> " + "[misc. options] [targets]" + "\n\tDelete specified group\n")); + d_printf(_("\nnet [<method>] group ADD <name> [-C comment] " + "[-c container] [misc. options] [targets]\n" + "\tCreate specified group\n")); + d_printf(_("\nnet rpc group MEMBERS <name>\n\tList Group Members\n\n")); + d_printf(_("\nnet rpc group ADDMEM <group> <member>\n" + "\tAdd Group Members\n\n")); + d_printf(_("\nnet rpc group DELMEM <group> <member>\n" + "\tDelete Group Members\n\n")); + net_common_methods_usage(c, argc, argv); + net_common_flags_usage(c, argc, argv); + d_printf(_("\t-C or --comment=<comment>\tdescriptive comment " + "(for add only)\n")); + d_printf(_("\t-c or --container=<container>\tLDAP container, " + "defaults to cn=Users (for add in ADS only)\n")); + d_printf(_("\t-L or --localgroup\t\tWhen adding groups, " + "create a local group (alias)\n")); + return -1; +} + +int net_group(struct net_context *c, int argc, const char **argv) +{ + if (argc < 1) + return net_group_usage(c, argc, argv); + + if (strcasecmp_m(argv[0], "HELP") == 0) { + net_group_usage(c, argc, argv); + return 0; + } + + if (net_ads_check(c) == 0) + return net_ads_group(c, argc, argv); + + return net_rap_group(c, argc, argv); +} + diff --git a/source3/utils/net_groupmap.c b/source3/utils/net_groupmap.c new file mode 100644 index 0000000..4f36d45 --- /dev/null +++ b/source3/utils/net_groupmap.c @@ -0,0 +1,1023 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-2000, + * Copyright (C) Jean François Micouleau 1998-2001. + * Copyright (C) Gerald Carter 2003, + * Copyright (C) Volker Lendecke 2004 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "includes.h" +#include "system/passwd.h" +#include "utils/net.h" +#include "../libcli/security/security.h" +#include "passdb.h" +#include "lib/util/string_wrappers.h" + +/********************************************************* + Figure out if the input was an NT group or a SID string. + Return the SID. +**********************************************************/ +static bool get_sid_from_input(struct dom_sid *sid, char *input) +{ + GROUP_MAP *map; + + map = talloc_zero(NULL, GROUP_MAP); + if (!map) { + return false; + } + + if (strncasecmp_m( input, "S-", 2)) { + /* Perhaps its the NT group name? */ + if (!pdb_getgrnam(map, input)) { + printf(_("NT Group %s doesn't exist in mapping DB\n"), + input); + TALLOC_FREE(map); + return false; + } else { + *sid = map->sid; + } + } else { + if (!string_to_sid(sid, input)) { + printf(_("converting sid %s from a string failed!\n"), + input); + TALLOC_FREE(map); + return false; + } + } + TALLOC_FREE(map); + return true; +} + +/********************************************************* + Dump a GROUP_MAP entry to stdout (long or short listing) +**********************************************************/ + +static void print_map_entry (const GROUP_MAP *map, bool long_list) +{ + struct dom_sid_buf buf; + + if (!long_list) + d_printf("%s (%s) -> %s\n", map->nt_name, + dom_sid_str_buf(&map->sid, &buf), + gidtoname(map->gid)); + else { + d_printf("%s\n", map->nt_name); + d_printf(_("\tSID : %s\n"), + dom_sid_str_buf(&map->sid, &buf)); + d_printf(_("\tUnix gid : %u\n"), (unsigned int)map->gid); + d_printf(_("\tUnix group: %s\n"), gidtoname(map->gid)); + d_printf(_("\tGroup type: %s\n"), + sid_type_lookup(map->sid_name_use)); + d_printf(_("\tComment : %s\n"), map->comment); + } + +} +/********************************************************* + List the groups. +**********************************************************/ +static int net_groupmap_list(struct net_context *c, int argc, const char **argv) +{ + size_t entries; + bool long_list = false; + size_t i; + fstring ntgroup = ""; + fstring sid_string = ""; + const char list_usage_str[] = N_("net groupmap list [verbose] " + "[ntgroup=NT group] [sid=SID]\n" + " verbose\tPrint verbose list\n" + " ntgroup\tNT group to list\n" + " sid\tSID of group to list"); + + if (c->display_usage) { + d_printf("%s\n%s\n", _("Usage: "), list_usage_str); + return 0; + } + + if (c->opt_verbose || c->opt_long_list_entries) + long_list = true; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !strcasecmp_m(argv[i], "verbose")) { + long_list = true; + } + else if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_fprintf(stderr, _("must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_fprintf(stderr, _("must supply a SID\n")); + return -1; + } + } + else { + d_fprintf(stderr, _("Bad option: %s\n"), argv[i]); + d_printf("%s\n%s\n", _("Usage:"), list_usage_str); + return -1; + } + } + + /* list a single group is given a name */ + if ( ntgroup[0] || sid_string[0] ) { + struct dom_sid sid; + GROUP_MAP *map; + + if ( sid_string[0] ) + strlcpy(ntgroup, sid_string, sizeof(ntgroup)); + + if (!get_sid_from_input(&sid, ntgroup)) { + return -1; + } + + map = talloc_zero(NULL, GROUP_MAP); + if (!map) { + return -1; + } + + /* Get the current mapping from the database */ + if(!pdb_getgrsid(map, sid)) { + d_fprintf(stderr, + _("Failure to find local group SID in the " + "database\n")); + TALLOC_FREE(map); + return -1; + } + + print_map_entry(map, long_list ); + TALLOC_FREE(map); + } + else { + GROUP_MAP **maps = NULL; + bool ok = false; + /* enumerate all group mappings */ + ok = pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN, + &maps, &entries, + ENUM_ALL_MAPPED); + if (!ok) { + return -1; + } + + for (i=0; i<entries; i++) { + print_map_entry(maps[i], long_list); + } + + TALLOC_FREE(maps); + } + + return 0; +} + +/********************************************************* + Add a new group mapping entry +**********************************************************/ + +static int net_groupmap_add(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + fstring ntgroup = ""; + fstring unixgrp = ""; + fstring string_sid = ""; + fstring type = ""; + fstring ntcomment = ""; + enum lsa_SidType sid_type = SID_NAME_DOM_GRP; + uint32_t rid = 0; + gid_t gid; + int i; + GROUP_MAP *map; + + const char *name_type; + const char add_usage_str[] = N_("net groupmap add " + "{rid=<int>|sid=<string>}" + " unixgroup=<string> " + "[type=<domain|local|builtin>] " + "[ntgroup=<string>] " + "[comment=<string>]"); + + name_type = "domain group"; + + if (c->display_usage) { + d_printf("%s\n%s\n", _("Usage:\n"), add_usage_str); + return 0; + } + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !strncasecmp_m(argv[i], "rid", strlen("rid")) ) { + rid = get_int_param(argv[i]); + if ( rid < DOMAIN_RID_ADMINS ) { + d_fprintf(stderr, + _("RID must be greater than %d\n"), + (uint32_t)DOMAIN_RID_ADMINS-1); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "unixgroup", strlen("unixgroup")) ) { + fstrcpy( unixgrp, get_string_param( argv[i] ) ); + if ( !unixgrp[0] ) { + d_fprintf(stderr,_( "must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_fprintf(stderr, _("must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) { + fstrcpy( string_sid, get_string_param( argv[i] ) ); + if ( !string_sid[0] ) { + d_fprintf(stderr, _("must supply a SID\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "comment", strlen("comment")) ) { + fstrcpy( ntcomment, get_string_param( argv[i] ) ); + if ( !ntcomment[0] ) { + d_fprintf(stderr, + _("must supply a comment string\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "type", strlen("type")) ) { + fstrcpy( type, get_string_param( argv[i] ) ); + switch ( type[0] ) { + case 'b': + case 'B': + sid_type = SID_NAME_WKN_GRP; + name_type = "wellknown group"; + break; + case 'd': + case 'D': + sid_type = SID_NAME_DOM_GRP; + name_type = "domain group"; + break; + case 'l': + case 'L': + sid_type = SID_NAME_ALIAS; + name_type = "alias (local) group"; + break; + default: + d_fprintf(stderr, + _("unknown group type %s\n"), + type); + return -1; + } + } + else { + d_fprintf(stderr, _("Bad option: %s\n"), argv[i]); + return -1; + } + } + + if ( !unixgrp[0] ) { + d_printf("%s\n%s\n", _("Usage:\n"), add_usage_str); + return -1; + } + + if ( (gid = nametogid(unixgrp)) == (gid_t)-1 ) { + d_fprintf(stderr, _("Can't lookup UNIX group %s\n"), unixgrp); + return -1; + } + + map = talloc_zero(NULL, GROUP_MAP); + if (!map) { + return -1; + } + /* Default is domain group. */ + map->sid_name_use = SID_NAME_DOM_GRP; + if (pdb_getgrgid(map, gid)) { + struct dom_sid_buf buf; + d_printf(_("Unix group %s already mapped to SID %s\n"), + unixgrp, dom_sid_str_buf(&map->sid, &buf)); + TALLOC_FREE(map); + return -1; + } + TALLOC_FREE(map); + + if ( (rid == 0) && (string_sid[0] == '\0') ) { + d_printf(_("No rid or sid specified, choosing a RID\n")); + if (pdb_capabilities() & PDB_CAP_STORE_RIDS) { + if (!pdb_new_rid(&rid)) { + d_printf(_("Could not get new RID\n")); + } + } else { + rid = algorithmic_pdb_gid_to_group_rid(gid); + } + d_printf(_("Got RID %d\n"), rid); + } + + /* append the rid to our own domain/machine SID if we don't have a full SID */ + if ( !string_sid[0] ) { + sid_compose(&sid, get_global_sam_sid(), rid); + sid_to_fstring(string_sid, &sid); + } + + if (!ntcomment[0]) { + switch (sid_type) { + case SID_NAME_WKN_GRP: + fstrcpy(ntcomment, "Wellknown Unix group"); + break; + case SID_NAME_DOM_GRP: + fstrcpy(ntcomment, "Domain Unix group"); + break; + case SID_NAME_ALIAS: + fstrcpy(ntcomment, "Local Unix group"); + break; + default: + fstrcpy(ntcomment, "Unix group"); + break; + } + } + + if (!ntgroup[0] ) + strlcpy(ntgroup, unixgrp, sizeof(ntgroup)); + + if (!NT_STATUS_IS_OK(add_initial_entry(gid, string_sid, sid_type, ntgroup, ntcomment))) { + d_fprintf(stderr, _("adding entry for group %s failed!\n"), ntgroup); + return -1; + } + + d_printf(_("Successfully added group %s to the mapping db as a %s\n"), + ntgroup, name_type); + return 0; +} + +static int net_groupmap_modify(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + GROUP_MAP *map = NULL; + fstring ntcomment = ""; + fstring type = ""; + fstring ntgroup = ""; + fstring unixgrp = ""; + fstring sid_string = ""; + enum lsa_SidType sid_type = SID_NAME_UNKNOWN; + int i; + gid_t gid; + const char modify_usage_str[] = N_("net groupmap modify " + "{ntgroup=<string>|sid=<SID>} " + "[comment=<string>] " + "[unixgroup=<string>] " + "[type=<domain|local>]"); + + if (c->display_usage) { + d_printf("%s\n%s\n", _("Usage:\n"), modify_usage_str); + return 0; + } + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_fprintf(stderr, _("must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_fprintf(stderr, _("must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "comment", strlen("comment")) ) { + fstrcpy( ntcomment, get_string_param( argv[i] ) ); + if ( !ntcomment[0] ) { + d_fprintf(stderr, + _("must supply a comment string\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "unixgroup", strlen("unixgroup")) ) { + fstrcpy( unixgrp, get_string_param( argv[i] ) ); + if ( !unixgrp[0] ) { + d_fprintf(stderr, + _("must supply a group name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "type", strlen("type")) ) { + fstrcpy( type, get_string_param( argv[i] ) ); + switch ( type[0] ) { + case 'd': + case 'D': + sid_type = SID_NAME_DOM_GRP; + break; + case 'l': + case 'L': + sid_type = SID_NAME_ALIAS; + break; + } + } + else { + d_fprintf(stderr, _("Bad option: %s\n"), argv[i]); + return -1; + } + } + + if ( !ntgroup[0] && !sid_string[0] ) { + d_printf("%s\n%s\n", _("Usage:\n"), modify_usage_str); + return -1; + } + + /* give preference to the SID; if both the ntgroup name and SID + are defined, use the SID and assume that the group name could be a + new name */ + + if ( sid_string[0] ) { + if (!get_sid_from_input(&sid, sid_string)) { + return -1; + } + } + else { + if (!get_sid_from_input(&sid, ntgroup)) { + return -1; + } + } + + map = talloc_zero(NULL, GROUP_MAP); + if (!map) { + return -1; + } + + /* Get the current mapping from the database */ + if(!pdb_getgrsid(map, sid)) { + d_fprintf(stderr, + _("Failed to find local group SID in the database\n")); + TALLOC_FREE(map); + return -1; + } + + /* + * Allow changing of group type only between domain and local + * We disallow changing Builtin groups !!! (SID problem) + */ + if (sid_type == SID_NAME_UNKNOWN) { + d_fprintf(stderr, _("Can't map to an unknown group type.\n")); + TALLOC_FREE(map); + return -1; + } + + if (map->sid_name_use == SID_NAME_WKN_GRP) { + d_fprintf(stderr, + _("You can only change between domain and local " + "groups.\n")); + TALLOC_FREE(map); + return -1; + } + + map->sid_name_use = sid_type; + + /* Change comment if new one */ + if (ntcomment[0]) { + map->comment = talloc_strdup(map, ntcomment); + if (!map->comment) { + d_fprintf(stderr, _("Out of memory!\n")); + return -1; + } + } + + if (ntgroup[0]) { + map->nt_name = talloc_strdup(map, ntgroup); + if (!map->nt_name) { + d_fprintf(stderr, _("Out of memory!\n")); + return -1; + } + } + + if ( unixgrp[0] ) { + gid = nametogid( unixgrp ); + if ( gid == -1 ) { + d_fprintf(stderr, _("Unable to lookup UNIX group %s. " + "Make sure the group exists.\n"), + unixgrp); + TALLOC_FREE(map); + return -1; + } + + map->gid = gid; + } + + if (!NT_STATUS_IS_OK(pdb_update_group_mapping_entry(map))) { + d_fprintf(stderr, _("Could not update group database\n")); + TALLOC_FREE(map); + return -1; + } + + d_printf(_("Updated mapping entry for %s\n"), map->nt_name); + + TALLOC_FREE(map); + return 0; +} + +static int net_groupmap_delete(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + fstring ntgroup = ""; + fstring sid_string = ""; + int i; + const char delete_usage_str[] = N_("net groupmap delete " + "{ntgroup=<string>|sid=<SID>}"); + + if (c->display_usage) { + d_printf("%s\n%s\n", _("Usage:\n"), delete_usage_str); + return 0; + } + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !strncasecmp_m(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_fprintf(stderr, _("must supply a name\n")); + return -1; + } + } + else if ( !strncasecmp_m(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_fprintf(stderr, _("must supply a SID\n")); + return -1; + } + } + else { + d_fprintf(stderr, _("Bad option: %s\n"), argv[i]); + return -1; + } + } + + if ( !ntgroup[0] && !sid_string[0]) { + d_printf("%s\n%s\n", _("Usage:\n"), delete_usage_str); + return -1; + } + + /* give preference to the SID if we have that */ + + if ( sid_string[0] ) + strlcpy(ntgroup, sid_string, sizeof(ntgroup)); + + if ( !get_sid_from_input(&sid, ntgroup) ) { + d_fprintf(stderr, _("Unable to resolve group %s to a SID\n"), + ntgroup); + return -1; + } + + if ( !NT_STATUS_IS_OK(pdb_delete_group_mapping_entry(sid)) ) { + d_fprintf(stderr, + _("Failed to remove group %s from the mapping db!\n"), + ntgroup); + return -1; + } + + d_printf(_("Successfully removed %s from the mapping db\n"), ntgroup); + + return 0; +} + +static int net_groupmap_set(struct net_context *c, int argc, const char **argv) +{ + const char *ntgroup = NULL; + struct group *grp = NULL; + GROUP_MAP *map; + bool have_map = false; + + if ((argc < 1) || (argc > 2) || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _(" net groupmap set \"NT Group\" " + "[\"unix group\"] [-C \"comment\"] [-L] [-D]\n")); + return -1; + } + + if ( c->opt_localgroup && c->opt_domaingroup ) { + d_printf(_("Can only specify -L or -D, not both\n")); + return -1; + } + + ntgroup = argv[0]; + + if (argc == 2) { + grp = getgrnam(argv[1]); + + if (grp == NULL) { + d_fprintf(stderr, _("Could not find unix group %s\n"), + argv[1]); + return -1; + } + } + + map = talloc_zero(NULL, GROUP_MAP); + if (!map) { + d_printf(_("Out of memory!\n")); + return -1; + } + + have_map = pdb_getgrnam(map, ntgroup); + + if (!have_map) { + struct dom_sid sid; + have_map = ( (strncmp(ntgroup, "S-", 2) == 0) && + string_to_sid(&sid, ntgroup) && + pdb_getgrsid(map, sid) ); + } + + if (!have_map) { + + /* Ok, add it */ + + if (grp == NULL) { + d_fprintf(stderr, + _("Could not find group mapping for %s\n"), + ntgroup); + TALLOC_FREE(map); + return -1; + } + + map->gid = grp->gr_gid; + + if (c->opt_rid == 0) { + if ( pdb_capabilities() & PDB_CAP_STORE_RIDS ) { + if ( !pdb_new_rid((uint32_t *)&c->opt_rid) ) { + d_fprintf( stderr, + _("Could not allocate new RID\n")); + TALLOC_FREE(map); + return -1; + } + } else { + c->opt_rid = algorithmic_pdb_gid_to_group_rid(map->gid); + } + } + + sid_compose(&map->sid, get_global_sam_sid(), c->opt_rid); + + map->sid_name_use = SID_NAME_DOM_GRP; + map->nt_name = talloc_strdup(map, ntgroup); + map->comment = talloc_strdup(map, ""); + if (!map->nt_name || !map->comment) { + d_printf(_("Out of memory!\n")); + TALLOC_FREE(map); + return -1; + } + + if (!NT_STATUS_IS_OK(pdb_add_group_mapping_entry(map))) { + d_fprintf(stderr, + _("Could not add mapping entry for %s\n"), + ntgroup); + TALLOC_FREE(map); + return -1; + } + } + + /* Now we have a mapping entry, update that stuff */ + + if ( c->opt_localgroup || c->opt_domaingroup ) { + if (map->sid_name_use == SID_NAME_WKN_GRP) { + d_fprintf(stderr, + _("Can't change type of the BUILTIN " + "group %s\n"), + map->nt_name); + TALLOC_FREE(map); + return -1; + } + } + + if (c->opt_localgroup) + map->sid_name_use = SID_NAME_ALIAS; + + if (c->opt_domaingroup) + map->sid_name_use = SID_NAME_DOM_GRP; + + /* The case (opt_domaingroup && opt_localgroup) was tested for above */ + + if ((c->opt_comment != NULL) && (strlen(c->opt_comment) > 0)) { + map->comment = talloc_strdup(map, c->opt_comment); + if (!map->comment) { + d_printf(_("Out of memory!\n")); + TALLOC_FREE(map); + return -1; + } + } + + if ((c->opt_newntname != NULL) && (strlen(c->opt_newntname) > 0)) { + map->nt_name = talloc_strdup(map, c->opt_newntname); + if (!map->nt_name) { + d_printf(_("Out of memory!\n")); + TALLOC_FREE(map); + return -1; + } + } + + if (grp != NULL) + map->gid = grp->gr_gid; + + if (!NT_STATUS_IS_OK(pdb_update_group_mapping_entry(map))) { + d_fprintf(stderr, _("Could not update group mapping for %s\n"), + ntgroup); + TALLOC_FREE(map); + return -1; + } + + TALLOC_FREE(map); + return 0; +} + +static int net_groupmap_cleanup(struct net_context *c, int argc, const char **argv) +{ + GROUP_MAP **maps = NULL; + size_t i, entries; + + if (c->display_usage) { + d_printf( "%s\n" + "net groupmap cleanup\n" + " %s\n", + _("Usage:"), + _("Delete all group mappings")); + return 0; + } + + if (!pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN, &maps, &entries, + ENUM_ALL_MAPPED)) { + d_fprintf(stderr, _("Could not list group mappings\n")); + return -1; + } + + for (i=0; i<entries; i++) { + + if (maps[i]->gid == -1) + printf(_("Group %s is not mapped\n"), + maps[i]->nt_name); + + if (!sid_check_is_in_our_sam(&maps[i]->sid) && + !sid_check_is_in_builtin(&maps[i]->sid)) + { + struct dom_sid_buf buf; + printf(_("Deleting mapping for NT Group %s, sid %s\n"), + maps[i]->nt_name, + dom_sid_str_buf(&maps[i]->sid, &buf)); + pdb_delete_group_mapping_entry(maps[i]->sid); + } + } + + TALLOC_FREE(maps); + return 0; +} + +static int net_groupmap_addmem(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid alias, member; + + if ( (argc != 2) || + c->display_usage || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("%s\n%s", + _("Usage:"), + _("net groupmap addmem alias-sid member-sid\n")); + return -1; + } + + if (!NT_STATUS_IS_OK(pdb_add_aliasmem(&alias, &member))) { + d_fprintf(stderr, _("Could not add sid %s to alias %s\n"), + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_delmem(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid alias, member; + + if ( (argc != 2) || + c->display_usage || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("%s\n%s", + _("Usage:"), + _("net groupmap delmem alias-sid member-sid\n")); + return -1; + } + + if (!NT_STATUS_IS_OK(pdb_del_aliasmem(&alias, &member))) { + d_fprintf(stderr, _("Could not delete sid %s from alias %s\n"), + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_listmem(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid alias; + struct dom_sid *members; + size_t i, num; + + if ( (argc != 1) || + c->display_usage || + !string_to_sid(&alias, argv[0]) ) { + d_printf("%s\n%s", + _("Usage:"), + _("net groupmap listmem alias-sid\n")); + return -1; + } + + members = NULL; + num = 0; + + if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(&alias, talloc_tos(), + &members, &num))) { + d_fprintf(stderr, _("Could not list members for sid %s\n"), + argv[0]); + return -1; + } + + for (i = 0; i < num; i++) { + struct dom_sid_buf buf; + printf("%s\n", dom_sid_str_buf(&(members[i]), &buf)); + } + + TALLOC_FREE(members); + + return 0; +} + +static bool print_alias_memberships(TALLOC_CTX *mem_ctx, + const struct dom_sid *domain_sid, + const struct dom_sid *member) +{ + uint32_t *alias_rids; + size_t i, num_alias_rids; + struct dom_sid_buf buf; + + alias_rids = NULL; + num_alias_rids = 0; + + if (!NT_STATUS_IS_OK(pdb_enum_alias_memberships( + mem_ctx, domain_sid, member, 1, + &alias_rids, &num_alias_rids))) { + d_fprintf(stderr, _("Could not list memberships for sid %s\n"), + dom_sid_str_buf(member, &buf)); + return false; + } + + for (i = 0; i < num_alias_rids; i++) { + struct dom_sid alias; + sid_compose(&alias, domain_sid, alias_rids[i]); + printf("%s\n", dom_sid_str_buf(&alias, &buf)); + } + + return true; +} + +static int net_groupmap_memberships(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + struct dom_sid *domain_sid, member; + + if ( (argc != 1) || + c->display_usage || + !string_to_sid(&member, argv[0]) ) { + d_printf("%s\n%s", + _("Usage:"), + _("net groupmap memberships sid\n")); + return -1; + } + + mem_ctx = talloc_init("net_groupmap_memberships"); + if (mem_ctx == NULL) { + d_fprintf(stderr, _("talloc_init failed\n")); + return -1; + } + + domain_sid = get_global_sam_sid(); + if (domain_sid == NULL) { + d_fprintf(stderr, _("Could not get domain sid\n")); + return -1; + } + + if (!print_alias_memberships(mem_ctx, domain_sid, &member) || + !print_alias_memberships(mem_ctx, &global_sid_Builtin, &member)) + return -1; + + talloc_destroy(mem_ctx); + + return 0; +} + +/*********************************************************** + migrated functionality from smbgroupedit + **********************************************************/ +int net_groupmap(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + net_groupmap_add, + NET_TRANSPORT_LOCAL, + N_("Create a new group mapping"), + N_("net groupmap add\n" + " Create a new group mapping") + }, + { + "modify", + net_groupmap_modify, + NET_TRANSPORT_LOCAL, + N_("Update a group mapping"), + N_("net groupmap modify\n" + " Modify an existing group mapping") + }, + { + "delete", + net_groupmap_delete, + NET_TRANSPORT_LOCAL, + N_("Remove a group mapping"), + N_("net groupmap delete\n" + " Remove a group mapping") + }, + { + "set", + net_groupmap_set, + NET_TRANSPORT_LOCAL, + N_("Set group mapping"), + N_("net groupmap set\n" + " Set a group mapping") + }, + { + "cleanup", + net_groupmap_cleanup, + NET_TRANSPORT_LOCAL, + N_("Remove foreign group mapping entries"), + N_("net groupmap cleanup\n" + " Remove foreign group mapping entries") + }, + { + "addmem", + net_groupmap_addmem, + NET_TRANSPORT_LOCAL, + N_("Add a foreign alias member"), + N_("net groupmap addmem\n" + " Add a foreign alias member") + }, + { + "delmem", + net_groupmap_delmem, + NET_TRANSPORT_LOCAL, + N_("Delete foreign alias member"), + N_("net groupmap delmem\n" + " Delete foreign alias member") + }, + { + "listmem", + net_groupmap_listmem, + NET_TRANSPORT_LOCAL, + N_("List foreign group members"), + N_("net groupmap listmem\n" + " List foreign alias members") + }, + { + "memberships", + net_groupmap_memberships, + NET_TRANSPORT_LOCAL, + N_("List foreign group memberships"), + N_("net groupmap memberships\n" + " List foreign group memberships") + }, + { + "list", + net_groupmap_list, + NET_TRANSPORT_LOCAL, + N_("List current group map"), + N_("net groupmap list\n" + " List current group map") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c,argc, argv, "net groupmap", func); +} + diff --git a/source3/utils/net_help.c b/source3/utils/net_help.c new file mode 100644 index 0000000..4aba1c5 --- /dev/null +++ b/source3/utils/net_help.c @@ -0,0 +1,69 @@ +/* + Samba Unix/Linux SMB client library + net help commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +static int net_usage(struct net_context *c, int argc, const char **argv); + +static int net_help_usage(struct net_context *c, int argc, const char **argv) +{ + c->display_usage = true; + return net_usage(c, argc, argv); +} + +static int net_usage(struct net_context *c, int argc, const char **argv) +{ + struct functable *table = (struct functable*) c->private_data; + int i; + + d_printf(_("Usage:\n")); + for (i=0; table[i].funcname != NULL; i++) { + if (c->display_usage) { + d_printf(_("net %s usage:\n"), table[i].funcname); + d_printf("\n%s\n\n", _(table[i].usage)); + } else { + d_printf("%s %-15s %s\n", "net", table[i].funcname, + _(table[i].description)); + } + + } + + net_common_flags_usage(c, argc, argv); + return -1; +} + +/* + handle "net help *" subcommands +*/ +int net_help(struct net_context *c, int argc, const char **argv) +{ + struct functable *func = (struct functable *)c->private_data; + + if (argc == 0) { + return net_usage(c, argc, argv); + } + + if (strcasecmp_m(argv[0], "help") == 0) { + return net_help_usage(c, argc, argv); + } + + c->display_usage = true; + return net_run_function(c, argc, argv, "net help", func); +} diff --git a/source3/utils/net_help_common.c b/source3/utils/net_help_common.c new file mode 100644 index 0000000..a861d3f --- /dev/null +++ b/source3/utils/net_help_common.c @@ -0,0 +1,95 @@ +/* + Samba Unix/Linux SMB client library + net help commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_common_methods_usage(struct net_context *c, int argc, const char**argv) +{ + d_printf(_("Valid methods: (auto-detected if not specified)\n")); + d_printf(_("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n")); + d_printf(_("\trpc\t\t\t\tDCE-RPC\n")); + d_printf(_("\trap\t\t\t\tRAP (older systems)\n")); + d_printf("\n"); + return 0; +} + +int net_common_flags_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("Valid targets: choose one (none defaults to localhost)\n")); + d_printf(_("\t-S|--server=<server>\t\t\tserver name\n")); + d_printf(_("\t-I|--ipaddress=<ipaddr>\t\t\taddress of target server\n")); + d_printf(_("\t-w|--target-workgroup=<wg>\t\ttarget workgroup or domain\n")); + + d_printf("\n"); + d_printf(_("Valid misc options are:\n")); /* misc options */ + d_printf(_("\t-p|--port=<port>\t\t\tconnection port on target\n")); + d_printf(_("\t--myname=<name>\t\t\t\tclient name\n")); + d_printf(_("\t--long\t\t\t\t\tDisplay full information\n")); + + d_printf("\n"); + d_printf(_("Valid common options are:\n")); /* misc options */ + d_printf(_("\t-d|--debuglevel=<level>\t\t\tdebug level (0-10)\n")); + d_printf(_("\t--debug-stdout\t\t\t\tSend debug output to standard " + "output\n")); + d_printf(_("\t--configfile=<path>\t\t\tpathname of smb.conf file\n")); + d_printf(_("\t--option=name=value\t\t\tSet smb.conf option from " + "command line\n")); + d_printf(_("\t-l|--log-basename=LOGFILEBASE\t\tBasename for " + "log/debug files\n")); + d_printf(_("\t--leak-report\t\t\t\tenable talloc leak reporting on " + "exit\n")); + d_printf(_("\t--leak-report-full\t\t\tenable full talloc leak " + "reporting on exit\n")); + d_printf(_("\t-V|--version\t\t\t\tPrint samba version information\n")); + + d_printf("\n"); + d_printf(_("Valid connection options are:\n")); /* misc options */ + d_printf(_("\t-R|--name-resolve=NAME-RESOLVE-ORDER\tUse these name " + "resolution services only\n")); + d_printf(_("\t-O|--socket-options=SOCKETOPTIONS\tsocket options to use\n")); + d_printf(_("\t-m|--max-protocol=MAXPROTOCOL\t\tSet max protocol level\n")); + d_printf(_("\t-n|--netbiosname=NETBIOSNAME\t\tPrimary netbios name\n")); + d_printf(_("\t--netbios-scope=SCOPE\t\t\tUse this Netbios scope\n")); + d_printf(_("\t-W|--workgroup=WORKGROUP\t\tSet the workgroup name\n")); + d_printf(_("\t--realm=REALM\t\t\t\tSet the realm name\n")); + + d_printf("\n"); + d_printf(_("Valid credential options are:\n")); /* misc options */ + d_printf(_("\t-U|--user=[DOMAIN/]USERNAME[%%PASSWORD]\tSet the " + "network username\n")); + d_printf(_("\t-N|--no-pass\t\t\t\tDon't ask for a password\n")); + d_printf(_("\t--password=STRING\t\t\tSet a password\n")); + d_printf(_("\t--pw-nt-hash\t\t\t\tThe supplied password is the NT hash\n")); + d_printf(_("\t-A|--authentication-file=FILE\t\tGet the " + "credentials from a file\n")); + d_printf(_("\t-P|--machine-pass\t\t\tUse stored machine account password\n")); + d_printf(_("\t--simple-bind-dn=DN\t\t\tDN to use for a simple bind\n")); + d_printf(_("\t--use-kerberos=desired|required|off\tUse kerberos " + "authentication\n")); + d_printf(_("\t--use-krb5-ccache=CCACHE\t\tCredentials cache location " + "for Kerberos\n")); + d_printf(_("\t--use-winbind-ccache\t\t\tUse the winbind ccache for " + "authentication\n")); + d_printf(_("\t--client-protection=sign|encrypt|off\tConfigure used " + "protection for client connections\n")); + + return -1; +} + diff --git a/source3/utils/net_help_common.h b/source3/utils/net_help_common.h new file mode 100644 index 0000000..ed85993 --- /dev/null +++ b/source3/utils/net_help_common.h @@ -0,0 +1,49 @@ +/* + Samba Unix/Linux SMB client library + net help commands + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef _NET_HELP_COMMON_H_ +#define _NET_HELP_COMMON_H_ + +/** + * Get help for common methods. + * + * This will output some help for using the ADS/RPC/RAP transports. + * + * @param c A net_context structure + * @param argc Normal argc with previous parameters removed + * @param argv Normal argv with previous parameters removed + * @return 0 on success, nonzero on failure. + */ +int net_common_methods_usage(struct net_context *c, int argc, const char**argv); + +/** + * Get help for common flags. + * + * This will output some help for using common flags. + * + * @param c A net_context structure + * @param argc Normal argc with previous parameters removed + * @param argv Normal argv with previous parameters removed + * @return 0 on success, nonzero on failure. + */ +int net_common_flags_usage(struct net_context *c, int argc, const char **argv); + + +#endif /* _NET_HELP_COMMON_H_*/ + diff --git a/source3/utils/net_idmap.c b/source3/utils/net_idmap.c new file mode 100644 index 0000000..027b741 --- /dev/null +++ b/source3/utils/net_idmap.c @@ -0,0 +1,1413 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2003 Andrew Bartlett (abartlet@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "utils/net.h" +#include "secrets.h" +#include "idmap.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "../libcli/security/security.h" +#include "net_idmap_check.h" +#include "util_tdb.h" +#include "idmap_autorid_tdb.h" +#include "lib/util/smb_strtox.h" + +#define ALLOC_CHECK(mem) do { \ + if (!mem) { \ + d_fprintf(stderr, _("Out of memory!\n")); \ + talloc_free(ctx); \ + return -1; \ + } } while(0) + +enum idmap_dump_backend { + TDB, + AUTORID +}; + +struct net_idmap_ctx { + enum idmap_dump_backend backend; +}; + +static int net_idmap_dump_one_autorid_entry(struct db_record *rec, + void *unused) +{ + TDB_DATA key; + TDB_DATA value; + + key = dbwrap_record_get_key(rec); + value = dbwrap_record_get_value(rec); + + if (strncmp((char *)key.dptr, "CONFIG", 6) == 0) { + char *config = talloc_array(talloc_tos(), char, value.dsize+1); + memcpy(config, value.dptr, value.dsize); + config[value.dsize] = '\0'; + printf("CONFIG: %s\n", config); + talloc_free(config); + return 0; + } + + if (strncmp((char *)key.dptr, "NEXT RANGE", 10) == 0) { + printf("RANGE HWM: %"PRIu32"\n", IVAL(value.dptr, 0)); + return 0; + } + + if (strncmp((char *)key.dptr, "NEXT ALLOC UID", 14) == 0) { + printf("UID HWM: %"PRIu32"\n", IVAL(value.dptr, 0)); + return 0; + } + + if (strncmp((char *)key.dptr, "NEXT ALLOC GID", 14) == 0) { + printf("GID HWM: %"PRIu32"\n", IVAL(value.dptr, 0)); + return 0; + } + + if (strncmp((char *)key.dptr, "UID", 3) == 0 || + strncmp((char *)key.dptr, "GID", 3) == 0) + { + /* mapped entry from allocation pool */ + printf("%s %s\n", value.dptr, key.dptr); + return 0; + } + + if ((strncmp((char *)key.dptr, "S-1-5-", 6) == 0 || + strncmp((char *)key.dptr, "ALLOC", 5) == 0) && + value.dsize == sizeof(uint32_t)) + { + /* this is a domain range assignment */ + uint32_t range = IVAL(value.dptr, 0); + printf("RANGE %"PRIu32": %s\n", range, key.dptr); + return 0; + } + + return 0; +} + +/*********************************************************** + Helper function for net_idmap_dump. Dump one entry. + **********************************************************/ +static int net_idmap_dump_one_tdb_entry(struct db_record *rec, + void *unused) +{ + TDB_DATA key; + TDB_DATA value; + + key = dbwrap_record_get_key(rec); + value = dbwrap_record_get_value(rec); + + if (strcmp((char *)key.dptr, "USER HWM") == 0) { + printf(_("USER HWM %d\n"), IVAL(value.dptr,0)); + return 0; + } + + if (strcmp((char *)key.dptr, "GROUP HWM") == 0) { + printf(_("GROUP HWM %d\n"), IVAL(value.dptr,0)); + return 0; + } + + if (strncmp((char *)key.dptr, "S-", 2) != 0) { + return 0; + } + + printf("%s %s\n", value.dptr, key.dptr); + return 0; +} + +/* returns db path for idmap backend alloced on talloc_tos */ +static char *net_idmap_dbfile(struct net_context *c, + struct net_idmap_ctx *ctx) +{ + char *dbfile = NULL; + const char *backend = NULL; + + backend = lp_idmap_default_backend(); + if (!backend) { + d_printf(_("Internal error: 'idmap config * : backend' is not set!\n")); + return NULL; + } + + if (c->opt_db != NULL) { + dbfile = talloc_strdup(talloc_tos(), c->opt_db); + if (dbfile == NULL) { + d_fprintf(stderr, _("Out of memory!\n")); + } + } else if (strequal(backend, "tdb")) { + dbfile = state_path(talloc_tos(), "winbindd_idmap.tdb"); + if (dbfile == NULL) { + d_fprintf(stderr, _("Out of memory!\n")); + } + ctx->backend = TDB; + } else if (strequal(backend, "tdb2")) { + dbfile = talloc_asprintf(talloc_tos(), "%s/idmap2.tdb", + lp_private_dir()); + if (dbfile == NULL) { + d_fprintf(stderr, _("Out of memory!\n")); + } + ctx->backend = TDB; + } else if (strequal(backend, "autorid")) { + dbfile = state_path(talloc_tos(), "autorid.tdb"); + if (dbfile == NULL) { + d_fprintf(stderr, _("Out of memory!\n")); + } + ctx->backend = AUTORID; + } else { + char *_backend = talloc_strdup(talloc_tos(), backend); + char* args = strchr(_backend, ':'); + if (args != NULL) { + *args = '\0'; + } + + d_printf(_("Sorry, 'idmap backend = %s' is currently not supported\n"), + _backend); + + talloc_free(_backend); + } + + return dbfile; +} + +static bool net_idmap_opendb_autorid(TALLOC_CTX *mem_ctx, + struct net_context *c, + bool readonly, + struct db_context **db) +{ + bool ret = false; + char *dbfile = NULL; + struct net_idmap_ctx ctx = { .backend = AUTORID }; + + if (c == NULL) { + goto done; + } + + dbfile = net_idmap_dbfile(c, &ctx); + if (dbfile == NULL) { + goto done; + } + + if (ctx.backend != AUTORID) { + d_fprintf(stderr, _("Unsupported backend\n")); + goto done; + } + + if (readonly) { + *db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDONLY, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (*db == NULL) { + d_fprintf(stderr, + _("Could not open autorid db (%s): %s\n"), + dbfile, strerror(errno)); + goto done; + } + } else { + NTSTATUS status; + status = idmap_autorid_db_init(dbfile, mem_ctx, db); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Error calling idmap_autorid_db_init: %s\n"), + nt_errstr(status)); + goto done; + } + } + + ret = true; + +done: + talloc_free(dbfile); + return ret; +} + + +/*********************************************************** + Dump the current idmap + **********************************************************/ +static int net_idmap_dump(struct net_context *c, int argc, const char **argv) +{ + struct db_context *db; + TALLOC_CTX *mem_ctx; + const char* dbfile; + NTSTATUS status; + int ret = -1; + struct net_idmap_ctx ctx = { .backend = TDB }; + + if ( argc > 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap dump [[--db=]<inputfile>]\n" + " Dump current ID mapping.\n" + " inputfile\tTDB file to read mappings from.\n")); + return c->display_usage?0:-1; + } + + mem_ctx = talloc_stackframe(); + + dbfile = (argc > 0) ? argv[0] : net_idmap_dbfile(c, &ctx); + if (dbfile == NULL) { + goto done; + } + d_fprintf(stderr, _("dumping id mapping from %s\n"), dbfile); + + db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDONLY, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (db == NULL) { + d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"), + dbfile, strerror(errno)); + goto done; + } + + if (ctx.backend == AUTORID) { + status = dbwrap_traverse_read(db, + net_idmap_dump_one_autorid_entry, + NULL, NULL); + } else { + status = dbwrap_traverse_read(db, + net_idmap_dump_one_tdb_entry, + NULL, NULL); + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("error traversing the database\n")); + ret = -1; + goto done; + } + + ret = 0; + +done: + talloc_free(mem_ctx); + return ret; +} + +/*********************************************************** + Write entries from stdin to current local idmap + **********************************************************/ + +static int net_idmap_store_id_mapping(struct db_context *db, + enum id_type type, + unsigned long idval, + const char *sid_string) +{ + NTSTATUS status; + char *idstr = NULL; + + switch(type) { + case ID_TYPE_UID: + idstr = talloc_asprintf(talloc_tos(), "UID %lu", idval); + break; + case ID_TYPE_GID: + idstr = talloc_asprintf(talloc_tos(), "GID %lu", idval); + break; + default: + d_fprintf(stderr, "Invalid id mapping type: %d\n", type); + return -1; + } + + status = dbwrap_store_bystring(db, idstr, + string_term_tdb_data(sid_string), + TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "Error storing ID -> SID: " + "%s\n", nt_errstr(status)); + talloc_free(idstr); + return -1; + } + status = dbwrap_store_bystring(db, sid_string, + string_term_tdb_data(idstr), + TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "Error storing SID -> ID: " + "%s\n", nt_errstr(status)); + talloc_free(idstr); + return -1; + } + + return 0; +} + +static int net_idmap_restore(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + FILE *input = NULL; + struct db_context *db; + const char *dbfile = NULL; + int ret = 0; + struct net_idmap_ctx ctx = { .backend = TDB }; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap restore [--db=<TDB>] [<inputfile>]\n" + " Restore ID mappings from file\n" + " TDB\tFile to store ID mappings to." + " inputfile\tFile to load ID mappings from. If not " + "given, load data from stdin.\n")); + return 0; + } + + mem_ctx = talloc_stackframe(); + + dbfile = net_idmap_dbfile(c, &ctx); + + if (dbfile == NULL) { + ret = -1; + goto done; + } + + if (ctx.backend != TDB) { + d_fprintf(stderr, _("Sorry, restoring of non-TDB databases is " + "currently not supported\n")); + ret = -1; + goto done; + } + + d_fprintf(stderr, _("restoring id mapping to %s\n"), dbfile); + + if (argc == 1) { + input = fopen(argv[0], "r"); + if (input == NULL) { + d_fprintf(stderr, _("Could not open input file (%s): %s\n"), + argv[0], strerror(errno)); + ret = -1; + goto done; + } + } else { + input = stdin; + } + + db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0644, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (db == NULL) { + d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"), + dbfile, strerror(errno)); + ret = -1; + goto done; + } + + if (dbwrap_transaction_start(db) != 0) { + d_fprintf(stderr, _("Failed to start transaction.\n")); + ret = -1; + goto done; + } + + while (!feof(input)) { + char line[128], sid_string[128]; + int len; + unsigned long idval; + NTSTATUS status; + + if (fgets(line, 127, input) == NULL) + break; + + len = strlen(line); + + if ( (len > 0) && (line[len-1] == '\n') ) + line[len-1] = '\0'; + + if (sscanf(line, "GID %lu %127s", &idval, sid_string) == 2) + { + ret = net_idmap_store_id_mapping(db, ID_TYPE_GID, + idval, sid_string); + if (ret != 0) { + break; + } + } else if (sscanf(line, "UID %lu %127s", &idval, sid_string) == 2) + { + ret = net_idmap_store_id_mapping(db, ID_TYPE_UID, + idval, sid_string); + if (ret != 0) { + break; + } + } else if (sscanf(line, "USER HWM %lu", &idval) == 1) { + status = dbwrap_store_int32_bystring( + db, "USER HWM", idval); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Could not store USER HWM: %s\n"), + nt_errstr(status)); + break; + } + } else if (sscanf(line, "GROUP HWM %lu", &idval) == 1) { + status = dbwrap_store_int32_bystring( + db, "GROUP HWM", idval); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Could not store GROUP HWM: %s\n"), + nt_errstr(status)); + break; + } + } else { + d_fprintf(stderr, _("ignoring invalid line [%s]\n"), + line); + continue; + } + } + + if (ret == 0) { + if(dbwrap_transaction_commit(db) != 0) { + d_fprintf(stderr, _("Failed to commit transaction.\n")); + ret = -1; + } + } else { + if (dbwrap_transaction_cancel(db) != 0) { + d_fprintf(stderr, _("Failed to cancel transaction.\n")); + } + } + +done: + if ((input != NULL) && (input != stdin)) { + fclose(input); + } + + talloc_free(mem_ctx); + return ret; +} + +static +NTSTATUS dbwrap_delete_mapping(struct db_context *db, TDB_DATA key1, bool force) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + bool is_valid_mapping; + NTSTATUS status = NT_STATUS_OK; + TDB_DATA val1, val2; + + ZERO_STRUCT(val1); + ZERO_STRUCT(val2); + + status = dbwrap_fetch(db, mem_ctx, key1, &val1); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("failed to fetch: %.*s\n", (int)key1.dsize, key1.dptr)); + goto done; + } + + if (val1.dptr == NULL) { + DEBUG(1, ("invalid mapping: %.*s -> empty value\n", + (int)key1.dsize, key1.dptr)); + status = NT_STATUS_FILE_INVALID; + goto done; + } + + DEBUG(2, ("mapping: %.*s -> %.*s\n", + (int)key1.dsize, key1.dptr, (int)val1.dsize, val1.dptr)); + + status = dbwrap_fetch(db, mem_ctx, val1, &val2); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("failed to fetch: %.*s\n", (int)val1.dsize, val1.dptr)); + goto done; + } + + is_valid_mapping = tdb_data_equal(key1, val2); + + if (!is_valid_mapping) { + DEBUG(1, ("invalid mapping: %.*s -> %.*s -> %.*s\n", + (int)key1.dsize, key1.dptr, + (int)val1.dsize, val1.dptr, + (int)val2.dsize, val2.dptr)); + if ( !force ) { + status = NT_STATUS_FILE_INVALID; + goto done; + } + } + + status = dbwrap_delete(db, key1); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("failed to delete: %.*s\n", (int)key1.dsize, key1.dptr)); + goto done; + } + + if (!is_valid_mapping) { + goto done; + } + + status = dbwrap_delete(db, val1); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("failed to delete: %.*s\n", (int)val1.dsize, val1.dptr)); + } + +done: + talloc_free(mem_ctx); + return status; +} + +static +NTSTATUS delete_mapping_action(struct db_context *db, void* data) +{ + return dbwrap_delete_mapping(db, *(TDB_DATA*)data, false); +} +static +NTSTATUS delete_mapping_action_force(struct db_context *db, void* data) +{ + return dbwrap_delete_mapping(db, *(TDB_DATA*)data, true); +} + +/*********************************************************** + Delete a SID mapping from a winbindd_idmap.tdb + **********************************************************/ +static bool delete_args_ok(int argc, const char **argv) +{ + if (argc != 1) + return false; + if (strncmp(argv[0], "S-", 2) == 0) + return true; + if (strncmp(argv[0], "GID ", 4) == 0) + return true; + if (strncmp(argv[0], "UID ", 4) == 0) + return true; + return false; +} + +static int net_idmap_delete_mapping(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + struct db_context *db; + TALLOC_CTX *mem_ctx; + TDB_DATA key; + NTSTATUS status; + const char* dbfile; + struct net_idmap_ctx ctx = { .backend = TDB }; + + if ( !delete_args_ok(argc,argv) || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap delete mapping [-f] [--db=<TDB>] <ID>\n" + " Delete mapping of ID from TDB.\n" + " -f\tforce\n" + " TDB\tidmap database\n" + " ID\tSID|GID|UID\n")); + return c->display_usage ? 0 : -1; + } + + mem_ctx = talloc_stackframe(); + + dbfile = net_idmap_dbfile(c, &ctx); + if (dbfile == NULL) { + goto done; + } + d_fprintf(stderr, _("deleting id mapping from %s\n"), dbfile); + + db = db_open(mem_ctx, dbfile, 0, TDB_DEFAULT, O_RDWR, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (db == NULL) { + d_fprintf(stderr, _("Could not open idmap db (%s): %s\n"), + dbfile, strerror(errno)); + goto done; + } + + key = string_term_tdb_data(argv[0]); + + status = dbwrap_trans_do(db, (c->opt_force + ? delete_mapping_action_force + : delete_mapping_action), &key); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("could not delete mapping: %s\n"), + nt_errstr(status)); + goto done; + } + ret = 0; +done: + talloc_free(mem_ctx); + return ret; +} + +static bool parse_uint32(const char *str, uint32_t *result) +{ + unsigned long val; + int error = 0; + + val = smb_strtoul(str, NULL, 10, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + return false; + } + + *result = val; /* Potential crop */ + return true; +} + +static void net_idmap_autorid_delete_range_usage(void) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net idmap delete range [-f] [--db=<TDB>] <RANGE>|(<SID>[ <INDEX>])\n" + " Delete a domain range mapping from the database.\n" + " -f\tforce\n" + " TDB\tidmap database\n" + " RANGE\tthe range number to delete\n" + " SID\t\tSID of the domain\n" + " INDEX\trange index number do delete for the domain\n")); +} + +static int net_idmap_autorid_delete_range(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + struct db_context *db = NULL; + NTSTATUS status; + uint32_t rangenum; + uint32_t range_index; + const char *domsid; + TALLOC_CTX *mem_ctx = NULL; + bool ok; + bool force = (c->opt_force != 0); + + if (c->display_usage) { + net_idmap_autorid_delete_range_usage(); + return 0; + } + + if (argc < 1 || argc > 2) { + net_idmap_autorid_delete_range_usage(); + return -1; + } + + mem_ctx = talloc_stackframe(); + if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) { + goto done; + } + + ok = parse_uint32(argv[0], &rangenum); + if (ok) { + d_printf("%s: %"PRIu32"\n", _("Deleting range number"), + rangenum); + + status = idmap_autorid_delete_range_by_num(db, rangenum, + force); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Failed to delete domain range mapping"), + nt_errstr(status)); + } else { + ret = 0; + } + + goto done; + } + + domsid = argv[0]; + range_index = 0; + + if (argc == 2) { + ok = parse_uint32(argv[1], &range_index); + if (!ok) { + d_printf("%s: %s\n", + _("Invalid index specification"), argv[1]); + net_idmap_autorid_delete_range_usage(); + goto done; + } + } + + status = idmap_autorid_delete_range_by_sid(db, domsid, range_index, + force); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Failed to delete domain range mapping"), + nt_errstr(status)); + goto done; + } + + ret = 0; + +done: + talloc_free(mem_ctx); + return ret; +} + +static void net_idmap_autorid_delete_ranges_usage(void) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net idmap delete ranges [-f] [--db=<TDB>] <SID>\n" + " Delete all domain range mappings for a given domain.\n" + " -f\tforce\n" + " TDB\tidmap database\n" + " SID\t\tSID of the domain\n")); +} + +static int net_idmap_autorid_delete_ranges(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + struct db_context *db = NULL; + NTSTATUS status; + const char *domsid; + TALLOC_CTX *mem_ctx = NULL; + bool force = (c->opt_force != 0); + int count = 0; + + if (c->display_usage) { + net_idmap_autorid_delete_ranges_usage(); + return 0; + } + + if (argc != 1) { + net_idmap_autorid_delete_ranges_usage(); + return -1; + } + + domsid = argv[0]; + + mem_ctx = talloc_stackframe(); + if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) { + goto done; + } + + status = idmap_autorid_delete_domain_ranges(db, domsid, force, &count); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s %s: %s\n", + _("Failed to delete domain range mappings for " + "domain"), + domsid, + nt_errstr(status)); + goto done; + } + + d_printf(_("deleted %d domain mappings\n"), count); + + ret = 0; + +done: + talloc_free(mem_ctx); + return ret; +} + +static int net_idmap_delete(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "mapping", + net_idmap_delete_mapping, + NET_TRANSPORT_LOCAL, + N_("Delete ID mapping"), + N_("net idmap delete mapping <ID>\n" + " Delete ID mapping") + }, + { + "range", + net_idmap_autorid_delete_range, + NET_TRANSPORT_LOCAL, + N_("Delete a domain range mapping"), + N_("net idmap delete range <RANGE>|(<SID>[ <INDEX>])\n" + " Delete a domain range mapping") + }, + { + "ranges", + net_idmap_autorid_delete_ranges, + NET_TRANSPORT_LOCAL, + N_("Delete all domain range mappings for a given " + "domain"), + N_("net idmap delete ranges <SID>\n" + " Delete a domain range mapping") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net idmap delete", func); +} + + +static int net_idmap_set_mapping(struct net_context *c, + int argc, const char **argv) +{ + d_printf("%s\n", _("Not implemented yet")); + return -1; +} + +static void net_idmap_autorid_set_range_usage(void) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net idmap set range" + " <range> <SID> [<index>] [--db=<inputfile>]\n" + " Store a domain-range mapping for a given domain.\n" + " range\tRange number to be set for the domain\n" + " SID\t\tSID of the domain\n" + " index\trange-index number to be set for the domain\n" + " inputfile\tTDB file to add mapping to.\n")); +} + +static int net_idmap_autorid_set_range(struct net_context *c, + int argc, const char **argv) +{ + int ret = -1; + TALLOC_CTX *mem_ctx; + struct db_context *db = NULL; + const char *domsid; + uint32_t rangenum; + uint32_t range_index = 0; + NTSTATUS status; + bool ok; + + if (c->display_usage) { + net_idmap_autorid_set_range_usage(); + return 0; + } + + if (argc < 2 || argc > 3) { + net_idmap_autorid_set_range_usage(); + return -1; + } + + ok = parse_uint32(argv[0], &rangenum); + if (!ok) { + d_printf("%s: %s\n", _("Invalid range specification"), + argv[0]); + net_idmap_autorid_set_range_usage(); + return -1; + } + + domsid = argv[1]; + + if (argc == 3) { + ok = parse_uint32(argv[2], &range_index); + if (!ok) { + d_printf("%s: %s\n", + _("Invalid index specification"), argv[2]); + net_idmap_autorid_set_range_usage(); + return -1; + } + } + + mem_ctx = talloc_stackframe(); + if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) { + goto done; + } + + status = idmap_autorid_setrange(db, domsid, range_index, rangenum); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Failed to save domain mapping"), + nt_errstr(status)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static bool idmap_store_secret(const char *backend, + const char *domain, + const char *identity, + const char *secret) +{ + char *tmp; + int r; + bool ret; + + r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain); + + if (r < 0) return false; + + /* make sure the key is case insensitive */ + if (!strupper_m(tmp)) { + free(tmp); + return false; + } + ret = secrets_store_generic(tmp, identity, secret); + + free(tmp); + return ret; +} + + +static int net_idmap_secret(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *ctx; + const char *secret; + const char *dn; + char *domain; + char *backend; + char *opt = NULL; + bool ret; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:\n"), + _("net idmap set secret <DOMAIN> <secret>\n" + " Set the secret for the specified domain\n" + " DOMAIN\tDomain to set secret for.\n" + " secret\tNew secret to set.\n")); + return c->display_usage?0:-1; + } + + secret = argv[1]; + + ctx = talloc_new(NULL); + ALLOC_CHECK(ctx); + + domain = talloc_strdup(ctx, argv[0]); + ALLOC_CHECK(domain); + + opt = talloc_asprintf(ctx, "idmap config %s", domain); + ALLOC_CHECK(opt); + + backend = talloc_strdup(ctx, lp_parm_const_string(-1, opt, "backend", "tdb")); + ALLOC_CHECK(backend); + + if ((!backend) || (!strequal(backend, "ldap") && + !strequal(backend, "rfc2307"))) { + d_fprintf(stderr, + _("The only currently supported backend are LDAP " + "and rfc2307\n")); + talloc_free(ctx); + return -1; + } + + dn = lp_parm_const_string(-1, opt, "ldap_user_dn", NULL); + if ( ! dn) { + d_fprintf(stderr, + _("Missing ldap_user_dn option for domain %s\n"), + domain); + talloc_free(ctx); + return -1; + } + + ret = idmap_store_secret("ldap", domain, dn, secret); + + if ( ! ret) { + d_fprintf(stderr, _("Failed to store secret\n")); + talloc_free(ctx); + return -1; + } + + d_printf(_("Secret stored\n")); + return 0; +} + +static int net_idmap_autorid_set_config(struct net_context *c, + int argc, const char **argv) +{ + int ret = -1; + NTSTATUS status; + TALLOC_CTX *mem_ctx; + struct db_context *db = NULL; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap set config <config>" + " [--db=<inputfile>]\n" + " Update CONFIG entry in autorid.\n" + " config\tConfig string to be stored\n" + " inputfile\tTDB file to update config.\n")); + return c->display_usage ? 0 : -1; + } + + mem_ctx = talloc_stackframe(); + + if (!net_idmap_opendb_autorid(mem_ctx, c, false, &db)) { + goto done; + } + + status = idmap_autorid_saveconfigstr(db, argv[0]); + if (!NT_STATUS_IS_OK(status)) { + printf("Error storing the config in the database: %s\n", + nt_errstr(status)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static int net_idmap_set(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "mapping", + net_idmap_set_mapping, + NET_TRANSPORT_LOCAL, + N_("Not implemented yet"), + N_("net idmap set mapping\n" + " Not implemented yet") + }, + { + "range", + net_idmap_autorid_set_range, + NET_TRANSPORT_LOCAL, + N_("Store a domain-range mapping"), + N_("net idmap set range\n" + " Store a domain-range mapping") + }, + { + "config", + net_idmap_autorid_set_config, + NET_TRANSPORT_LOCAL, + N_("Save the global configuration in the autorid database"), + N_("net idmap set config \n" + " Save the global configuration in the autorid database ") + }, + { + "secret", + net_idmap_secret, + NET_TRANSPORT_LOCAL, + N_("Set secret for specified domain"), + N_("net idmap set secret <DOMAIN> <secret>\n" + " Set secret for specified domain") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net idmap set", func); +} + +static void net_idmap_autorid_get_range_usage(void) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net idmap get range <SID> [<index>] [--db=<inputfile>]\n" + " Get the range for a given domain and index.\n" + " SID\t\tSID of the domain\n" + " index\trange-index number to be retrieved\n" + " inputfile\tTDB file to add mapping to.\n")); +} + + +static int net_idmap_autorid_get_range(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + TALLOC_CTX *mem_ctx; + struct db_context *db = NULL; + const char *domsid; + uint32_t rangenum; + uint32_t range_index = 0; + uint32_t low_id; + NTSTATUS status; + char *keystr; + bool ok; + + if (c->display_usage) { + net_idmap_autorid_get_range_usage(); + return 0; + } + + if (argc < 1 || argc > 2) { + net_idmap_autorid_get_range_usage(); + return -1; + } + + domsid = argv[0]; + + if (argc == 2) { + ok = parse_uint32(argv[1], &range_index); + if (!ok) { + d_printf("%s: %s\n", + _("Invalid index specification"), argv[1]); + net_idmap_autorid_get_range_usage(); + return -1; + } + } + + mem_ctx = talloc_stackframe(); + if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) { + goto done; + } + + status = idmap_autorid_getrange(db, domsid, range_index, &rangenum, + &low_id); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Failed to load domain range"), nt_errstr(status)); + goto done; + } + + if (range_index == 0) { + keystr = talloc_strdup(mem_ctx, domsid); + } else { + keystr = talloc_asprintf(mem_ctx, "%s#%"PRIu32, domsid, + range_index); + } + + printf("RANGE %"PRIu32": %s (low id: %"PRIu32")\n", + rangenum, keystr, low_id); + + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static NTSTATUS net_idmap_autorid_print_range(struct db_context *db, + const char *domsid, + uint32_t range_index, + uint32_t rangenum, + void *private_data) +{ + if (range_index == 0) { + printf("RANGE %"PRIu32": %s\n", rangenum, domsid); + } else { + printf("RANGE %"PRIu32": %s#%"PRIu32"\n", rangenum, domsid, + range_index); + } + + return NT_STATUS_OK; +} + +static void net_idmap_autorid_get_ranges_usage(void) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net idmap get ranges [<SID>] [--db=<inputfile>]\n" + " Get all ranges for a given domain.\n" + " SID\t\tSID of the domain - list all ranges if omitted\n" + " inputfile\tTDB file to add mapping to.\n")); +} + +static int net_idmap_autorid_get_ranges(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + TALLOC_CTX *mem_ctx; + struct db_context *db = NULL; + const char *domsid; + NTSTATUS status; + + if (c->display_usage) { + net_idmap_autorid_get_ranges_usage(); + return 0; + } + + if (argc == 0) { + domsid = NULL; + } else if (argc == 1) { + domsid = argv[0]; + } else { + net_idmap_autorid_get_ranges_usage(); + return -1; + } + + mem_ctx = talloc_stackframe(); + if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) { + goto done; + } + + status = idmap_autorid_iterate_domain_ranges_read(db, + domsid, + net_idmap_autorid_print_range, + NULL, /* private_data */ + NULL /* count */); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Error getting domain ranges"), nt_errstr(status)); + goto done; + } + + ret = 0; + +done: + talloc_free(mem_ctx); + return ret; +} + +static int net_idmap_autorid_get_config(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + char *config; + TALLOC_CTX *mem_ctx; + NTSTATUS status; + struct db_context *db = NULL; + + if (argc > 0 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap get config" + " [--db=<inputfile>]\n" + " Get CONFIG entry from autorid database\n" + " inputfile\tTDB file to read config from.\n")); + return c->display_usage ? 0 : -1; + } + + mem_ctx = talloc_stackframe(); + + if (!net_idmap_opendb_autorid(mem_ctx, c, true, &db)) { + goto done; + } + + status = idmap_autorid_getconfigstr(db, mem_ctx, &config); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "%s: %s\n", + _("Error: unable to read config entry"), + nt_errstr(status)); + goto done; + } + + printf("CONFIG: %s\n", config); + ret = 0; + +done: + TALLOC_FREE(mem_ctx); + return ret; +} + + +static int net_idmap_get(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "range", + net_idmap_autorid_get_range, + NET_TRANSPORT_LOCAL, + N_("Get the range for a domain and range-index"), + N_("net idmap get range\n" + " Get the range for a domain and range-index") + }, + { + "ranges", + net_idmap_autorid_get_ranges, + NET_TRANSPORT_LOCAL, + N_("Get all ranges for a domain"), + N_("net idmap get ranges <SID>\n" + " Get all ranges for a domain") + }, + { + "config", + net_idmap_autorid_get_config, + NET_TRANSPORT_LOCAL, + N_("Get the global configuration from the autorid database"), + N_("net idmap get config \n" + " Get the global configuration from the autorid database ") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net idmap get", func); +} + +static int net_idmap_check(struct net_context *c, int argc, const char **argv) +{ + char *dbfile; + struct check_options opts; + struct net_idmap_ctx ctx = { .backend = TDB }; + int ret; + + if ( argc > 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net idmap check [-v] [-r] [-a] [-T] [-f] [-l] [[--db=]<TDB>]\n" + " Check an idmap database.\n" + " --verbose,-v\tverbose\n" + " --repair,-r\trepair\n" + " --auto,-a\tnoninteractive mode\n" + " --test,-T\tdry run\n" + " --force,-f\tforce\n" + " --lock,-l\tlock db while doing the check\n" + " TDB\tidmap database\n")); + return c->display_usage ? 0 : -1; + } + + if (argc > 0) { + dbfile = talloc_strdup(talloc_tos(), argv[0]); + } else { + dbfile = net_idmap_dbfile(c, &ctx); + } + if (dbfile == NULL) { + return -1; + } + + if (ctx.backend != TDB) { + d_fprintf(stderr, _("Sorry, checking of non-TDB databases is " + "currently not supported\n")); + talloc_free(dbfile); + return -1; + } + + d_fprintf(stderr, _("check database: %s\n"), dbfile); + + opts = (struct check_options) { + .lock = c->opt_lock || c->opt_long_list_entries, + .test = c->opt_testmode, + .automatic = c->opt_auto, + .verbose = c->opt_verbose, + .force = c->opt_force, + .repair = c->opt_repair || c->opt_reboot, + }; + + ret = net_idmap_check_db(dbfile, &opts); + talloc_free(dbfile); + return ret; +} + +/*********************************************************** + Look at the current idmap + **********************************************************/ +int net_idmap(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "dump", + net_idmap_dump, + NET_TRANSPORT_LOCAL, + N_("Dump the current ID mapping database"), + N_("net idmap dump\n" + " Dump the current ID mappings") + }, + { + "restore", + net_idmap_restore, + NET_TRANSPORT_LOCAL, + N_("Restore entries from a file or stdin"), + N_("net idmap restore\n" + " Restore entries from stdin") + }, + { + "get", + net_idmap_get, + NET_TRANSPORT_LOCAL, + N_("Read data from the ID mapping database"), + N_("net idmap get\n" + " Read data from the ID mapping database") + }, + { + "set", + net_idmap_set, + NET_TRANSPORT_LOCAL, + N_("Write data to the ID mapping database"), + N_("net idmap set\n" + " Write data to the ID mapping database") + }, + { + "delete", + net_idmap_delete, + NET_TRANSPORT_LOCAL, + N_("Delete entries from the ID mapping database"), + N_("net idmap delete\n" + " Delete entries from the ID mapping database") + }, + { + "check", + net_idmap_check, + NET_TRANSPORT_LOCAL, + N_("Check id mappings"), + N_("net idmap check\n" + " Check id mappings") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net idmap", func); +} + + diff --git a/source3/utils/net_idmap_check.c b/source3/utils/net_idmap_check.c new file mode 100644 index 0000000..51f4a40 --- /dev/null +++ b/source3/utils/net_idmap_check.c @@ -0,0 +1,974 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Check the idmap database. + * @author Gregor Beck <gb@sernet.de> + * @date Mar 2011 + */ + +#include "net_idmap_check.h" +#include "includes.h" +#include "system/filesys.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "dbwrap/dbwrap_rbt.h" +#include "net.h" +#include "../libcli/security/dom_sid.h" +#include "cbuf.h" +#include "srprs.h" +#include "util_tdb.h" +#include "interact.h" + +static int traverse_commit(struct db_record *diff_rec, void* data); +static int traverse_check(struct db_record *rec, void* data); + +/* TDB_DATA *******************************************************************/ +static char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d); +static TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr); + +/* record *********************************************************************/ + +enum DT { + DT_INV = 0, + DT_SID, DT_UID, DT_GID, + DT_HWM, DT_VER, DT_SEQ, +}; + +struct record { + enum DT key_type, val_type; + TDB_DATA key, val; + struct dom_sid sid; + long unsigned id; +}; + +static struct record* parse_record(TALLOC_CTX* ctx, TDB_DATA key, TDB_DATA val); +static struct record* reverse_record(struct record* rec); + +static bool is_invalid(const struct record* r) { + return (r->key_type == DT_INV) || (r->val_type == DT_INV); +} + +static bool is_map(const struct record* r) { + return (r->key_type == DT_SID) + || (r->key_type == DT_UID) || (r->key_type == DT_GID); +} + +/* action *********************************************************************/ + +typedef struct check_action { + void (*fmt)(struct check_action *a, + struct record *r, + TDB_DATA *v); + const char* name; + const char* prompt; + const char* answers; + char auto_action; + char default_action; + bool verbose; +} check_action; + +struct check_actions { + check_action invalid_record; + check_action missing_reverse; + check_action invalid_mapping; + check_action invalid_edit; + check_action record_exists; + check_action no_version; + check_action wrong_version; + check_action invalid_hwm; + check_action commit; + check_action valid_mapping; + check_action valid_other; + check_action invalid_diff; +}; + +static void invalid_mapping_fmt(struct check_action *a, + struct record *r, + TDB_DATA *v) +{ + d_printf("%1$s: %2$s -> %3$s\n(%4$s <- %3$s)\n", + a->name, + print_data(r, r->key), + print_data(r, r->val), + (v ? print_data(r, *v) : "")); +} + +static void record_exists_fmt(struct check_action *a, + struct record *r, + TDB_DATA *v) +{ + d_printf("%1$s: %2$s\n-%4$s\n+%3$s\n", + a->name, + print_data(r, r->key), + print_data(r, r->val), + (v ? print_data(r, *v) : "")); +} + +static void valid_mapping_fmt(struct check_action *a, + struct record *r, + TDB_DATA *v) +{ + d_printf("%1$s: %2$s <-> %3$s\n", + a->name, + print_data(r, r->key), + print_data(r, r->val)); +} + +static struct check_actions +check_actions_init(const struct check_options* opts) { + struct check_actions ret = { + .invalid_record = (check_action) { + .name = "Invalid record", + .prompt = "[e]dit/[d]elete/[D]elete all" + "/[s]kip/[S]kip all", + .answers = "eds", + .default_action = 'e', + .verbose = true, + }, + .missing_reverse = (check_action) { + .name = "Missing reverse mapping for", + .prompt = "[f]ix/[F]ix all/[e]dit/[d]elete/[D]elete all" + "/[s]kip/[S]kip all", + .answers = "feds", + .default_action = 'f', + .verbose = true, + }, + .invalid_mapping = (check_action) { + .fmt = invalid_mapping_fmt, + .name = "Invalid mapping", + .prompt = "[e]dit/[d]elete/[D]elete all" + "/[s]kip/[S]kip all", + .answers = "eds", + .default_action = 'd', + .verbose = true, + }, + .invalid_edit = (check_action) { + .name = "Invalid record", + .prompt = "[e]dit/[d]elete/[D]elete all" + "/[s]kip/[S]kip all", + .answers = "eds", + .default_action = 'e', + .verbose = true, + }, + .record_exists = (check_action) { + .fmt = record_exists_fmt, + .name = "Record exists", + .prompt = "[o]verwrite/[O]verwrite all/[e]dit" + "/[d]elete/[D]elete all/[s]kip/[S]kip all", + .answers = "oeds", + .default_action = 'o', + .verbose = true, + }, + .no_version = (check_action) { + .prompt = "[f]ix/[s]kip/[a]bort", + .answers = "fsa", + .default_action = 'f', + }, + .wrong_version = (check_action) { + .prompt = "[f]ix/[s]kip/[a]bort", + .answers = "fsa", + .default_action = 'a', + }, + .invalid_hwm = (check_action) { + .prompt = "[f]ix/[s]kip", + .answers = "fs", + .default_action = 'f', + }, + .commit = (check_action) { + .prompt = "[c]ommit/[l]ist/[s]kip", + .answers = "cls", + .default_action = 'l', + .verbose = true, + }, + .valid_mapping = (check_action) { + .fmt = valid_mapping_fmt, + .name = "Mapping", + .auto_action = 's', + .verbose = opts->verbose, + }, + .valid_other = (check_action) { + .name = "Other", + .auto_action = 's', + .verbose = opts->verbose, + }, + .invalid_diff = (check_action) { + .prompt = "[s]kip/[S]kip all/[c]ommit/[C]ommit all" + "/[a]bort", + .answers = "sca", + .default_action = 's', + }, + }; + + if (!opts->repair) { + ret.invalid_record.auto_action = 's'; + ret.missing_reverse.auto_action = 's'; + ret.invalid_mapping.auto_action = 's'; + ret.no_version.auto_action = 's'; + ret.wrong_version.auto_action = 's'; + ret.invalid_hwm.auto_action = 's'; + ret.commit.auto_action = 's'; + } + + if (opts->automatic) { + ret.invalid_record.auto_action = 'd'; /* delete */ + ret.missing_reverse.auto_action = 'f'; /* fix */ + ret.invalid_mapping.auto_action = 'd'; /* delete */ + ret.no_version.auto_action = 'f'; /* fix */ + ret.wrong_version.auto_action = 'a'; /* abort */ + ret.invalid_hwm.auto_action = 'f'; /* fix */ + ret.commit.auto_action = 'c'; /* commit */ + ret.invalid_diff.auto_action = 'a'; /* abort */ + if (opts->force) { + ret.wrong_version.auto_action = 'f'; /* fix */ + ret.invalid_diff.auto_action = 'c'; /* commit */ + } + } + if (opts->test) { + ret.invalid_diff.auto_action = 'c'; /* commit */ +/* ret.commit.auto_action = 'c';*/ /* commit */ + } + + return ret; +} + +static char get_action(struct check_action* a, struct record* r, TDB_DATA* v) { + char ret; + if (a->verbose && (r != NULL)) { + if (!a->fmt) { + d_printf("%s: %s ", a->name, print_data(r, r->key)); + if (is_map(r)) { + d_printf("-> %s\n", print_data(r, r->val)); + } else if (r->key_type == DT_HWM || + r->key_type == DT_VER || + r->key_type == DT_SEQ) + { + d_printf(": %ld\n", r->id); + } else { + d_printf("\n"); + } + } else { + a->fmt(a, r, v); + } + } + + if (a->auto_action != '\0') { + return a->auto_action; + } + + ret = interact_prompt(a->prompt, a->answers, a->default_action); + + if (isupper(ret)) { + ret = tolower(ret); + a->auto_action = ret; + } + a->default_action = ret; + return ret; +} + +/* *************************************************************************/ + +typedef struct { + TDB_DATA oval, nval; +} TDB_DATA_diff; + +static TDB_DATA pack_diff(TDB_DATA_diff* diff) { + return (TDB_DATA) { + .dptr = (uint8_t *)diff, + .dsize = sizeof(TDB_DATA_diff), + }; +} + +static TDB_DATA_diff unpack_diff(TDB_DATA data) { + assert(data.dsize == sizeof(TDB_DATA_diff)); + return *(TDB_DATA_diff*)data.dptr; +} + +#define DEBUG_DIFF(LEV,MEM,MSG,KEY,OLD,NEW) \ + DEBUG(LEV, ("%s: %s\n", MSG, print_data(MEM, KEY))); \ + if (!tdb_data_is_empty(OLD)) { \ + DEBUGADD(LEV, ("-%s\n", print_data(MEM, OLD))); \ + } \ + if (!tdb_data_is_empty(NEW)) { \ + DEBUGADD(LEV, ("+%s\n", print_data(MEM, NEW))); \ + } + +struct check_ctx { + int oflags; + char* name; + bool transaction; + struct db_context *db; + struct db_context *diff; + struct check_actions action; + + uint32_t uid_hwm; + uint32_t gid_hwm; + + unsigned n_invalid_record; + unsigned n_missing_reverse; + unsigned n_invalid_mappping; + unsigned n_map; + unsigned n_other; + unsigned n_diff; + struct check_options opts; +}; + + +static void adjust_hwm(struct check_ctx* ctx, const struct record* r); + +static int add_record(struct check_ctx* ctx, TDB_DATA key, TDB_DATA value) +{ + NTSTATUS status; + TDB_DATA_diff diff; + TALLOC_CTX* mem = talloc_new(ctx->diff); + TDB_DATA recvalue; + struct db_record *rec = dbwrap_fetch_locked(ctx->diff, mem, key); + + if (rec == NULL) { + return -1; + } + + recvalue = dbwrap_record_get_value(rec); + + if (recvalue.dptr == 0) { /* first entry */ + status = dbwrap_fetch(ctx->db, ctx->diff, key, &diff.oval); + if (!NT_STATUS_IS_OK(status)) { + diff.oval = tdb_null; + } + } else { + diff = unpack_diff(recvalue); + talloc_free(diff.nval.dptr); + } + diff.nval = tdb_data_talloc_copy(ctx->diff, value); + + DEBUG_DIFF(2, mem, "TDB DIFF", key, diff.oval, diff.nval); + + status = dbwrap_record_store(rec, pack_diff(&diff), 0); + + talloc_free(mem); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("could not store record %s\n", nt_errstr(status))); + return -1; + } + ctx->n_diff ++; + return 0; +} + +static int del_record(struct check_ctx* ctx, TDB_DATA key) { + return add_record(ctx, key, tdb_null); +} + +static TDB_DATA +fetch_record(struct check_ctx* ctx, TALLOC_CTX* mem_ctx, TDB_DATA key) +{ + TDB_DATA tmp; + NTSTATUS status; + + status = dbwrap_fetch(ctx->diff, mem_ctx, key, &tmp); + + if (NT_STATUS_IS_OK(status)) { + TDB_DATA_diff diff = unpack_diff(tmp); + TDB_DATA ret = tdb_data_talloc_copy(mem_ctx, diff.nval); + talloc_free(tmp.dptr); + return ret; + } + + status = dbwrap_fetch(ctx->db, mem_ctx, key, &tmp); + if (!NT_STATUS_IS_OK(status)) { + return tdb_null; + } + + return tmp; +} + +static void edit_record(struct record* r) { + TALLOC_CTX* mem = talloc_new(r); + cbuf* ost = cbuf_new(mem); + const char* str; + struct record* nr; + TDB_DATA key; + TDB_DATA val; + cbuf_printf(ost, "%s %s\n", + print_data(mem, r->key), print_data(mem, r->val)); + str = interact_edit(mem, cbuf_gets(ost, 0)); + key = parse_data(mem, &str); + val = parse_data(mem, &str); + nr = parse_record(talloc_parent(r), key, val); + if (nr != NULL) { + *r = *nr; + } + talloc_free(mem); +} + +static bool check_version(struct check_ctx* ctx) { + static const char* key = "IDMAP_VERSION"; + uint32_t version; + NTSTATUS status; + char action = 's'; + struct check_actions* act = &ctx->action; + + status = dbwrap_fetch_uint32_bystring(ctx->db, key, &version); + if (!NT_STATUS_IS_OK(status)) { + d_printf("No version number, assume 2\n"); + action = get_action(&act->no_version, NULL, NULL); + } else if (version != 2) { + d_printf("Wrong version number %d, should be 2\n", version); + action = get_action(&act->wrong_version, NULL, NULL); + } + switch (action) { + case 's': + break; + case 'f': + SIVAL(&version, 0, 2); + add_record(ctx, string_term_tdb_data(key), + make_tdb_data((uint8_t *)&version, sizeof(uint32_t))); + break; + case 'a': + return false; + default: + assert(false); + } + return true; +} + +static void check_hwm(struct check_ctx* ctx, const char* key, uint32_t target) { + uint32_t hwm; + char action = 's'; + NTSTATUS status; + struct check_actions* act = &ctx->action; + + status = dbwrap_fetch_uint32_bystring(ctx->db, key, &hwm); + if (!NT_STATUS_IS_OK(status)) { + d_printf("No %s should be %d\n", key, target); + action = get_action(&act->invalid_hwm, NULL, NULL); + } else if (target < hwm) { + d_printf("Invalid %s %d: should be %d\n", key, hwm, target); + action = get_action(&act->invalid_hwm, NULL, NULL); + } + if (action == 'f') { + SIVAL(&hwm, 0, target); + add_record(ctx, string_term_tdb_data(key), + make_tdb_data((uint8_t *)&hwm, sizeof(uint32_t))); + } +} + +int traverse_check(struct db_record *rec, void* data) { + struct check_ctx* ctx = (struct check_ctx*)data; + struct check_actions* act = &ctx->action; + TALLOC_CTX* mem = talloc_new(ctx->diff); + TDB_DATA key; + TDB_DATA value; + struct record *r; + char action = 's'; + + key = dbwrap_record_get_key(rec); + value = dbwrap_record_get_value(rec); + + r = parse_record(mem, key, value); + + if (is_invalid(r)) { + action = get_action(&act->invalid_record, r, NULL); + ctx->n_invalid_record++; + } else if (is_map(r)) { + TDB_DATA back = fetch_record(ctx, mem, r->val); + if (back.dptr == NULL) { + action = get_action(&act->missing_reverse, r, NULL); + ctx->n_missing_reverse++; + } else if (!tdb_data_equal(r->key, back)) { + action = get_action(&act->invalid_mapping, r, &back); + ctx->n_invalid_mappping++; + } else { + if (r->key_type == DT_SID) { + action = get_action(&act->valid_mapping, r, NULL); + ctx->n_map++; + } else { + action = get_action(&act->valid_mapping, NULL, + NULL); + } + } + adjust_hwm(ctx, r); + } else { + action = get_action(&act->valid_other, r, NULL); + ctx->n_other++; + } + + while (action) { + switch (action) { + case 's': /* skip */ + break; + case 'd': /* delete */ + del_record(ctx, key); + break; + case 'f': /* add reverse mapping */ + add_record(ctx, value, key); + break; + case 'e': /* edit */ + edit_record(r); + action = 'o'; + if (is_invalid(r)) { + action = get_action(&act->invalid_edit, r,NULL); + continue; + } + if (!tdb_data_equal(key, r->key)) { + TDB_DATA oval = fetch_record(ctx, mem, r->key); + if (!tdb_data_is_empty(oval) && + !tdb_data_equal(oval, r->val)) + { + action = get_action(&act->record_exists, + r, &oval); + if (action != 'o') { + continue; + } + } + } + if (is_map(r)) { + TDB_DATA okey = fetch_record(ctx, mem, r->val); + if (!tdb_data_is_empty(okey) && + !tdb_data_equal(okey, r->key)) + { + action = get_action(&act->record_exists, + reverse_record(r), + &okey); + } + } + continue; + case 'o': /* overwrite */ + adjust_hwm(ctx, r); + if (!tdb_data_equal(key, r->key)) { + del_record(ctx, key); + } + add_record(ctx, r->key, r->val); + if (is_map(r)) { + add_record(ctx, r->val, r->key); + } + } + action = '\0'; + }; + + talloc_free(mem); + + return 0; +} + +/******************************************************************************/ + +void adjust_hwm(struct check_ctx* ctx, const struct record* r) { + enum DT type = (r->key_type == DT_SID) ? r->val_type : r->key_type; + if (type == DT_UID) { + ctx->uid_hwm = MAX(ctx->uid_hwm, r->id); + } else if (type == DT_GID) { + ctx->gid_hwm = MAX(ctx->gid_hwm, r->id); + } +} + +static bool is_cstr(TDB_DATA str) { + return !tdb_data_is_empty(str) && str.dptr[str.dsize-1] == '\0'; +} + +static bool parse_sid (TDB_DATA str, enum DT* type, struct dom_sid* sid) { + struct dom_sid tmp; + const char* s = (const char*)str.dptr; + if ((s[0] == 'S') && string_to_sid(&tmp, s)) { + *sid = tmp; + *type = DT_SID; + return true; + } + return false; +} + +static bool parse_xid(TDB_DATA str, enum DT* type, unsigned long* id) { + char c, t; + unsigned long tmp; + if (sscanf((const char*)str.dptr, "%cID %lu%c", &c, &tmp, &t) == 2) { + if (c == 'U') { + *id = tmp; + *type = DT_UID; + return true; + } else if (c == 'G') { + *id = tmp; + *type = DT_GID; + return true; + } + } + return false; +} + + +struct record* +parse_record(TALLOC_CTX* mem_ctx, TDB_DATA key, TDB_DATA val) +{ + struct record* ret = talloc_zero(mem_ctx, struct record); + if (ret == NULL) { + DEBUG(0, ("Out of memory.\n")); + return NULL; + } + ret->key = tdb_data_talloc_copy(ret, key); + ret->val = tdb_data_talloc_copy(ret, val); + if ((ret->key.dptr == NULL && key.dptr != NULL) || + (ret->val.dptr == NULL && val.dptr != NULL)) + { + talloc_free(ret); + DEBUG(0, ("Out of memory.\n")); + return NULL; + } + assert((ret->key_type == DT_INV) && (ret->val_type == DT_INV)); + + if (!is_cstr(key)) { + return ret; + } + if (parse_sid(key, &ret->key_type, &ret->sid)) { + parse_xid(val, &ret->val_type, &ret->id); + } else if (parse_xid(key, &ret->key_type, &ret->id)) { + if (is_cstr(val)) { + parse_sid(val, &ret->val_type, &ret->sid); + } + } else if (strcmp((const char*)key.dptr, "USER HWM") == 0) { + ret->key_type = DT_HWM; + if (val.dsize == 4) { + ret->id = IVAL(val.dptr,0); + ret->val_type = DT_UID; + } + } else if (strcmp((const char*)key.dptr, "GROUP HWM") == 0) { + ret->key_type = DT_HWM; + if (val.dsize == 4) { + ret->id = IVAL(val.dptr,0); + ret->val_type = DT_GID; + } + } else if (strcmp((const char*)key.dptr, "IDMAP_VERSION") == 0) { + ret->key_type = DT_VER; + if (val.dsize == 4) { + ret->id = IVAL(val.dptr,0); + ret->val_type = DT_VER; + } + } else if (strcmp((const char*)key.dptr, "__db_sequence_number__") == 0) { + ret->key_type = DT_SEQ; + if (val.dsize == 8) { + ret->id = *(uint64_t*)val.dptr; + ret->val_type = DT_SEQ; + } + } + + return ret; +} + +struct record* reverse_record(struct record* in) +{ + return parse_record(talloc_parent(in), in->val, in->key); +} + + +/******************************************************************************/ + + +char* print_data(TALLOC_CTX* mem_ctx, TDB_DATA d) +{ + if (!tdb_data_is_empty(d)) { + char* ret = NULL; + cbuf* ost = cbuf_new(mem_ctx); + int len = cbuf_print_quoted(ost, (const char*)d.dptr, d.dsize); + if (len != -1) { + cbuf_swapptr(ost, &ret, 0); + talloc_steal(mem_ctx, ret); + } + talloc_free(ost); + return ret; + } + return talloc_strdup(mem_ctx, "<NULL>"); +} + + +TDB_DATA parse_data(TALLOC_CTX* mem_ctx, const char** ptr) { + cbuf* ost = cbuf_new(mem_ctx); + TDB_DATA ret = tdb_null; + srprs_skipws(ptr); + if (srprs_quoted(ptr, ost)) { + ret.dsize = cbuf_getpos(ost); + ret.dptr = (uint8_t *)talloc_steal(mem_ctx, cbuf_gets(ost,0)); + } + talloc_free(ost); + return ret; +} + +static int traverse_print_diff(struct db_record *rec, void* data) { + struct check_ctx* ctx = (struct check_ctx*)data; + TDB_DATA key; + TDB_DATA value; + TDB_DATA_diff diff; + TALLOC_CTX* mem = talloc_new(ctx->diff); + + key = dbwrap_record_get_key(rec); + value = dbwrap_record_get_value(rec); + diff = unpack_diff(value); + + DEBUG_DIFF(0, mem, "DIFF", key, diff.oval, diff.nval); + + talloc_free(mem); + return 0; +} + + +static int traverse_commit(struct db_record *diff_rec, void* data) { + struct check_ctx* ctx = (struct check_ctx*)data; + TDB_DATA key; + TDB_DATA diff_value; + TDB_DATA_diff diff; + TDB_DATA value; + TALLOC_CTX* mem = talloc_new(ctx->diff); + int ret = -1; + NTSTATUS status; + struct check_actions* act = &ctx->action; + struct db_record* rec; + + key = dbwrap_record_get_key(diff_rec); + diff_value = dbwrap_record_get_value(diff_rec); + diff = unpack_diff(diff_value); + + rec = dbwrap_fetch_locked(ctx->db, mem, key); + if (rec == NULL) { + goto done; + } + + value = dbwrap_record_get_value(rec); + + if (!tdb_data_equal(value, diff.oval)) { + char action; + + d_printf("Warning: record has changed: %s\n" + "expected: %s got %s\n", print_data(mem, key), + print_data(mem, diff.oval), + print_data(mem, value)); + + action = get_action(&act->invalid_diff, NULL, NULL); + if (action == 's') { + ret = 0; + goto done; + } else if (action == 'a') { + goto done; + } + } + + DEBUG_DIFF(0, mem, "Commit", key, diff.oval, diff.nval); + + if (tdb_data_is_empty(diff.nval)) { + status = dbwrap_record_delete(rec); + } else { + status = dbwrap_record_store(rec, diff.nval, 0); + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("could not store record %s\n", nt_errstr(status))); + if (!ctx->opts.force) { + goto done; + } + } + ret = 0; +done: + talloc_free(mem); + return ret; +} + +static struct check_ctx* +check_init(TALLOC_CTX* mem_ctx, const struct check_options* o) +{ + struct check_ctx* ctx = talloc_zero(mem_ctx, struct check_ctx); + if (ctx == NULL) { + DEBUG(0, (_("No memory\n"))); + return NULL; + } + + ctx->diff = db_open_rbt(ctx); + if (ctx->diff == NULL) { + talloc_free(ctx); + DEBUG(0, (_("No memory\n"))); + return NULL; + } + + ctx->action = check_actions_init(o); + ctx->opts = *o; + return ctx; +} + +static bool check_open_db(struct check_ctx* ctx, const char* name, int oflags) +{ + if (name == NULL) { + d_fprintf(stderr, _("Error: name == NULL in check_open_db().\n")); + return false; + } + + if (ctx->db != NULL) { + if ((ctx->oflags == oflags) && (strcmp(ctx->name, name))) { + return true; + } else { + TALLOC_FREE(ctx->db); + } + } + + ctx->db = db_open(ctx, name, 0, TDB_DEFAULT, oflags, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (ctx->db == NULL) { + d_fprintf(stderr, + _("Could not open idmap db (%s) for writing: %s\n"), + name, strerror(errno)); + return false; + } + + if (ctx->name != name) { + TALLOC_FREE(ctx->name); + ctx->name = talloc_strdup(ctx, name); + } + + ctx->oflags = oflags; + return true; +} + +static bool check_do_checks(struct check_ctx* ctx) +{ + NTSTATUS status; + + if (!check_version(ctx)) { + return false; + } + + status = dbwrap_traverse(ctx->db, traverse_check, ctx, NULL); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("failed to traverse %s\n", ctx->name)); + return false; + } + + check_hwm(ctx, "USER HWM", ctx->uid_hwm + 1); + check_hwm(ctx, "GROUP HWM", ctx->gid_hwm + 1); + + return true; +} + +static void check_summary(const struct check_ctx* ctx) +{ + d_printf("uid hwm: %d\ngid hwm: %d\n", ctx->uid_hwm, ctx->gid_hwm); + d_printf("mappings: %d\nother: %d\n", ctx->n_map, ctx->n_other); + d_printf("invalid records: %d\nmissing links: %d\ninvalid links: %d\n", + ctx->n_invalid_record, ctx->n_missing_reverse, + ctx->n_invalid_mappping); + d_printf("%u changes:\n", ctx->n_diff); +} + +static bool check_transaction_start(struct check_ctx* ctx) { + return (dbwrap_transaction_start(ctx->db) == 0); +} + +static bool check_transaction_commit(struct check_ctx* ctx) { + return (dbwrap_transaction_commit(ctx->db) == 0); +} + +static bool check_transaction_cancel(struct check_ctx* ctx) { + return (dbwrap_transaction_cancel(ctx->db) == 0); +} + + +static void check_diff_list(struct check_ctx* ctx) { + NTSTATUS status = dbwrap_traverse(ctx->diff, traverse_print_diff, ctx, NULL); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("failed to traverse diff\n")); + } + +} + +static bool check_commit(struct check_ctx* ctx) +{ + struct check_actions* act = &ctx->action; + char action; + NTSTATUS status = NT_STATUS_OK; + + check_summary(ctx); + + if (ctx->n_diff == 0) { + return true; + } + + while ((action = get_action(&act->commit, NULL, NULL)) == 'l') { + check_diff_list(ctx); + } + if (action == 's') { + return true; + } + assert(action == 'c'); + + if (!check_open_db(ctx, ctx->name, O_RDWR)) { + return false; + } + + if (!check_transaction_start(ctx)) { + return false; + } + + status = dbwrap_traverse(ctx->diff, traverse_commit, ctx, NULL); + + if (!NT_STATUS_IS_OK(status)) { + check_transaction_cancel(ctx); + return false; + } + if (ctx->opts.test) { /*get_action? */ + return check_transaction_cancel(ctx); + } else { + return check_transaction_commit(ctx); + } +} + +int net_idmap_check_db(const char* db, const struct check_options* o) +{ + int ret = -1; + TALLOC_CTX* mem_ctx = talloc_stackframe(); + struct check_ctx* ctx = check_init(mem_ctx, o); + + if (!o->automatic && !isatty(STDIN_FILENO)) { + DEBUG(0, ("Interactive use needs tty, use --auto\n")); + goto done; + } + if (o->lock) { + if (check_open_db(ctx, db, O_RDWR) + && check_transaction_start(ctx)) + { + if ( check_do_checks(ctx) + && check_commit(ctx) + && check_transaction_commit(ctx)) + { + ret = 0; + } else { + check_transaction_cancel(ctx); + } + } + } else { + if (check_open_db(ctx, db, O_RDONLY) + && check_do_checks(ctx) + && check_commit(ctx)) + { + ret = 0; + } + } +done: + talloc_free(mem_ctx); + return ret; +} + + +/*Local Variables:*/ +/*mode: c*/ +/*End:*/ diff --git a/source3/utils/net_idmap_check.h b/source3/utils/net_idmap_check.h new file mode 100644 index 0000000..4176342 --- /dev/null +++ b/source3/utils/net_idmap_check.h @@ -0,0 +1,48 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Check the idmap database. + * @author Gregor Beck <gb@sernet.de> + * @date Mar 2011 + */ + +#ifndef NET_IDMAP_CHECK_H +#define NET_IDMAP_CHECK_H + +#include <stdbool.h> + +struct net_context; + +struct check_options { + bool test; + bool verbose; + bool lock; + bool automatic; + bool force; + bool repair; +}; + +int net_idmap_check_db(const char* db, const struct check_options* opts); + +#endif /* NET_IDMAP_CHECK_H */ + +/*Local Variables:*/ +/*mode: c*/ +/*End:*/ diff --git a/source3/utils/net_join.c b/source3/utils/net_join.c new file mode 100644 index 0000000..f67f08f --- /dev/null +++ b/source3/utils/net_join.c @@ -0,0 +1,55 @@ +/* + Samba Unix/Linux SMB client library + net join commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_join_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet [<method>] join [misc. options]\n" + "\tjoins this server to a domain\n")); + d_printf(_("Valid methods: (auto-detected if not specified)\n")); + d_printf(_("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n")); + d_printf(_("\trpc\t\t\t\tDCE-RPC\n")); + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_join(struct net_context *c, int argc, const char **argv) +{ + if ((argc > 0) && (strcasecmp_m(argv[0], "HELP") == 0)) { + net_join_usage(c, argc, argv); + return 0; + } + + net_warn_member_options(); + + if (net_ads_check_our_domain(c) == 0) { + if (net_ads_join(c, argc, argv) == 0) + return 0; + else + d_fprintf(stderr, + _("ADS join did not work, falling back to " + "RPC...\n")); + } + return net_rpc_join(c, argc, argv); +} + + diff --git a/source3/utils/net_lookup.c b/source3/utils/net_lookup.c new file mode 100644 index 0000000..570135a --- /dev/null +++ b/source3/utils/net_lookup.c @@ -0,0 +1,542 @@ +/* + Samba Unix/Linux SMB client library + net lookup command + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "utils/net.h" +#include "libsmb/namequery.h" +#include "libads/sitename_cache.h" +#include "lib/addns/dnsquery_srv.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "smb_krb5.h" +#include "../libcli/security/security.h" +#include "passdb/lookup_sid.h" +#include "libsmb/dsgetdcname.h" + +int net_lookup_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( +" net lookup [host] HOSTNAME[#<type>]\n\tgives IP for a hostname\n\n" +" net lookup ldap [domain] [sitename]\n\tgives IP of domain's ldap server\n\n" +" net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n" +" net lookup pdc [domain|realm]\n\tgives IP of realm's kerberos KDC\n\n" +" net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n" +" net lookup master [domain|wg]\n\tgive IP of master browser\n\n" +" net lookup name [name]\n\tLookup name's sid and type\n\n" +" net lookup sid [sid]\n\tGive sid's name and type\n\n" +" net lookup dsgetdcname [name] [flags] [sitename]\n\n" +)); + return -1; +} + +/* lookup a hostname giving an IP */ +static int net_lookup_host(struct net_context *c, int argc, const char **argv) +{ + struct sockaddr_storage ss; + int name_type = 0x20; + char addr[INET6_ADDRSTRLEN]; + const char *name = argv[0]; + char *p; + + if (argc == 0) + return net_lookup_usage(c, argc, argv); + + p = strchr_m(name,'#'); + if (p) { + *p = '\0'; + sscanf(++p,"%x",&name_type); + } + + if (!resolve_name(name, &ss, name_type, false)) { + /* we deliberately use DEBUG() here to send it to stderr + so scripts aren't mucked up */ + DEBUG(0,("Didn't find %s#%02x\n", name, name_type)); + return -1; + } + + print_sockaddr(addr, sizeof(addr), &ss); + d_printf("%s\n", addr); + return 0; +} + +#ifdef HAVE_ADS +static void print_ldap_srvlist(struct dns_rr_srv *dclist, size_t numdcs) +{ + size_t i; + + for ( i=0; i<numdcs; i++ ) { + struct dns_rr_srv *dc = &dclist[i]; + size_t j; + + for (j=0; j<dc->num_ips; j++) { + struct sockaddr_storage *ss = &dc->ss_s[j]; + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), ss); +#ifdef HAVE_IPV6 + if (ss->ss_family == AF_INET6) { + d_printf("[%s]:%"PRIu16"\n", addr, dc->port); + } +#endif + if (ss->ss_family == AF_INET) { + d_printf("%s:%"PRIu16"\n", addr, dc->port); + } + } + } +} +#endif + +static int net_lookup_ldap(struct net_context *c, int argc, const char **argv) +{ +#ifdef HAVE_ADS + const char *domain; + struct sockaddr_storage ss; + struct dns_rr_srv *dcs = NULL; + size_t numdcs = 0; + const char *sitename = NULL; + TALLOC_CTX *ctx; + NTSTATUS status; + int ret; + char h_name[MAX_DNS_NAME_LENGTH]; + char *query = NULL; + + if (argc > 0) + domain = argv[0]; + else + domain = c->opt_target_workgroup; + + if (argc > 1) { + sitename = argv[1]; + } + + if ( (ctx = talloc_init("net_lookup_ldap")) == NULL ) { + d_fprintf(stderr,"net_lookup_ldap: talloc_init() %s!\n", + _("failed")); + return -1; + } + + if (sitename == NULL) { + sitename = sitename_fetch(ctx, domain); + } + query = ads_dns_query_string_dcs(ctx, domain); + + DEBUG(9, ("Lookup up ldap for domain %s\n", domain)); + + status = ads_dns_query_srv( + ctx, + lp_get_async_dns_timeout(), + sitename, + query, + &dcs, + &numdcs); + if ( NT_STATUS_IS_OK(status) && numdcs ) { + print_ldap_srvlist(dcs, numdcs); + TALLOC_FREE( ctx ); + return 0; + } + + DEBUG(9, ("Looking up PDC for domain %s\n", domain)); + if (!get_pdc_ip(domain, &ss)) { + TALLOC_FREE( ctx ); + return -1; + } + + ret = sys_getnameinfo((struct sockaddr *)&ss, + sizeof(struct sockaddr_storage), + h_name, sizeof(h_name), + NULL, 0, + NI_NAMEREQD); + + if (ret) { + TALLOC_FREE( ctx ); + return -1; + } + + DEBUG(9, ("Found PDC with DNS name %s\n", h_name)); + domain = strchr(h_name, '.'); + if (!domain) { + TALLOC_FREE( ctx ); + return -1; + } + domain++; + + DEBUG(9, ("Looking up ldap for domain %s\n", domain)); + + status = ads_dns_query_srv( + ctx, + lp_get_async_dns_timeout(), + sitename, + query, + &dcs, + &numdcs); + if ( NT_STATUS_IS_OK(status) && numdcs ) { + print_ldap_srvlist(dcs, numdcs); + TALLOC_FREE( ctx ); + return 0; + } + + TALLOC_FREE( ctx ); + + return -1; +#endif + DEBUG(1,("No ADS support\n")); + return -1; +} + +static int net_lookup_dc(struct net_context *c, int argc, const char **argv) +{ + struct samba_sockaddr *sa_list = NULL; + struct sockaddr_storage ss; + char *pdc_str = NULL; + const char *domain = NULL; + char *sitename = NULL; + size_t count = 0; + size_t i; + char addr[INET6_ADDRSTRLEN]; + bool sec_ads = (lp_security() == SEC_ADS); + NTSTATUS status; + + if (sec_ads) { + domain = lp_realm(); + } else { + domain = c->opt_target_workgroup; + } + + if (argc > 0) + domain=argv[0]; + + /* first get PDC */ + if (!get_pdc_ip(domain, &ss)) + return -1; + + print_sockaddr(addr, sizeof(addr), &ss); + if (asprintf(&pdc_str, "%s", addr) == -1) { + return -1; + } + d_printf("%s\n", pdc_str); + + sitename = sitename_fetch(talloc_tos(), domain); + status = get_sorted_dc_list(talloc_tos(), + domain, + sitename, + &sa_list, + &count, + sec_ads); + if (!NT_STATUS_IS_OK(status)) { + SAFE_FREE(pdc_str); + TALLOC_FREE(sitename); + return 0; + } + TALLOC_FREE(sitename); + for (i=0;i<count;i++) { + print_sockaddr(addr, sizeof(addr), &sa_list[i].u.ss); + if (!strequal(pdc_str, addr)) + d_printf("%s\n", addr); + } + TALLOC_FREE(sa_list); + SAFE_FREE(pdc_str); + return 0; +} + +static int net_lookup_pdc(struct net_context *c, int argc, const char **argv) +{ + struct sockaddr_storage ss; + char *pdc_str = NULL; + const char *domain; + char addr[INET6_ADDRSTRLEN]; + + if (lp_security() == SEC_ADS) { + domain = lp_realm(); + } else { + domain = c->opt_target_workgroup; + } + + if (argc > 0) + domain=argv[0]; + + /* first get PDC */ + if (!get_pdc_ip(domain, &ss)) + return -1; + + print_sockaddr(addr, sizeof(addr), &ss); + if (asprintf(&pdc_str, "%s", addr) == -1) { + return -1; + } + d_printf("%s\n", pdc_str); + SAFE_FREE(pdc_str); + return 0; +} + + +static int net_lookup_master(struct net_context *c, int argc, const char **argv) +{ + struct sockaddr_storage master_ss; + const char *domain = c->opt_target_workgroup; + char addr[INET6_ADDRSTRLEN]; + + if (argc > 0) + domain=argv[0]; + + if (!find_master_ip(domain, &master_ss)) + return -1; + print_sockaddr(addr, sizeof(addr), &master_ss); + d_printf("%s\n", addr); + return 0; +} + +static int net_lookup_kdc(struct net_context *c, int argc, const char **argv) +{ +#ifdef HAVE_KRB5 + krb5_error_code rc; + krb5_context ctx; + struct samba_sockaddr *kdcs = NULL; + const char *realm; + char **get_host_realms = NULL; + size_t num_kdcs = 0; + size_t i; + NTSTATUS status; + + rc = smb_krb5_init_context_common(&ctx); + if (rc) { + DBG_ERR("kerberos init context failed (%s)\n", + error_message(rc)); + return -1; + } + + if (argc > 0) { + realm = argv[0]; + } else if (lp_realm() && *lp_realm()) { + realm = lp_realm(); + } else { + rc = krb5_get_host_realm(ctx, NULL, &get_host_realms); + if (rc) { + DEBUG(1,("krb5_gethost_realm failed (%s)\n", + error_message(rc))); + krb5_free_context(ctx); + return -1; + } + realm = (const char *) *get_host_realms; + } + + status = get_kdc_list(talloc_tos(), + realm, + NULL, + &kdcs, + &num_kdcs); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("get_kdc_list failed (%s)\n", + nt_errstr(status)); + krb5_free_host_realm(ctx, get_host_realms); + krb5_free_context(ctx); + return -1; + } + + for (i = 0; i < num_kdcs; i++) { + char addr[INET6_ADDRSTRLEN]; + + print_sockaddr(addr, sizeof(addr), &kdcs[i].u.ss); + + d_printf("%s:88\n", addr); + } + + krb5_free_host_realm(ctx, get_host_realms); + krb5_free_context(ctx); + TALLOC_FREE(kdcs); + return 0; +#endif + DEBUG(1, ("No kerberos support\n")); + return -1; +} + +static int net_lookup_name(struct net_context *c, int argc, const char **argv) +{ + const char *dom, *name; + struct dom_sid sid; + struct dom_sid_buf buf; + enum lsa_SidType type; + + if (argc != 1) { + d_printf("%s\n%s", + _("Usage:"), + _(" net lookup name <name>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_ALL, + &dom, &name, &sid, &type)) { + d_printf(_("Could not lookup name %s\n"), argv[0]); + return -1; + } + + d_printf("%s %d (%s) %s\\%s\n", dom_sid_str_buf(&sid, &buf), + type, sid_type_lookup(type), dom, name); + return 0; +} + +static int net_lookup_sid(struct net_context *c, int argc, const char **argv) +{ + const char *dom, *name; + struct dom_sid sid; + struct dom_sid_buf buf; + enum lsa_SidType type; + + if (argc != 1) { + d_printf("%s\n%s", + _("Usage:"), + _(" net lookup sid <sid>\n")); + return -1; + } + + if (!string_to_sid(&sid, argv[0])) { + d_printf(_("Could not convert %s to SID\n"), argv[0]); + return -1; + } + + if (!lookup_sid(talloc_tos(), &sid, + &dom, &name, &type)) { + d_printf(_("Could not lookup name %s\n"), argv[0]); + return -1; + } + + d_printf("%s %d (%s) %s\\%s\n", dom_sid_str_buf(&sid, &buf), + type, sid_type_lookup(type), dom, name); + return 0; +} + +static int net_lookup_dsgetdcname(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + const char *domain_name = NULL; + const char *site_name = NULL; + uint32_t flags = 0; + struct netr_DsRGetDCNameInfo *info = NULL; + TALLOC_CTX *mem_ctx; + char *s = NULL; + + if (argc < 1 || argc > 3) { + d_printf("%s\n%s", + _("Usage:"), + _(" net lookup dsgetdcname " + "<name> <flags> <sitename>\n")); + return -1; + } + + mem_ctx = talloc_init("net_lookup_dsgetdcname"); + if (!mem_ctx) { + return -1; + } + + domain_name = argv[0]; + + if (argc >= 2) { + sscanf(argv[1], "%x", &flags); + } + + if (flags == 0) { + flags = DS_DIRECTORY_SERVICE_REQUIRED; + } + + if (argc == 3) { + site_name = argv[2]; + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + return -1; + } + + status = dsgetdcname(mem_ctx, c->msg_ctx, domain_name, NULL, site_name, + flags, &info); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed with: %s\n"), nt_errstr(status)); + TALLOC_FREE(mem_ctx); + return -1; + } + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, netr_DsRGetDCNameInfo, info); + printf("%s\n", s); + TALLOC_FREE(s); + + TALLOC_FREE(mem_ctx); + return 0; +} + + +/* lookup hosts or IP addresses using internal samba lookup fns */ +int net_lookup(struct net_context *c, int argc, const char **argv) +{ + int i; + + struct functable table[] = { + { + .funcname = "HOST", + .fn = net_lookup_host, + }, + { + .funcname = "LDAP", + .fn = net_lookup_ldap, + }, + { + .funcname = "DC", + .fn = net_lookup_dc, + }, + { + .funcname = "PDC", + .fn = net_lookup_pdc, + }, + { + .funcname = "MASTER", + .fn = net_lookup_master, + }, + { + .funcname = "KDC", + .fn = net_lookup_kdc, + }, + { + .funcname = "NAME", + .fn = net_lookup_name, + }, + { + .funcname = "SID", + .fn = net_lookup_sid, + }, + { + .funcname = "DSGETDCNAME", + .fn = net_lookup_dsgetdcname, + }, + { + .funcname = NULL, + }, + }; + + if (argc < 1) { + d_printf(_("\nUsage: \n")); + return net_lookup_usage(c, argc, argv); + } + for (i=0; table[i].funcname; i++) { + if (strcasecmp_m(argv[0], table[i].funcname) == 0) + return table[i].fn(c, argc-1, argv+1); + } + + /* Default to lookup a hostname so 'net lookup foo#1b' can be + used instead of 'net lookup host foo#1b'. The host syntax + is a bit confusing as non #00 names can't really be + considered hosts as such. */ + + return net_lookup_host(c, argc, argv); +} diff --git a/source3/utils/net_notify.c b/source3/utils/net_notify.c new file mode 100644 index 0000000..eddfb0e --- /dev/null +++ b/source3/utils/net_notify.c @@ -0,0 +1,199 @@ +/* + * Samba Unix/Linux notifyd client code + * Copyright (C) 2015 Volker Lendecke <vl@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "utils/net.h" +#include "lib/util/server_id.h" +#include "lib/util/tevent_unix.h" +#include "lib/util/server_id_db.h" +#include "messages.h" +#include "source3/smbd/notifyd/notifyd.h" + +static void net_notify_got_event(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id server_id, + DATA_BLOB *data) +{ + struct notify_event_msg *event_msg; + + if (data->length < offsetof(struct notify_event_msg, path) + 1) { + d_fprintf(stderr, "message too short\n"); + return; + } + if (data->data[data->length-1] != 0) { + d_fprintf(stderr, "path not 0-terminated\n"); + return; + } + + event_msg = (struct notify_event_msg *)data->data; + + d_printf("%u %s\n", (unsigned)event_msg->action, + event_msg->path); +} + +static int net_notify_listen(struct net_context *c, int argc, + const char **argv) +{ + struct messaging_context *msg_ctx = c->msg_ctx; + struct tevent_context *ev = messaging_tevent_context(msg_ctx); + struct server_id_db *names_db = messaging_names_db(msg_ctx); + struct server_id notifyd; + struct server_id_buf idbuf; + struct notify_rec_change_msg msg; + struct iovec iov[2]; + NTSTATUS status; + bool ok; + + if (argc != 3) { + d_printf("Usage: net notify listen <path> <filter> " + "<subdir-filter>\n"); + return -1; + } + + ok = server_id_db_lookup_one(names_db, "notify-daemon", ¬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 <path> <action> " + "<filter>\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 <path>") + }, + { "trigger", + net_notify_trigger, + NET_TRANSPORT_LOCAL, + N_("Simulate a trigger action"), + N_("net notify trigger <path> <action> <filter>") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (c->msg_ctx == NULL) { + d_fprintf(stderr, "No connection to messaging, need to run " + "as root\n"); + return -1; + } + + return net_run_function(c, argc, argv, "net notify", func); +} diff --git a/source3/utils/net_offlinejoin.c b/source3/utils/net_offlinejoin.c new file mode 100644 index 0000000..3ec5c97 --- /dev/null +++ b/source3/utils/net_offlinejoin.c @@ -0,0 +1,600 @@ +/* + Samba Unix/Linux SMB client library + net join commands + Copyright (C) 2021 Guenther Deschner (gd@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include <netapi.h> +#include "netapi/netapi_net.h" +#include "libcli/registry/util_reg.h" +#include "libcli/security/dom_sid.h" +#include "lib/cmdline/cmdline.h" + +int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet offlinejoin [misc. options]\n" + "\tjoins a computer to a domain\n")); + d_printf(_("Valid commands:\n")); + d_printf(_("\tprovision\t\t\tProvision machine account in AD\n")); + d_printf(_("\trequestodj\t\t\tRequest offline domain join\n")); + d_printf(_("\tcomposeodj\t\t\tCompose offline domain join blob\n")); + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_offlinejoin(struct net_context *c, int argc, const char **argv) +{ + int ret; + NET_API_STATUS status; + + if ((argc > 0) && (strcasecmp_m(argv[0], "HELP") == 0)) { + net_offlinejoin_usage(c, argc, argv); + return 0; + } + + if (argc == 0) { + net_offlinejoin_usage(c, argc, argv); + return -1; + } + + net_warn_member_options(); + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + if (c->opt_kerberos) { + libnetapi_set_use_kerberos(c->netapi_ctx); + } + + if (strcasecmp_m(argv[0], "provision") == 0) { + ret = net_offlinejoin_provision(c, argc, argv); + if (ret != 0) { + return ret; + } + } + + if (strcasecmp_m(argv[0], "requestodj") == 0) { + ret = net_offlinejoin_requestodj(c, argc, argv); + if (ret != 0) { + return ret; + } + } + + if (strcasecmp_m(argv[0], "composeodj") == 0) { + ret = net_offlinejoin_composeodj(c, argc, argv); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static int net_offlinejoin_provision_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet offlinejoin provision [misc. options]\n" + "\tProvisions machine account in AD\n")); + d_printf(_("Valid options:\n")); + d_printf(_("\tdomain=<DOMAIN>\t\t\t\tDefines AD Domain to join\n")); + d_printf(_("\tmachine_name=<MACHINE_NAME>\t\tDefines the machine account name\n")); + d_printf(_("\tmachine_account_ou=<OU>\t\t\tDefines the machine account organizational unit DN\n")); + d_printf(_("\tdcname=<DCNAME>\t\t\t\tSpecify a Domain Controller to join to\n")); + d_printf(_("\tdefpwd\t\t\t\t\tUse default machine account password\n")); + d_printf(_("\treuse\t\t\t\t\tReuse existing machine account in AD\n")); + d_printf(_("\tsavefile=<FILENAME>\t\t\tFile to store the ODJ data\n")); + d_printf(_("\tprintblob\t\t\t\tPrint the base64 encoded ODJ data on stdout\n")); + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_offlinejoin_provision(struct net_context *c, + int argc, const char **argv) +{ + NET_API_STATUS status; + const char *dcname = NULL; + const char *domain = NULL; + const char *machine_name = NULL; + const char *machine_account_ou = NULL; + const char *provision_text_data = NULL; + uint32_t options = 0; + const char *savefile = NULL; + bool printblob = false; + int i; + + if (c->display_usage || argc == 1) { + return net_offlinejoin_provision_usage(c, argc, argv); + } + + /* process additional command line args */ + + for (i = 0; i < argc; i++) { + + if (strnequal(argv[i], "domain", strlen("domain"))) { + domain = get_string_param(argv[i]); + if (domain == NULL) { + return -1; + } + } + if (strnequal(argv[i], "machine_name", strlen("machine_name"))) { + machine_name = get_string_param(argv[i]); + if (machine_name == NULL) { + return -1; + } + } + if (strnequal(argv[i], "machine_account_ou", strlen("machine_account_ou"))) { + machine_account_ou = get_string_param(argv[i]); + if (machine_account_ou == NULL) { + return -1; + } + } + if (strnequal(argv[i], "dcname", strlen("dcname"))) { + dcname = get_string_param(argv[i]); + if (dcname == NULL) { + return -1; + } + } + if (strnequal(argv[i], "defpwd", strlen("defpwd"))) { + options |= NETSETUP_PROVISION_USE_DEFAULT_PASSWORD; + } + if (strnequal(argv[i], "reuse", strlen("reuse"))) { + options |= NETSETUP_PROVISION_REUSE_ACCOUNT; + } + if (strnequal(argv[i], "savefile", strlen("savefile"))) { + savefile = get_string_param(argv[i]); + if (savefile == NULL) { + return -1; + } + } + if (strnequal(argv[i], "printblob", strlen("printblob"))) { + printblob = true; + } + } + + if (domain == NULL) { + d_printf("Failed to provision computer account: %s\n", + libnetapi_errstr(W_ERROR_V(WERR_INVALID_DOMAINNAME))); + return -1; + } + + if (machine_name == NULL) { + d_printf("Failed to provision computer account: %s\n", + libnetapi_errstr(W_ERROR_V(WERR_INVALID_COMPUTERNAME))); + return -1; + } + + status = NetProvisionComputerAccount(domain, + machine_name, + machine_account_ou, + dcname, + options, + NULL, + 0, + &provision_text_data); + if (status != 0) { + d_printf("Failed to provision computer account: %s\n", + libnetapi_get_error_string(c->netapi_ctx, status)); + return status; + } + + if (savefile != NULL) { + + DATA_BLOB ucs2_blob, blob; + bool ok; + + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs + * so we also do it for compatibility. Someone may provision an + * account for a Windows machine with samba. + */ + ok = push_reg_sz(c, &ucs2_blob, provision_text_data); + if (!ok) { + return -1; + } + + /* Add the unicode BOM mark */ + blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2); + if (blob.data == NULL) { + d_printf("Failed to allocate blob: %s\n", + strerror(errno)); + return -1; + } + + blob.data[0] = 0xff; + blob.data[1] = 0xfe; + + memcpy(blob.data + 2, ucs2_blob.data, ucs2_blob.length); + + ok = file_save(savefile, blob.data, blob.length); + if (!ok) { + d_printf("Failed to save %s: %s\n", savefile, + strerror(errno)); + return -1; + } + } + + d_printf("Successfully provisioned computer '%s' in domain '%s'\n", + machine_name, domain); + + if (printblob) { + printf("%s\n", provision_text_data); + } + + return 0; +} + +static int net_offlinejoin_requestodj_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet offlinejoin requestodj [misc. options]\n" + "\tRequests offline domain join\n")); + d_printf(_("Valid options:\n")); + d_printf(_("\t-i\t\t\t\t\tRead ODJ data from STDIN\n")); + d_printf(_("\tloadfile=<FILENAME>\t\t\tFile that provides the ODJ data\n")); + /*d_printf(_("\tlocalos\t\t\t\t\tModify the local machine\n"));*/ + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_offlinejoin_requestodj(struct net_context *c, + int argc, const char **argv) +{ + NET_API_STATUS status; + uint8_t *provision_bin_data = NULL; + size_t provision_bin_data_size = 0; + uint32_t options = NETSETUP_PROVISION_ONLINE_CALLER; + const char *windows_path = NULL; + int i; + + if (c->display_usage) { + return net_offlinejoin_requestodj_usage(c, argc, argv); + } + + /* process additional command line args */ + + for (i = 0; i < argc; i++) { + + if (strnequal(argv[i], "loadfile", strlen("loadfile"))) { + const char *loadfile = NULL; + + loadfile = get_string_param(argv[i]); + if (loadfile == NULL) { + return -1; + } + + provision_bin_data = + (uint8_t *)file_load(loadfile, + &provision_bin_data_size, + 0, + c); + if (provision_bin_data == NULL) { + d_printf("Failed to read loadfile: %s\n", + loadfile); + return -1; + } + } +#if 0 + if (strnequal(argv[i], "localos", strlen("localos"))) { + options |= NETSETUP_PROVISION_ONLINE_CALLER; + } +#endif + } + + if (c->opt_stdin) { + if (isatty(STDIN_FILENO) == 1) { + d_fprintf(stderr, + "hint: stdin waiting for ODJ blob, end " + "with <crtl-D>.\n"); + } + provision_bin_data = + (uint8_t *)fd_load(STDIN_FILENO, + &provision_bin_data_size, 0, c); + if (provision_bin_data == NULL) { + d_printf("Failed to read ODJ blob from stdin\n"); + return -1; + } + + /* Strip last newline */ + if (provision_bin_data[provision_bin_data_size - 1] == '\n') { + provision_bin_data[provision_bin_data_size - 1] = '\0'; + } + } + + if (provision_bin_data == NULL || provision_bin_data_size == 0) { + d_printf("Please provide provision data either from file " + "(using loadfile parameter) or from stdin (-i)\n"); + return -1; + } + if (provision_bin_data_size > UINT32_MAX) { + d_printf("provision binary data size too big: %zu\n", + provision_bin_data_size); + return -1; + } + + status = NetRequestOfflineDomainJoin(provision_bin_data, + provision_bin_data_size, + options, + windows_path); + if (status != 0 && status != 0x00000a99) { + /* NERR_JoinPerformedMustRestart */ + printf("Failed to call NetRequestOfflineDomainJoin: %s\n", + libnetapi_get_error_string(c->netapi_ctx, status)); + return -1; + } + + d_printf("Successfully requested Offline Domain Join\n"); + + return 0; +} + +static int net_offlinejoin_composeodj_usage(struct net_context *c, + int argc, + const char **argv) +{ + d_printf(_("\nnet offlinejoin composeodj [misc. options]\n" + "\tComposes offline domain join blob\n")); + d_printf(_("Valid options:\n")); + d_printf(_("\tdomain_sid=<SID>\t\t\tThe domain SID\n")); + d_printf(_("\tdomain_guid=<GUID>\t\t\tThe domain GUID\n")); + d_printf(_("\tforest_name=<NAME>\t\t\tThe forest name\n")); + d_printf(_("\tdomain_is_nt4\t\t\t\tThe domain not AD but NT4\n")); + d_printf(_("\tsavefile=<FILENAME>\t\t\tFile to store the ODJ data\n")); + d_printf(_("\tprintblob\t\t\t\tPrint the base64 encoded ODJ data on stdout\n")); + net_common_flags_usage(c, argc, argv); + d_printf(_("Example:\n")); + d_printf("\tnet offlinejoin composeodj --realm=<realm> " + "--workgroup=<domain> domain_sid=<sid> domain_guid=<guid> " + "forest_name=<name> -S <dc name> -I <dc address> " + "--password=<password> printblob\n"); + return -1; +} + +int net_offlinejoin_composeodj(struct net_context *c, + int argc, + const char **argv) +{ + struct cli_credentials *creds = samba_cmdline_get_creds(); + NET_API_STATUS status; + const char *dns_domain_name = NULL; + const char *netbios_domain_name = NULL; + const char *machine_account_name = NULL; + const char *machine_account_password = NULL; + const char *domain_sid_str = NULL; + const char *domain_guid_str = NULL; + struct dom_sid domain_sid; + struct GUID domain_guid; + const char *forest_name = NULL; + const char *dc_name = NULL; + char dc_address[INET6_ADDRSTRLEN] = { 0 }; + bool domain_is_ad = true; + const char *provision_text_data = NULL; + const char *savefile = NULL; + bool printblob = false; + enum credentials_obtained obtained; + bool ok; + NTSTATUS ntstatus; + int i; + + if (c->display_usage || argc < 4) { + return net_offlinejoin_composeodj_usage(c, argc, argv); + } + + dns_domain_name = cli_credentials_get_realm(creds); + netbios_domain_name = cli_credentials_get_domain(creds); + + machine_account_name = cli_credentials_get_username_and_obtained(creds, &obtained); + if (obtained < CRED_CALLBACK_RESULT) { + const char *netbios_name = cli_credentials_get_workstation(creds); + cli_credentials_set_username( + creds, + talloc_asprintf(c, "%s$", netbios_name), + CRED_SPECIFIED); + } + + machine_account_name = cli_credentials_get_username(creds); + machine_account_password = cli_credentials_get_password(creds); + dc_name = c->opt_host; + + if (c->opt_have_ip) { + struct sockaddr_in *in4 = NULL; + struct sockaddr_in6 *in6 = NULL; + const char *p = NULL; + + switch(c->opt_dest_ip.ss_family) { + case AF_INET: + in4 = (struct sockaddr_in *)&c->opt_dest_ip; + p = inet_ntop(AF_INET, &in4->sin_addr, dc_address, sizeof(dc_address)); + break; + case AF_INET6: + in6 = (struct sockaddr_in6 *)&c->opt_dest_ip; + p = inet_ntop(AF_INET6, &in6->sin6_addr, dc_address, sizeof(dc_address)); + break; + default: + d_printf("Unknown IP address family\n"); + return -1; + } + + if (p == NULL) { + d_fprintf(stderr, "Failed to parse IP address: %s\n", strerror(errno)); + return -1; + } + } + + /* process additional command line args */ + + for (i = 0; i < argc; i++) { + if (strnequal(argv[i], "domain_sid", strlen("domain_sid"))) { + domain_sid_str = get_string_param(argv[i]); + if (domain_sid_str == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "domain_guid", strlen("domain_guid"))) { + domain_guid_str = get_string_param(argv[i]); + if (domain_guid_str == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "forest_name", strlen("forest_name"))) { + forest_name = get_string_param(argv[i]); + if (forest_name == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "savefile", strlen("savefile"))) { + savefile = get_string_param(argv[i]); + if (savefile == NULL) { + return -1; + } + } + + if (strnequal(argv[i], "printblob", strlen("printblob"))) { + printblob = true; + } + + if (strnequal(argv[i], "domain_is_nt4", strlen("domain_is_nt4"))) { + domain_is_ad = false; + } + } + + /* Check command line arguments */ + + if (savefile == NULL && !printblob) { + d_printf("Choose either save the blob to a file or print it\n"); + return -1; + } + + if (dns_domain_name == NULL) { + d_printf("Please provide a valid realm parameter (--realm)\n"); + return -1; + } + + if (netbios_domain_name == NULL) { + d_printf("Please provide a valid domain parameter (--workgroup)\n"); + return -1; + } + + if (dc_name == NULL) { + d_printf("Please provide a valid DC name parameter (-S)\n"); + return -1; + } + + if (strlen(dc_address) == 0) { + d_printf("Please provide a valid domain controller address parameter (-I)\n"); + return -1; + } + + if (machine_account_name == NULL) { + d_printf("Please provide a valid netbios name parameter\n"); + return -1; + } + + if (machine_account_password == NULL) { + d_printf("Please provide a valid password parameter\n"); + return -1; + } + + if (domain_sid_str == NULL) { + d_printf("Please provide a valid <domain_sid> parameter\n"); + return -1; + } + + if (domain_guid_str == NULL) { + d_printf("Please provide a valid <domain_guid> parameter\n"); + return -1; + } + + if (forest_name == NULL) { + d_printf("Please provide a valid <forest_name> parameter\n"); + return -1; + } + + ok = dom_sid_parse(domain_sid_str, &domain_sid); + if (!ok) { + d_fprintf(stderr, _("Failed to parse domain SID\n")); + return -1; + } + + ntstatus = GUID_from_string(domain_guid_str, &domain_guid); + if (NT_STATUS_IS_ERR(ntstatus)) { + d_fprintf(stderr, _("Failed to parse domain GUID\n")); + return -1; + } + + status = NetComposeOfflineDomainJoin(dns_domain_name, + netbios_domain_name, + (struct domsid *)&domain_sid, + &domain_guid, + forest_name, + machine_account_name, + machine_account_password, + dc_name, + dc_address, + domain_is_ad, + NULL, + 0, + &provision_text_data); + if (status != 0) { + d_printf("Failed to compose offline domain join blob: %s\n", + libnetapi_get_error_string(c->netapi_ctx, status)); + return status; + } + + if (savefile != NULL) { + DATA_BLOB ucs2_blob, blob; + + /* + * Windows produces and consumes UTF16/UCS2 encoded blobs + * so we also do it for compatibility. Someone may provision an + * account for a Windows machine with samba. + */ + ok = push_reg_sz(c, &ucs2_blob, provision_text_data); + if (!ok) { + return -1; + } + + /* Add the unicode BOM mark */ + blob = data_blob_talloc(c, NULL, ucs2_blob.length + 2); + if (blob.data == NULL) { + d_printf("Failed to allocate blob: %s\n", + strerror(errno)); + return -1; + } + + blob.data[0] = 0xff; + blob.data[1] = 0xfe; + + memcpy(blob.data + 2, ucs2_blob.data, ucs2_blob.length); + + ok = file_save(savefile, blob.data, blob.length); + if (!ok) { + d_printf("Failed to save %s: %s\n", savefile, + strerror(errno)); + return -1; + } + } + + if (printblob) { + printf("%s\n", provision_text_data); + } + + return 0; +} diff --git a/source3/utils/net_printing.c b/source3/utils/net_printing.c new file mode 100644 index 0000000..04a3acc --- /dev/null +++ b/source3/utils/net_printing.c @@ -0,0 +1,592 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Local printing tdb migration interface + + Copyright (C) Guenther Deschner 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "system/filesys.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "rpc_client/cli_pipe.h" +#include "librpc/gen_ndr/ndr_ntprinting.h" +#include "librpc/gen_ndr/ndr_spoolss.h" +#include "../libcli/security/security.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "../librpc/gen_ndr/ndr_winreg.h" +#include "util_tdb.h" +#include "printing/nt_printing_migrate.h" +#include "lib/param/param.h" + +#define FORMS_PREFIX "FORMS/" +#define FORMS_PREFIX_LEN 6 +#define DRIVERS_PREFIX "DRIVERS/" +#define DRIVERS_PREFIX_LEN 8 +#define PRINTERS_PREFIX "PRINTERS/" +#define PRINTERS_PREFIX_LEN 9 +#define SECDESC_PREFIX "SECDESC/" +#define SECDESC_PREFIX_LEN 8 + +#define ARG_ENCODING "encoding=" + +struct printing_opts { + const char *encoding; + const char *tdb; +}; + +static NTSTATUS printing_parse_args(TALLOC_CTX *mem_ctx, + struct printing_opts **popts, + int argc, const char **argv) +{ + size_t c; + struct printing_opts *o; + + if (argc == 0) { + return NT_STATUS_INVALID_PARAMETER; + } + + o = talloc_zero(mem_ctx, struct printing_opts); + if (o == NULL) { + return NT_STATUS_INVALID_PARAMETER; + } + + for (c = 0; c < argc; c++) { + if (strnequal(argv[c], ARG_ENCODING, sizeof(ARG_ENCODING) - 1)) { + o->encoding = talloc_strdup(o, + argv[c] + sizeof(ARG_ENCODING) - 1); + if (o->encoding == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + o->tdb = talloc_strdup(o, argv[c]); + if (o->tdb == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + } + + *popts = o; + return NT_STATUS_OK; +} + +static void dump_form(TALLOC_CTX *mem_ctx, + const char *key_name, + unsigned char *data, + size_t length) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + char *s; + struct ntprinting_form r; + + printf("found form: %s\n", key_name); + + blob = data_blob_const(data, length); + + ZERO_STRUCT(r); + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_ntprinting_form); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("form pull failed: %s\n"), + ndr_errstr(ndr_err)); + return; + } + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_form, &r); + if (s) { + printf("%s\n", s); + } +} + +static void dump_driver(TALLOC_CTX *mem_ctx, + const char *key_name, + unsigned char *data, + size_t length, + bool do_string_conversion) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + char *s; + struct ntprinting_driver r; + + printf("found driver: %s\n", key_name); + + blob = data_blob_const(data, length); + + ZERO_STRUCT(r); + + if (do_string_conversion) { + r.string_flags = LIBNDR_FLAG_STR_ASCII; + } + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_ntprinting_driver); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("driver pull failed: %s\n"), + ndr_errstr(ndr_err)); + return; + } + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_driver, &r); + if (s) { + printf("%s\n", s); + } +} + +static void dump_printer(TALLOC_CTX *mem_ctx, + const char *key_name, + unsigned char *data, + size_t length, + bool do_string_conversion) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + char *s; + struct ntprinting_printer r; + + printf("found printer: %s\n", key_name); + + blob = data_blob_const(data, length); + + ZERO_STRUCT(r); + + if (do_string_conversion) { + r.info.string_flags = LIBNDR_FLAG_STR_ASCII; + } + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_ntprinting_printer); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("printer pull failed: %s\n"), + ndr_errstr(ndr_err)); + return; + } + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, ntprinting_printer, &r); + if (s) { + printf("%s\n", s); + } +} + +static void dump_sd(TALLOC_CTX *mem_ctx, + const char *key_name, + unsigned char *data, + size_t length) +{ + enum ndr_err_code ndr_err; + DATA_BLOB blob; + char *s; + struct sec_desc_buf r; + + printf("found security descriptor: %s\n", key_name); + + blob = data_blob_const(data, length); + + ZERO_STRUCT(r); + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &r, + (ndr_pull_flags_fn_t)ndr_pull_sec_desc_buf); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_fprintf(stderr, _("security descriptor pull failed: %s\n"), + ndr_errstr(ndr_err)); + return; + } + + s = NDR_PRINT_STRUCT_STRING(mem_ctx, sec_desc_buf, &r); + if (s) { + printf("%s\n", s); + } +} + + +static int net_printing_dump(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + TALLOC_CTX *ctx = talloc_stackframe(); + TDB_CONTEXT *tdb; + TDB_DATA kbuf, newkey, dbuf; + struct printing_opts *o; + const char *save_dos_charset = lp_dos_charset(); + bool do_string_conversion = false; + NTSTATUS status; + + if (argc < 1 || c->display_usage) { + d_printf( "%s\n" + "net printing dump [options] <file.tdb>\n" + " %s\n", + _("Usage:"), + _("Dump formatted printer information of the tdb.")); + d_printf(_("Valid options:\n")); + d_printf(_(" encoding=<CP> Set the Code Page of the tdb file.\n" + " See iconv -l for the list of CP values\n" + " (CP1252 is Western latin1, CP1251 is Cyrillic).\n")); + goto done; + } + + status = printing_parse_args(ctx, &o, argc, argv); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("failed to parse arguments\n")); + goto done; + } + + tdb = tdb_open_log(o->tdb, 0, TDB_DEFAULT, O_RDONLY, 0600); + if (!tdb) { + d_fprintf(stderr, _("failed to open tdb file: %s\n"), o->tdb); + goto done; + } + + if (o->encoding != NULL) { + lpcfg_set_cmdline(c->lp_ctx, "dos charset", o->encoding); + d_fprintf(stderr, _("do string conversion from %s to %s\n"), + lp_dos_charset(), lp_unix_charset()); + do_string_conversion = true; + } + + for (kbuf = tdb_firstkey(tdb); + kbuf.dptr; + newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf=newkey) + { + int cmp; + + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) { + continue; + } + + cmp = strncmp((const char *)kbuf.dptr, + FORMS_PREFIX, + FORMS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(ctx, + &key_name, + (const char *) kbuf.dptr + strlen(FORMS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + dump_form(ctx, key_name, dbuf.dptr, dbuf.dsize); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + + cmp = strncmp((const char *)kbuf.dptr, + DRIVERS_PREFIX, + DRIVERS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(ctx, + &key_name, + (const char *) kbuf.dptr + strlen(DRIVERS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + dump_driver(ctx, + key_name, + dbuf.dptr, + dbuf.dsize, + do_string_conversion); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + + cmp = strncmp((const char *)kbuf.dptr, + PRINTERS_PREFIX, + PRINTERS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(ctx, + &key_name, + (const char *) kbuf.dptr + strlen(PRINTERS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + dump_printer(ctx, + key_name, + dbuf.dptr, + dbuf.dsize, + do_string_conversion); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + + cmp = strncmp((const char *)kbuf.dptr, + SECDESC_PREFIX, + SECDESC_PREFIX_LEN); + if (cmp == 0) { + dump_sd(ctx, (const char *)kbuf.dptr+strlen(SECDESC_PREFIX), dbuf.dptr, dbuf.dsize); + SAFE_FREE(dbuf.dptr); + continue; + } + + } + + ret = 0; + + done: + lpcfg_set_cmdline(c->lp_ctx, "dos charset", save_dos_charset); + talloc_free(ctx); + return ret; +} + +static NTSTATUS printing_migrate_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *winreg_pipe, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct printing_opts *o; + TALLOC_CTX *tmp_ctx; + TDB_CONTEXT *tdb; + TDB_DATA kbuf, newkey, dbuf; + NTSTATUS status; + const char *save_dos_charset = lp_dos_charset(); + bool do_string_conversion = false; + + tmp_ctx = talloc_new(mem_ctx); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + status = printing_parse_args(tmp_ctx, &o, argc, argv); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("failed to parse arguments\n")); + goto done; + } + + tdb = tdb_open_log(o->tdb, 0, TDB_DEFAULT, O_RDONLY, 0600); + if (tdb == NULL) { + d_fprintf(stderr, _("failed to open tdb file: %s\n"), o->tdb); + status = NT_STATUS_NO_SUCH_FILE; + goto done; + } + + if (o->encoding != NULL) { + lpcfg_set_cmdline(c->lp_ctx, "dos charset", o->encoding); + d_fprintf(stderr, _("do string conversion from %s to %s\n"), + lp_dos_charset(), lp_unix_charset()); + do_string_conversion = true; + } + + for (kbuf = tdb_firstkey(tdb); + kbuf.dptr; + newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf = newkey) + { + int cmp; + + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) { + continue; + } + + cmp = strncmp((const char *) kbuf.dptr, + FORMS_PREFIX, + FORMS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(tmp_ctx, + &key_name, + (const char *) kbuf.dptr + strlen(FORMS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + printing_tdb_migrate_form(tmp_ctx, + winreg_pipe, + key_name, + dbuf.dptr, + dbuf.dsize); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + + cmp = strncmp((const char *) kbuf.dptr, + DRIVERS_PREFIX, + DRIVERS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(tmp_ctx, + &key_name, + (const char *) kbuf.dptr + strlen(DRIVERS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + printing_tdb_migrate_driver(tmp_ctx, + winreg_pipe, + key_name, + dbuf.dptr, + dbuf.dsize, + do_string_conversion); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + + cmp = strncmp((const char *) kbuf.dptr, + PRINTERS_PREFIX, + PRINTERS_PREFIX_LEN); + if (cmp == 0) { + char *key_name = NULL; + size_t converted_size = 0; + bool ok; + + ok = pull_ascii_talloc(tmp_ctx, + &key_name, + (const char *) kbuf.dptr + strlen(PRINTERS_PREFIX), + &converted_size); + if (!ok) { + continue; + } + + printing_tdb_migrate_printer(tmp_ctx, + winreg_pipe, + key_name, + dbuf.dptr, + dbuf.dsize, + do_string_conversion); + TALLOC_FREE(key_name); + SAFE_FREE(dbuf.dptr); + continue; + } + SAFE_FREE(dbuf.dptr); + } + + for (kbuf = tdb_firstkey(tdb); + kbuf.dptr; + newkey = tdb_nextkey(tdb, kbuf), free(kbuf.dptr), kbuf = newkey) + { + dbuf = tdb_fetch(tdb, kbuf); + if (!dbuf.dptr) { + continue; + } + + if (strncmp((const char *) kbuf.dptr, SECDESC_PREFIX, strlen(SECDESC_PREFIX)) == 0) { + printing_tdb_migrate_secdesc(tmp_ctx, + winreg_pipe, + (const char *) kbuf.dptr + strlen(SECDESC_PREFIX), + dbuf.dptr, + dbuf.dsize); + SAFE_FREE(dbuf.dptr); + continue; + } + SAFE_FREE(dbuf.dptr); + + } + + status = NT_STATUS_OK; + + done: + lpcfg_set_cmdline(c->lp_ctx, "dos charset", save_dos_charset); + talloc_free(tmp_ctx); + return status; +} + +static int net_printing_migrate(struct net_context *c, + int argc, + const char **argv) +{ + if (argc < 1 || c->display_usage) { + d_printf( "%s\n" + "net printing migrate [options] <file.tdb>\n" + " %s\n", + _("Usage:"), + _("Migrate tdb printing files to new storage")); + d_printf(_("Valid options:\n")); + d_printf(_(" encoding=<CP> Set the Code Page of the tdb file.\n" + " See iconv -l for the list of CP values\n" + " (CP1252 is Western latin1, CP1251 is Cyrillic).\n")); + return 0; + } + + return run_rpc_command(c, + NULL, + &ndr_table_winreg, + 0, + printing_migrate_internal, + argc, + argv); +} +/** + * 'net printing' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_printing(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + + struct functable func[] = { + { + "dump", + net_printing_dump, + NET_TRANSPORT_LOCAL, + N_("Dump printer databases"), + N_("net printing dump\n" + " Dump tdb printing file") + }, + + { + "migrate", + net_printing_migrate, + NET_TRANSPORT_LOCAL | NET_TRANSPORT_RPC, + N_("Migrate printer databases"), + N_("net printing migrate\n" + " Migrate tdb printing files to new storage") + }, + + { NULL, NULL, 0, NULL, NULL } + }; + + ret = net_run_function(c, argc, argv, "net printing", func); + + return ret; +} diff --git a/source3/utils/net_proto.h b/source3/utils/net_proto.h new file mode 100644 index 0000000..ccfa89a --- /dev/null +++ b/source3/utils/net_proto.h @@ -0,0 +1,488 @@ +/* + * Unix SMB/CIFS implementation. + * collected prototypes header + * + * frozen from "make proto" in May 2008 + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _NET_PROTO_H_ +#define _NET_PROTO_H_ + +#include "ads.h" +#include "libads/ads_status.h" +#include "librpc/gen_ndr/libnet_join.h" + +/* The following definitions come from utils/net.c */ + +enum netr_SchannelType get_sec_channel_type(const char *param); + +/* The following definitions come from utils/net_ads.c */ +struct ads_struct; +ADS_STATUS ads_startup(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + struct ads_struct **ads); +ADS_STATUS ads_startup_nobind(struct net_context *c, + bool only_own_domain, + TALLOC_CTX *mem_ctx, + struct ads_struct **ads); +int net_ads_check_our_domain(struct net_context *c); +int net_ads_check(struct net_context *c); +int net_ads_user(struct net_context *c, int argc, const char **argv); +int net_ads_group(struct net_context *c, int argc, const char **argv); +int net_ads_testjoin(struct net_context *c, int argc, const char **argv); +int net_ads_join(struct net_context *c, int argc, const char **argv); +int net_ads_printer_usage(struct net_context *c, int argc, const char **argv); +int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv); +int net_ads_keytab(struct net_context *c, int argc, const char **argv); +int net_ads_kerberos(struct net_context *c, int argc, const char **argv); +int net_ads_setspn(struct net_context *c, int argc, const char **argv); +int net_ads(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_ads_join_dns.c */ +void use_in_memory_ccache(void); +NTSTATUS net_update_dns_ext(struct net_context *c, + TALLOC_CTX *mem_ctx, ADS_STRUCT *ads, + const char *hostname, + struct sockaddr_storage *iplist, + int num_addrs, bool remove_host); +void net_ads_join_dns_updates(struct net_context *c, TALLOC_CTX *ctx, struct libnet_JoinCtx *r); + +/* The following definitions come from utils/net_ads_gpo.c */ + +int net_ads_gpo(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_cache.c */ + +int net_cache(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_conf.c */ + +int net_conf(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_dns.c */ + +int get_my_ip_address( struct sockaddr_storage **pp_ss ); + +/* The following definitions come from utils/net_dom.c */ + +int net_dom_usage(struct net_context *c, int argc, const char **argv); +int net_dom(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_file.c */ + +int net_file_usage(struct net_context *c, int argc, const char **argv); +int net_file(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_group.c */ + +int net_group_usage(struct net_context *c, int argc, const char **argv); +int net_group(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_groupmap.c */ + +int net_groupmap_usage(struct net_context *c, int argc, const char **argv); +int net_groupmap(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_help.c */ + +int net_help(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_idmap.c */ + +int net_idmap(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_join.c */ + +int net_join_usage(struct net_context *c, int argc, const char **argv); +int net_join(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from source3/utils/net_offlinejoin.c */ + +int net_offlinejoin_usage(struct net_context *c, int argc, const char **argv); +int net_offlinejoin(struct net_context *c, int argc, const char **argv); +int net_offlinejoin_provision(struct net_context *c, + int argc, const char **argv); +int net_offlinejoin_requestodj(struct net_context *c, + int argc, const char **argv); +int net_offlinejoin_composeodj(struct net_context *c, + int argc, const char **argv); + +/* The following definitions come from utils/net_lookup.c */ + +int net_lookup_usage(struct net_context *c, int argc, const char **argv); +int net_lookup(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rap.c */ + +int net_rap_file_usage(struct net_context *c, int argc, const char **argv); +int net_rap_file(struct net_context *c, int argc, const char **argv); +int net_rap_share_usage(struct net_context *c, int argc, const char **argv); +int net_rap_share(struct net_context *c, int argc, const char **argv); +int net_rap_session_usage(struct net_context *c, int argc, const char **argv); +int net_rap_session(struct net_context *c, int argc, const char **argv); +int net_rap_server_usage(struct net_context *c, int argc, const char **argv); +int net_rap_server(struct net_context *c, int argc, const char **argv); +int net_rap_domain_usage(struct net_context *c, int argc, const char **argv); +int net_rap_domain(struct net_context *c, int argc, const char **argv); +int net_rap_printq_usage(struct net_context *c, int argc, const char **argv); +int net_rap_printq(struct net_context *c, int argc, const char **argv); +int net_rap_user(struct net_context *c, int argc, const char **argv); +int net_rap_group_usage(struct net_context *c, int argc, const char **argv); +int net_rap_group(struct net_context *c, int argc, const char **argv); +int net_rap_groupmember_usage(struct net_context *c, int argc, const char **argv); +int net_rap_groupmember(struct net_context *c, int argc, const char **argv); +int net_rap_validate_usage(struct net_context *c, int argc, const char **argv); +int net_rap_validate(struct net_context *c, int argc, const char **argv); +int net_rap_service_usage(struct net_context *c, int argc, const char **argv); +int net_rap_service(struct net_context *c, int argc, const char **argv); +int net_rap_password_usage(struct net_context *c, int argc, const char **argv); +int net_rap_password(struct net_context *c, int argc, const char **argv); +int net_rap_admin_usage(struct net_context *c, int argc, const char **argv); +int net_rap_admin(struct net_context *c, int argc, const char **argv); +int net_rap(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_registry.c */ + +int net_registry(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc.c */ + +NTSTATUS net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx, + struct dom_sid **domain_sid, + const char **domain_name); +int run_rpc_command(struct net_context *c, + struct cli_state *cli_arg, + const struct ndr_interface_table *table, + int conn_flags, + rpc_command_fn fn, + int argc, + const char **argv); +int net_rpc_changetrustpw(struct net_context *c, int argc, const char **argv); +int net_rpc_testjoin(struct net_context *c, int argc, const char **argv); +int net_rpc_join(struct net_context *c, int argc, const char **argv); +NTSTATUS rpc_info_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +int net_rpc_info(struct net_context *c, int argc, const char **argv); +int net_rpc_getsid(struct net_context *c, int argc, const char **argv); +int net_rpc_user(struct net_context *c, int argc, const char **argv); +struct rpc_sh_cmd *net_rpc_user_edit_cmds(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); +struct rpc_sh_cmd *net_rpc_user_cmds(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); +int net_rpc_group(struct net_context *c, int argc, const char **argv); +bool copy_top_level_perms(struct net_context *c, + struct copy_clistate *cp_clistate, + const char *sharename); +int net_usersidlist(struct net_context *c, int argc, const char **argv); +int net_usersidlist_usage(struct net_context *c, int argc, const char **argv); +int net_rpc_share(struct net_context *c, int argc, const char **argv); +struct rpc_sh_cmd *net_rpc_share_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); +int net_rpc_file(struct net_context *c, int argc, const char **argv); +NTSTATUS rpc_init_shutdown_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_reg_shutdown_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +bool net_rpc_check(struct net_context *c, unsigned flags); +int rpc_printer_migrate(struct net_context *c, int argc, const char **argv); +int rpc_printer_usage(struct net_context *c, int argc, const char **argv); +int net_rpc_printer(struct net_context *c, int argc, const char **argv); +int net_rpc(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_audit.c */ + +int net_rpc_audit(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_printer.c */ + +NTSTATUS net_copy_fileattr(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *src_name, const char *dst_name, + bool copy_acls, bool copy_attrs, + bool copy_timestamps, bool is_file); +NTSTATUS net_copy_file(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *src_name, const char *dst_name, + bool copy_acls, bool copy_attrs, + bool copy_timestamps, bool is_file); +NTSTATUS rpc_printer_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_driver_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_publish_publish_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_publish_unpublish_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_publish_update_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_publish_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_migrate_security_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_migrate_forms_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_migrate_drivers_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_migrate_printers_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); +NTSTATUS rpc_printer_migrate_settings_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv); + +/* The following definitions come from utils/net_rpc_registry.c */ + +int net_rpc_registry(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_rights.c */ + +int net_rpc_rights(struct net_context *c, int argc, const char **argv); +struct rpc_sh_cmd *net_rpc_rights_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); + +/* The following definitions come from utils/net_rpc_samsync.c */ + +int rpc_vampire_usage(struct net_context *c, int argc, const char **argv); +int rpc_vampire_passdb(struct net_context *c, int argc, const char **argv); +int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_service.c */ + +const char *svc_status_string( uint32_t state ); +int net_rpc_service(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_sh_acct.c */ + +struct rpc_sh_cmd *net_rpc_acct_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx); + +/* The following definitions come from utils/net_rpc_shell.c */ + +int net_rpc_shell(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_sam.c */ + +int net_sam(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_share.c */ + +int net_share_usage(struct net_context *c, int argc, const char **argv); +int net_share(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_status.c */ + +int net_status_usage(struct net_context *c, int argc, const char **argv); +int net_status(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_time.c */ + +int net_time_usage(struct net_context *c, int argc, const char **argv); +int net_time(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_user.c */ + +int net_user_usage(struct net_context *c, int argc, const char **argv); +int net_user(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_usershare.c */ + +int net_usershare_usage(struct net_context *c, int argc, const char **argv); +int net_usershare_help(struct net_context *c, int argc, const char **argv); +int net_usershare(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_eventlog.c */ + +int net_eventlog(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_printing.c */ + +int net_printing(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_serverid.c */ + +int net_serverid(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_util.c */ + +NTSTATUS net_rpc_lookup_name(struct net_context *c, + TALLOC_CTX *mem_ctx, struct cli_state *cli, + const char *name, const char **ret_domain, + const char **ret_name, struct dom_sid *ret_sid, + enum lsa_SidType *ret_type); +NTSTATUS connect_to_service(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name, + const char *service_name, + const char *service_type); +NTSTATUS connect_to_ipc(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name); +NTSTATUS connect_to_ipc_anonymous(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name); +NTSTATUS connect_dst_pipe(struct net_context *c, struct cli_state **cli_dst, + struct rpc_pipe_client **pp_pipe_hnd, + const struct ndr_interface_table *table); +int net_use_krb_machine_account(struct net_context *c); +bool net_find_server(struct net_context *c, + const char *domain, + unsigned flags, + struct sockaddr_storage *server_ss, + char **server_name); +bool net_find_pdc(struct sockaddr_storage *server_ss, + fstring server_name, + const char *domain_name); +NTSTATUS net_make_ipc_connection(struct net_context *c, unsigned flags, + struct cli_state **pcli); +NTSTATUS net_make_ipc_connection_ex(struct net_context *c ,const char *domain, + const char *server, + const struct sockaddr_storage *pss, + unsigned flags, struct cli_state **pcli); +const char *net_prompt_pass(struct net_context *c, const char *user); +int net_run_function(struct net_context *c, int argc, const char **argv, + const char *whoami, struct functable *table); +void net_display_usage_from_functable(struct functable *table); + +void net_warn_member_options(void); + +const char *net_share_type_str(int num_type); + +NTSTATUS net_scan_dc(struct net_context *c, + struct cli_state *cli, + struct net_dc_info *dc_info); + +/* The following definitions come from utils/netlookup.c */ + +NTSTATUS net_lookup_name_from_sid(struct net_context *c, + TALLOC_CTX *ctx, + struct dom_sid *psid, + const char **ppdomain, + const char **ppname); +NTSTATUS net_lookup_sid_from_name(struct net_context *c, TALLOC_CTX *ctx, + const char *full_name, struct dom_sid *pret_sid); + +/* The following definitions come from utils/net_g_lock.c */ +int net_g_lock(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_trust.c */ +int net_rpc_trust(struct net_context *c, int argc, const char **argv); + +/* The following definitions come from utils/net_rpc_conf.c */ +int net_rpc_conf(struct net_context *c, int argc, const char **argv); + +int net_notify(struct net_context *c, int argc, const char **argv); + +int net_tdb(struct net_context *c, int argc, const char **argv); + +int net_vfs(struct net_context *c, int argc, const char **argv); + +int net_witness(struct net_context *c, int argc, const char **argv); + +#endif /* _NET_PROTO_H_ */ diff --git a/source3/utils/net_rap.c b/source3/utils/net_rap.c new file mode 100644 index 0000000..9818623 --- /dev/null +++ b/source3/utils/net_rap.c @@ -0,0 +1,1386 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + Originally written by Steve and Jim. Largely rewritten by tridge in + November 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "../librpc/gen_ndr/rap.h" +#include "../librpc/gen_ndr/svcctl.h" +#include "utils/net.h" +#include "libsmb/libsmb.h" +#include "clirap2.h" +#include "../libcli/smb/smbXcli_base.h" + +/* The following messages were for error checking that is not properly + reported at the moment. Which should be reinstated? */ +#define ERRMSG_TARGET_WG_NOT_VALID "\nTarget workgroup option not valid "\ + "except on net rap server command, ignored" +#define ERRMSG_INVALID_HELP_OPTION "\nInvalid help option\n" + +#define ERRMSG_BOTH_SERVER_IPADDRESS "\nTarget server and IP address both "\ + "specified. Do not set both at the same time. The target IP address was used\n" + +static int errmsg_not_implemented(void) +{ + d_printf(_("\nNot implemented\n")); + return 0; +} + +int net_rap_file_usage(struct net_context *c, int argc, const char **argv) +{ + return net_file_usage(c, argc, argv); +} + +/*************************************************************************** + list info on an open file +***************************************************************************/ +static void file_fn(const char * pPath, const char * pUser, uint16_t perms, + uint16_t locks, uint32_t id) +{ + d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n", + id, pUser, perms, locks, pPath); +} + +static void one_file_fn(const char *pPath, const char *pUser, uint16_t perms, + uint16_t locks, uint32_t id) +{ + d_printf(_("File ID %d\n" + "User name %s\n" + "Locks 0x%-4.2x\n" + "Path %s\n" + "Permissions 0x%x\n"), + id, pUser, locks, pPath, perms); +} + + +static int rap_file_close(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0 || c->display_usage) { + return net_rap_file_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetFileClose(cli, atoi(argv[0])); + cli_shutdown(cli); + return ret; +} + +static int rap_file_info(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0 || c->display_usage) + return net_rap_file_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn); + cli_shutdown(cli); + return ret; +} + +static int rap_file_user(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) + return net_rap_file_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + /* list open files */ + + d_printf(_("\nEnumerating open files on remote server:\n\n" + "\nFileId Opened by Perms Locks Path \n" + "------ --------- ----- ----- ---- \n")); + ret = cli_NetFileEnum(cli, argv[0], NULL, file_fn); + + if (ret == -1) + d_printf(_("\nOperation not supported by server!\n\n")); + + cli_shutdown(cli); + return ret; +} + +int net_rap_file(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "close", + rap_file_close, + NET_TRANSPORT_RAP, + N_("Close specified file on server"), + N_("net rap file close\n" + " Close specified file on server") + }, + { + "user", + rap_file_user, + NET_TRANSPORT_RAP, + N_("List all files opened by username"), + N_("net rap file user\n" + " List all files opened by username") + }, + { + "info", + rap_file_info, + NET_TRANSPORT_RAP, + N_("Display info about an opened file"), + N_("net rap file info\n" + " Display info about an opened file") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap file\n" + " List all open files on rempte " + "server\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + /* list open files */ + + d_printf(_("\nEnumerating open files on remote server:\n\n" + "\nFileId Opened by Perms Locks Path\n" + "------ --------- ----- ----- ----\n")); + ret = cli_NetFileEnum(cli, NULL, NULL, file_fn); + + if (ret == -1) + d_printf(_("\nOperation not supported by server!\n\n")); + + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap file", func); +} + +int net_rap_share_usage(struct net_context *c, int argc, const char **argv) +{ + return net_share_usage(c, argc, argv); +} + +static void long_share_fn(const char *share_name, uint32_t type, + const char *comment, void *state) +{ + d_printf("%-12s %-8.8s %-50s\n", + share_name, net_share_type_str(type), comment); +} + +static void share_fn(const char *share_name, uint32_t type, + const char *comment, void *state) +{ + d_printf("%s\n", share_name); +} + +static int rap_share_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) { + return net_rap_share_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetShareDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_share_add(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + struct rap_share_info_2 sinfo; + char *p; + char *sharename; + + if (argc == 0 || c->display_usage) { + return net_rap_share_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + sharename = SMB_STRDUP(argv[0]); + p = strchr(sharename, '='); + if (p == NULL) { + d_printf(_("Server path not specified\n")); + SAFE_FREE(sharename); + return net_rap_share_usage(c, argc, argv); + } + *p = 0; + strlcpy((char *)sinfo.share_name, sharename, sizeof(sinfo.share_name)); + sinfo.reserved1 = '\0'; + sinfo.share_type = 0; + sinfo.comment = c->opt_comment ? smb_xstrdup(c->opt_comment) : ""; + sinfo.perms = 0; + sinfo.maximum_users = c->opt_maxusers; + sinfo.active_users = 0; + sinfo.path = p+1; + memset(sinfo.password, '\0', sizeof(sinfo.password)); + sinfo.reserved2 = '\0'; + + ret = cli_NetShareAdd(cli, &sinfo); + cli_shutdown(cli); + SAFE_FREE(sharename); + return ret; +} + + +int net_rap_share(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "delete", + rap_share_delete, + NET_TRANSPORT_RAP, + N_("Delete a share from server"), + N_("net rap share delete\n" + " Delete a share from server") + }, + { + "close", + rap_share_delete, + NET_TRANSPORT_RAP, + N_("Delete a share from server"), + N_("net rap share close\n" + " Delete a share from server\n" + " Alias for net rap share delete") + }, + { + "add", + rap_share_add, + NET_TRANSPORT_RAP, + N_("Add a share to server"), + N_("net rap share add\n" + " Add a share to server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap share\n" + " List all shares on remote server\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + if (c->opt_long_list_entries) { + d_printf(_( + "\nEnumerating shared resources (exports) on remote server:\n\n" + "\nShare name Type Description\n" + "---------- ---- -----------\n")); + ret = cli_RNetShareEnum(cli, long_share_fn, NULL); + } else { + ret = cli_RNetShareEnum(cli, share_fn, NULL); + } + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap share", func); +} + +int net_rap_session_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet rap session [misc. options] [targets]" + "\n\tenumerates all active SMB/CIFS sessions on target server\n")); + d_printf(_( + "\nnet rap session DELETE <client_name> [misc. options] [targets] \n" + "\tor" + "\nnet rap session CLOSE <client_name> [misc. options] [targets]" + "\n\tDeletes (closes) a session from specified client to server\n")); + d_printf(_( + "\nnet rap session INFO <client_name>" + "\n\tEnumerates all open files in specified session\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +static void list_sessions_func(char *wsname, char *username, uint16_t conns, + uint16_t opens, uint16_t users, uint32_t sess_time, + uint32_t idle_time, uint32_t user_flags, char *clitype) +{ + int hrs = idle_time / 3600; + int min = (idle_time / 60) % 60; + int sec = idle_time % 60; + + d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n", + wsname, username, clitype, opens, hrs, min, sec); +} + +static void display_session_func(const char *wsname, const char *username, + uint16_t conns, uint16_t opens, uint16_t users, + uint32_t sess_time, uint32_t idle_time, + uint32_t user_flags, const char *clitype) +{ + int ihrs = idle_time / 3600; + int imin = (idle_time / 60) % 60; + int isec = idle_time % 60; + int shrs = sess_time / 3600; + int smin = (sess_time / 60) % 60; + int ssec = sess_time % 60; + d_printf(_("User name %-20.20s\n" + "Computer %-20.20s\n" + "Guest logon %-20.20s\n" + "Client Type %-40.40s\n" + "Sess time %2.2d:%2.2d:%2.2d\n" + "Idle time %2.2d:%2.2d:%2.2d\n"), + username, wsname, + (user_flags&0x0)?_("yes"):_("no"), clitype, + shrs, smin, ssec, ihrs, imin, isec); +} + +static void display_conns_func(uint16_t conn_id, uint16_t conn_type, uint16_t opens, + uint16_t users, uint32_t conn_time, + const char *username, const char *netname) +{ + d_printf("%-14.14s %-8.8s %5d\n", + netname, net_share_type_str(conn_type), opens); +} + +static int rap_session_info(struct net_context *c, int argc, const char **argv) +{ + const char *sessname; + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) + return net_rap_session_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + sessname = argv[0]; + + ret = cli_NetSessionGetInfo(cli, sessname, display_session_func); + if (ret < 0) { + cli_shutdown(cli); + return ret; + } + + d_printf(_("Share name Type # Opens\n-------------------------" + "-----------------------------------------------------\n")); + ret = cli_NetConnectionEnum(cli, sessname, display_conns_func); + cli_shutdown(cli); + return ret; +} + +static int rap_session_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) + return net_rap_session_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetSessionDel(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +int net_rap_session(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "info", + rap_session_info, + NET_TRANSPORT_RAP, + N_("Display information about session"), + N_("net rap session info\n" + " Display information about session") + }, + { + "delete", + rap_session_delete, + NET_TRANSPORT_RAP, + N_("Close specified session"), + N_("net rap session delete\n" + " Close specified session\n" + " Alias for net rap session close") + }, + { + "close", + rap_session_delete, + NET_TRANSPORT_RAP, + N_("Close specified session"), + N_("net rap session close\n" + " Close specified session") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap session\n" + " List all open sessions on remote " + "server\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + d_printf(_("Computer User name " + "Client Type Opens Idle time\n" + "------------------------------------------" + "------------------------------------\n")); + ret = cli_NetSessionEnum(cli, list_sessions_func); + + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap session", func); +} + +/**************************************************************************** +list a server name +****************************************************************************/ +static void display_server_func(const char *name, uint32_t m, + const char *comment, void * reserved) +{ + d_printf("\t%-16.16s %s\n", name, comment); +} + +static int net_rap_server_name(struct net_context *c, int argc, const char *argv[]) +{ + struct cli_state *cli; + char *name; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rap server name\n" + " Get the name of the server\n")); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + if (!cli_get_server_name(NULL, cli, &name)) { + d_fprintf(stderr, _("cli_get_server_name failed\n")); + cli_shutdown(cli); + return -1; + } + + d_printf(_("Server name = %s\n"), name); + + TALLOC_FREE(name); + cli_shutdown(cli); + return 0; +} + +static int net_rap_server_domain(struct net_context *c, int argc, + const char **argv) +{ + struct cli_state *cli; + int ret; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rap server domain\n" + " Enumerate servers in this domain/workgroup\n")); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + d_printf(_("\nEnumerating servers in this domain or workgroup: \n\n" + "\tServer name Server description\n" + "\t------------- ----------------------------\n")); + + ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, + display_server_func,NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_server(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "name", + net_rap_server_name, + NET_TRANSPORT_RAP, + N_("Get the name of the server"), + N_("net rap server name\n" + " Get the name of the server") + }, + { + "domain", + net_rap_server_domain, + NET_TRANSPORT_RAP, + N_("Get the servers in this domain/workgroup"), + N_("net rap server domain\n" + " Get the servers in this domain/workgroup") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + /* smb4k uses 'net [rap|rpc] server domain' to query servers in a domain */ + /* Fall through for 'domain', any other forms will cause to show usage message */ + return net_run_function(c, argc, argv, "net rap server", func); + +} + +int net_rap_domain_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rap domain [misc. options] [target]\n\tlists the" + " domains or workgroups visible on the current network\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_rap_domain(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (c->display_usage) + return net_rap_domain_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + d_printf(_("\nEnumerating domains:\n\n" + "\tDomain name Server name of Browse Master\n" + "\t------------- ----------------------------\n")); + + ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM, + display_server_func,NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_printq_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net rap printq [misc. options] [targets]\n" + "\tor\n" + "net rap printq info [<queue_name>] [misc. options] [targets]\n" + "\tlists the specified queue and jobs on the target server.\n" + "\tIf the queue name is not specified, all queues are listed.\n\n")); + d_printf(_( + "net rap printq delete [<queue name>] [misc. options] [targets]\n" + "\tdeletes the specified job number on the target server, or the\n" + "\tprinter queue if no job number is specified\n")); + + net_common_flags_usage(c, argc, argv); + + return -1; +} + +static void enum_queue(const char *queuename, uint16_t pri, uint16_t start, + uint16_t until, const char *sep, const char *pproc, + const char *dest, const char *qparms, + const char *qcomment, uint16_t status, uint16_t jobcount) +{ + d_printf(_("%-17.17s Queue %5d jobs "), + queuename, jobcount); + + switch (status) { + case 0: + d_printf(_("*Printer Active*\n")); + break; + case 1: + d_printf(_("*Printer Paused*\n")); + break; + case 2: + d_printf(_("*Printer error*\n")); + break; + case 3: + d_printf(_("*Delete Pending*\n")); + break; + default: + d_printf(_("**UNKNOWN STATUS**\n")); + } +} + +static void enum_jobs(uint16_t jobid, const char *ownername, + const char *notifyname, const char *datatype, + const char *jparms, uint16_t pos, uint16_t status, + const char *jstatus, unsigned int submitted, unsigned int jobsize, + const char *comment) +{ + d_printf(" %-23.23s %5d %9d ", + ownername, jobid, jobsize); + switch (status) { + case 0: + d_printf(_("Waiting\n")); + break; + case 1: + d_printf(_("Held in queue\n")); + break; + case 2: + d_printf(_("Spooling\n")); + break; + case 3: + d_printf(_("Printing\n")); + break; + default: + d_printf(_("**UNKNOWN STATUS**\n")); + } +} + +#define PRINTQ_ENUM_DISPLAY \ + _("Print queues at \\\\%s\n\n"\ + "Name Job # Size Status\n\n"\ + "------------------------------------------------------------------"\ + "-------------\n") + +static int rap_printq_info(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) + return net_rap_printq_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + d_printf(PRINTQ_ENUM_DISPLAY, smbXcli_conn_remote_name(cli->conn)); /* list header */ + ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs); + cli_shutdown(cli); + return ret; +} + +static int rap_printq_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + NTSTATUS status; + + if (argc == 0 || c->display_usage) + return net_rap_printq_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + status = cli_printjob_del(cli, atoi(argv[0])); + cli_shutdown(cli); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + return 0; +} + +int net_rap_printq(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + struct functable func[] = { + { + "info", + rap_printq_info, + NET_TRANSPORT_RAP, + N_("Display info about print queues and jobs"), + N_("net rap printq info [queue]\n" + " Display info about print jobs in queue.\n" + " If queue is not specified, all queues are " + "listed") + }, + { + "delete", + rap_printq_delete, + NET_TRANSPORT_RAP, + N_("Delete print job(s)"), + N_("net rap printq delete\n" + " Delete print job(s)") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap printq\n" + " List the print queue\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + d_printf(PRINTQ_ENUM_DISPLAY, smbXcli_conn_remote_name(cli->conn)); /* list header */ + ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs); + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap printq", func); +} + +static int net_rap_user_usage(struct net_context *c, int argc, const char **argv) +{ + return net_user_usage(c, argc, argv); +} + +static void user_fn(const char *user_name, void *state) +{ + d_printf("%-21.21s\n", user_name); +} + +static void long_user_fn(const char *user_name, const char *comment, + const char * home_dir, const char * logon_script, + void *state) +{ + d_printf("%-21.21s %s\n", + user_name, comment); +} + +static void group_member_fn(const char *user_name, void *state) +{ + d_printf("%-21.21s\n", user_name); +} + +static int rap_user_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0 || c->display_usage) { + return net_rap_user_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetUserDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_user_add(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + struct rap_user_info_1 userinfo; + + if (argc == 0 || c->display_usage) { + return net_rap_user_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + strlcpy((char *)userinfo.user_name, argv[0], sizeof(userinfo.user_name)); + if (c->opt_flags == 0) + c->opt_flags = 0x21; + + userinfo.userflags = c->opt_flags; + userinfo.reserved1 = '\0'; + userinfo.comment = smb_xstrdup(c->opt_comment ? c->opt_comment : ""); + userinfo.priv = 1; + userinfo.home_dir = NULL; + userinfo.logon_script = NULL; + userinfo.passwrd[0] = '\0'; + + ret = cli_NetUserAdd(cli, &userinfo); + + cli_shutdown(cli); + return ret; +} + +static int rap_user_info(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0 || c->display_usage) { + return net_rap_user_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_user(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + struct functable func[] = { + { + "add", + rap_user_add, + NET_TRANSPORT_RAP, + N_("Add specified user"), + N_("net rap user add\n" + " Add specified user") + }, + { + "info", + rap_user_info, + NET_TRANSPORT_RAP, + N_("List domain groups of specified user"), + N_("net rap user info\n" + " List domain groups of specified user") + + }, + { + "delete", + rap_user_delete, + NET_TRANSPORT_RAP, + N_("Remove specified user"), + N_("net rap user delete\n" + " Remove specified user") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap user\n" + " List all users\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + goto done; + if (c->opt_long_list_entries) { + d_printf(_("\nUser name Comment" + "\n-----------------------------\n")); + ret = cli_RNetUserEnum(cli, long_user_fn, NULL); + cli_shutdown(cli); + goto done; + } + ret = cli_RNetUserEnum0(cli, user_fn, NULL); + cli_shutdown(cli); + goto done; + } + + ret = net_run_function(c, argc, argv, "net rap user", func); + done: + if (ret != 0) { + DEBUG(1, (_("Net user returned: %d\n"), ret)); + } + return ret; +} + + +int net_rap_group_usage(struct net_context *c, int argc, const char **argv) +{ + return net_group_usage(c, argc, argv); +} + +static void long_group_fn(const char *group_name, const char *comment, + void *state) +{ + d_printf("%-21.21s %s\n", group_name, comment); +} + +static void group_fn(const char *group_name, void *state) +{ + d_printf("%-21.21s\n", group_name); +} + +static int rap_group_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0 || c->display_usage) { + return net_rap_group_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetGroupDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_group_add(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + struct rap_group_info_1 grinfo; + + if (argc == 0 || c->display_usage) { + return net_rap_group_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + /* BB check for length 21 or smaller explicitly ? BB */ + strlcpy((char *)grinfo.group_name, argv[0], sizeof(grinfo.group_name)); + grinfo.reserved1 = '\0'; + grinfo.comment = smb_xstrdup(c->opt_comment ? c->opt_comment : ""); + + ret = cli_NetGroupAdd(cli, &grinfo); + cli_shutdown(cli); + return ret; +} + +int net_rap_group(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + rap_group_add, + NET_TRANSPORT_RAP, + N_("Add specified group"), + N_("net rap group add\n" + " Add specified group") + }, + { + "delete", + rap_group_delete, + NET_TRANSPORT_RAP, + N_("Delete specified group"), + N_("net rap group delete\n" + " Delete specified group") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap group\n" + " List all groups\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + if (c->opt_long_list_entries) { + d_printf(_("Group name Comment\n" + "-----------------------------\n")); + ret = cli_RNetGroupEnum(cli, long_group_fn, NULL); + cli_shutdown(cli); + return ret; + } + ret = cli_RNetGroupEnum0(cli, group_fn, NULL); + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap group", func); +} + +int net_rap_groupmember_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net rap groupmember LIST <group> [misc. options] [targets]" + "\n\t Enumerate users in a group\n" + "\nnet rap groupmember DELETE <group> <user> [misc. options] " + "[targets]\n\t Delete specified user from specified group\n" + "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]" + "\n\t Add specified user to specified group\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + + +static int rap_groupmember_add(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc != 2 || c->display_usage) { + return net_rap_groupmember_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetGroupAddUser(cli, argv[0], argv[1]); + cli_shutdown(cli); + return ret; +} + +static int rap_groupmember_delete(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc != 2 || c->display_usage) { + return net_rap_groupmember_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetGroupDelUser(cli, argv[0], argv[1]); + cli_shutdown(cli); + return ret; +} + +static int rap_groupmember_list(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0 || c->display_usage) { + return net_rap_groupmember_usage(c, argc, argv); + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL ); + cli_shutdown(cli); + return ret; +} + +int net_rap_groupmember(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + rap_groupmember_add, + NET_TRANSPORT_RAP, + N_("Add specified user to group"), + N_("net rap groupmember add\n" + " Add specified user to group") + }, + { + "list", + rap_groupmember_list, + NET_TRANSPORT_RAP, + N_("List users in group"), + N_("net rap groupmember list\n" + " List users in group") + }, + { + "delete", + rap_groupmember_delete, + NET_TRANSPORT_RAP, + N_("Remove user from group"), + N_("net rap groupmember delete\n" + " Remove user from group") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rap groupmember", func); +} + +int net_rap_validate_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rap validate <username> [password]\n" + "\tValidate user and password to check whether they" + " can access target server or domain\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +int net_rap_validate(struct net_context *c, int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +int net_rap_service_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rap service [misc. options] [targets] \n" + "\tlists all running service daemons on target server\n")); + d_printf(_("\nnet rap service START <name> [service startup arguments]" + " [misc. options] [targets]" + "\n\tStart named service on remote server\n")); + d_printf(_("\nnet rap service STOP <name> [misc. options] [targets]\n" + "\n\tStop named service on remote server\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +static int rap_service_start(struct net_context *c, int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +static int rap_service_stop(struct net_context *c, int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +static void service_fn(const char *service_name, const char *dummy, + void *state) +{ + d_printf("%-21.21s\n", service_name); +} + +int net_rap_service(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "start", + rap_service_start, + NET_TRANSPORT_RAP, + N_("Start service on remote server"), + N_("net rap service start\n" + " Start service on remote server") + }, + { + "stop", + rap_service_stop, + NET_TRANSPORT_RAP, + N_("Stop named serve on remote server"), + N_("net rap service stop\n" + " Stop named serve on remote server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rap service\n" + " List services on remote server\n")); + net_display_usage_from_functable(func); + return 0; + } + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + if (c->opt_long_list_entries) { + d_printf(_("Service name Comment\n" + "-----------------------------\n")); + ret = cli_RNetServiceEnum(cli, long_group_fn, NULL); + if (ret) { + cli_shutdown(cli); + return ret; + } + } + ret = cli_RNetServiceEnum(cli, service_fn, NULL); + cli_shutdown(cli); + return ret; + } + + return net_run_function(c, argc, argv, "net rap service", func); +} + +int net_rap_password_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n" + "\tchanges the password for the specified user at target\n")); + + return -1; +} + + +int net_rap_password(struct net_context *c, int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc < 3 || c->display_usage) + return net_rap_password_usage(c, argc, argv); + + if (!NT_STATUS_IS_OK(net_make_ipc_connection(c, 0, &cli))) + return -1; + + /* BB Add check for password lengths? */ + ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]); + cli_shutdown(cli); + return ret; +} + +int net_rap_admin_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]" + "\n\texecutes a remote command on an os/2 target server\n")); + + return -1; +} + + +int net_rap_admin(struct net_context *c, int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +/* Entry-point for all the RAP functions. */ + +int net_rap(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "file", + net_rap_file, + NET_TRANSPORT_RAP, + N_("List open files"), + N_("net rap file\n" + " List open files") + }, + { + "share", + net_rap_share, + NET_TRANSPORT_RAP, + N_("List shares exported by server"), + N_("net rap share\n" + " List shares exported by server") + }, + { + "session", + net_rap_session, + NET_TRANSPORT_RAP, + N_("List open sessions"), + N_("net rap session\n" + " List open sessions") + }, + { + "server", + net_rap_server, + NET_TRANSPORT_RAP, + N_("List servers in workgroup"), + N_("net rap server\n" + " List servers in domain/workgroup") + }, + { + "domain", + net_rap_domain, + NET_TRANSPORT_RAP, + N_("List domains in network"), + N_("net rap domain\n" + " List domains in network") + }, + { + "printq", + net_rap_printq, + NET_TRANSPORT_RAP, + N_("List printer queues on server"), + N_("net rap printq\n" + " List printer queues on server") + }, + { + "user", + net_rap_user, + NET_TRANSPORT_RAP, + N_("List users"), + N_("net rap user\n" + " List users") + }, + { + "group", + net_rap_group, + NET_TRANSPORT_RAP, + N_("List user groups"), + N_("net rap group\n" + " List user groups") + }, + { + "validate", + net_rap_validate, + NET_TRANSPORT_RAP, + N_("Check username/password"), + N_("net rap validate\n" + " Check username/password") + }, + { + "groupmember", + net_rap_groupmember, + NET_TRANSPORT_RAP, + N_("List/modify group memberships"), + N_("net rap groupmember\n" + " List/modify group memberships") + }, + { + "admin", + net_rap_admin, + NET_TRANSPORT_RAP, + N_("Execute commands on remote OS/2"), + N_("net rap admin\n" + " Execute commands on remote OS/2") + }, + { + "service", + net_rap_service, + NET_TRANSPORT_RAP, + N_("Start/stop remote service"), + N_("net rap service\n" + " Start/stop remote service") + }, + { + "password", + net_rap_password, + NET_TRANSPORT_RAP, + N_("Change user password"), + N_("net rap password\n" + " Change user password") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rap", func); +} + diff --git a/source3/utils/net_registry.c b/source3/utils/net_registry.c new file mode 100644 index 0000000..5d1314e --- /dev/null +++ b/source3/utils/net_registry.c @@ -0,0 +1,1732 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Local registry interface + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "registry.h" +#include "registry/reg_api.h" +#include "registry/reg_util_token.h" +#include "registry/reg_init_basic.h" +#include "utils/net.h" +#include "utils/net_registry_util.h" +#include "include/g_lock.h" +#include "registry/reg_backend_db.h" +#include "registry/reg_import.h" +#include "registry/reg_format.h" +#include "registry/reg_api_util.h" +#include <assert.h> +#include "../libcli/security/display_sec.h" +#include "../libcli/security/sddl.h" +#include "../libcli/registry/util_reg.h" +#include "passdb/machine_sid.h" +#include "net_registry_check.h" +#include "lib/util/util_tdb.h" +#include "lib/util/smb_strtox.h" + +/* + * + * Helper functions + * + */ + +/** + * split given path into hive and remaining path and open the hive key + */ +static WERROR open_hive(TALLOC_CTX *ctx, const char *path, + uint32_t desired_access, + struct registry_key **hive, + char **subkeyname) +{ + WERROR werr; + struct security_token *token = NULL; + char *hivename = NULL; + char *tmp_subkeyname = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if ((hive == NULL) || (subkeyname == NULL)) { + werr = WERR_INVALID_PARAMETER; + goto done; + } + + werr = split_hive_key(tmp_ctx, path, &hivename, &tmp_subkeyname); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + *subkeyname = talloc_strdup(ctx, tmp_subkeyname); + if (*subkeyname == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + werr = ntstatus_to_werror(registry_create_admin_token(tmp_ctx, &token)); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = reg_openhive(ctx, hivename, desired_access, token, hive); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + werr = WERR_OK; + +done: + TALLOC_FREE(tmp_ctx); + return werr; +} + +static WERROR open_key(TALLOC_CTX *ctx, const char *path, + uint32_t desired_access, + struct registry_key **key) +{ + WERROR werr; + char *subkey_name = NULL; + struct registry_key *hive = NULL; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + if ((path == NULL) || (key == NULL)) { + return WERR_INVALID_PARAMETER; + } + + werr = open_hive(tmp_ctx, path, desired_access, &hive, &subkey_name); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_hive failed: %s\n"), + win_errstr(werr)); + goto done; + } + + werr = reg_openkey(ctx, hive, subkey_name, desired_access, key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_openkey failed: %s\n"), + win_errstr(werr)); + goto done; + } + + werr = WERR_OK; + +done: + TALLOC_FREE(tmp_ctx); + return werr; +} + +static WERROR registry_enumkey(struct registry_key *parent, const char *keyname, + bool recursive) +{ + WERROR werr; + TALLOC_CTX *ctx = talloc_stackframe(); + char *subkey_name; + NTTIME modtime; + uint32_t count; + char *valname = NULL; + struct registry_value *valvalue = NULL; + struct registry_key *key = NULL; + + werr = reg_openkey(ctx, parent, keyname, REG_KEY_READ, &key); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + if (recursive) { + printf("[%s]\n\n", key->key->name); + } else { + for (count = 0; + werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime), + W_ERROR_IS_OK(werr); + count++) + { + print_registry_key(subkey_name, &modtime); + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + goto done; + } + } + + for (count = 0; + werr = reg_enumvalue(ctx, key, count, &valname, &valvalue), + W_ERROR_IS_OK(werr); + count++) + { + print_registry_value_with_name(valname, valvalue); + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + goto done; + } + + if (!recursive) { + werr = WERR_OK; + goto done; + } + + for (count = 0; + werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime), + W_ERROR_IS_OK(werr); + count++) + { + werr = registry_enumkey(key, subkey_name, recursive); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + goto done; + } + + werr = WERR_OK; + +done: + TALLOC_FREE(ctx); + return werr; +} + + + +/* + * + * the main "net registry" function implementations + * + */ +static int net_registry_enumerate(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + struct registry_key *key = NULL; + char *name = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = -1; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry enumerate <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry enumerate 'HKLM\\Software\\Samba'\n")); + goto done; + } + + werr = open_hive(ctx, argv[0], REG_KEY_READ, &key, &name); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + werr = registry_enumkey(key, name, c->opt_reboot); + if (W_ERROR_IS_OK(werr)) { + ret = 0; + } +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_enumerate_recursive(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + struct registry_key *key = NULL; + char *name = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = -1; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry enumerate <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry enumerate 'HKLM\\Software\\Samba'\n")); + goto done; + } + + werr = open_hive(ctx, argv[0], REG_KEY_READ, &key, &name); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + werr = registry_enumkey(key, name, true); + if (W_ERROR_IS_OK(werr)) { + ret = 0; + } +done: + TALLOC_FREE(ctx); + return ret; +} + + +static int net_registry_createkey(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + enum winreg_CreateAction action; + char *subkeyname = NULL; + struct registry_key *hivekey = NULL; + struct registry_key *subkey = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = -1; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry createkey <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry createkey " + "'HKLM\\Software\\Samba\\smbconf.127.0.0.1'\n")); + goto done; + } + if (strlen(argv[0]) == 0) { + d_fprintf(stderr, _("error: zero length key name given\n")); + goto done; + } + + werr = open_hive(ctx, argv[0], REG_KEY_WRITE, &hivekey, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_hive failed: %s\n"), + win_errstr(werr)); + goto done; + } + + werr = reg_createkey(ctx, hivekey, subkeyname, REG_KEY_WRITE, + &subkey, &action); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_createkey failed: %s\n"), + win_errstr(werr)); + goto done; + } + switch (action) { + case REG_ACTION_NONE: + d_printf(_("createkey did nothing -- huh?\n")); + break; + case REG_CREATED_NEW_KEY: + d_printf(_("createkey created %s\n"), argv[0]); + break; + case REG_OPENED_EXISTING_KEY: + d_printf(_("createkey opened existing %s\n"), argv[0]); + break; + } + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_deletekey_internal(struct net_context *c, int argc, + const char **argv, + bool recursive) +{ + WERROR werr; + char *subkeyname = NULL; + struct registry_key *hivekey = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = -1; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry deletekey <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry deletekey " + "'HKLM\\Software\\Samba\\smbconf.127.0.0.1'\n")); + goto done; + } + if (strlen(argv[0]) == 0) { + d_fprintf(stderr, _("error: zero length key name given\n")); + goto done; + } + + werr = open_hive(ctx, argv[0], REG_KEY_WRITE, &hivekey, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "open_hive %s: %s\n", _("failed"), + win_errstr(werr)); + goto done; + } + + if (recursive) { + werr = reg_deletekey_recursive(hivekey, subkeyname); + } else { + werr = reg_deletekey(hivekey, subkeyname); + } + if (!W_ERROR_IS_OK(werr) && + !(c->opt_force && W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND))) + { + d_fprintf(stderr, "reg_deletekey %s: %s\n", _("failed"), + win_errstr(werr)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_deletekey(struct net_context *c, int argc, + const char **argv) +{ + return net_registry_deletekey_internal(c, argc, argv, false); +} + +static int net_registry_deletekey_recursive(struct net_context *c, int argc, + const char **argv) +{ + return net_registry_deletekey_internal(c, argc, argv, true); +} + +static int net_registry_getvalue_internal(struct net_context *c, int argc, + const char **argv, bool raw) +{ + WERROR werr; + int ret = -1; + struct registry_key *key = NULL; + struct registry_value *value = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net registry getvalue <key> <valuename>\n")); + goto done; + } + + werr = open_key(ctx, argv[0], REG_KEY_READ, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + werr = reg_queryvalue(ctx, key, argv[1], &value); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_queryvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + print_registry_value(value, raw); + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_getvalue(struct net_context *c, int argc, + const char **argv) +{ + return net_registry_getvalue_internal(c, argc, argv, false); +} + +static int net_registry_getvalueraw(struct net_context *c, int argc, + const char **argv) +{ + return net_registry_getvalue_internal(c, argc, argv, true); +} + +static int net_registry_getvaluesraw(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + int ret = -1; + struct registry_key *key = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + uint32_t idx; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "usage: net rpc registry getvaluesraw " + "<key>\n"); + goto done; + } + + werr = open_key(ctx, argv[0], REG_KEY_READ, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "open_key failed: %s\n", win_errstr(werr)); + goto done; + } + + idx = 0; + while (true) { + struct registry_value *val; + + werr = reg_enumvalue(talloc_tos(), key, idx, NULL, &val); + + if (W_ERROR_EQUAL(werr, WERR_NO_MORE_ITEMS)) { + ret = 0; + break; + } + if (!W_ERROR_IS_OK(werr)) { + break; + } + print_registry_value(val, true); + TALLOC_FREE(val); + idx += 1; + } +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_setvalue(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + struct registry_value value; + struct registry_key *key = NULL; + int ret = -1; + TALLOC_CTX *ctx = talloc_stackframe(); + + if (argc < 4 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net registry setvalue <key> <valuename> " + "<type> [<val>]+\n")); + goto done; + } + + if (!strequal(argv[2], "multi_sz") && (argc != 4)) { + d_fprintf(stderr, _("Too many args for type %s\n"), argv[2]); + goto done; + } + + if (strequal(argv[2], "dword")) { + int error = 0; + uint32_t v; + + v = smb_strtoul(argv[3], NULL, 10, &error, SMB_STR_STANDARD); + if (error != 0) { + goto done; + } + + value.type = REG_DWORD; + value.data = data_blob_talloc(ctx, NULL, 4); + SIVAL(value.data.data, 0, v); + } else if (strequal(argv[2], "sz")) { + value.type = REG_SZ; + if (!push_reg_sz(ctx, &value.data, argv[3])) { + goto done; + } + } else if (strequal(argv[2], "multi_sz")) { + const char **array; + int count = argc - 3; + int i; + value.type = REG_MULTI_SZ; + array = talloc_zero_array(ctx, const char *, count + 1); + if (array == NULL) { + goto done; + } + for (i=0; i < count; i++) { + array[i] = talloc_strdup(array, argv[count+i]); + if (array[i] == NULL) { + goto done; + } + } + if (!push_reg_multi_sz(ctx, &value.data, array)) { + goto done; + } + } else { + d_fprintf(stderr, _("type \"%s\" not implemented\n"), argv[2]); + goto done; + } + + werr = open_key(ctx, argv[0], REG_KEY_WRITE, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + werr = reg_setvalue(key, argv[1], &value); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_setvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_increment(struct net_context *c, int argc, + const char **argv) +{ + TDB_DATA lock_key = string_term_tdb_data("registry_increment_lock"); + struct g_lock_ctx *ctx = NULL; + const char *keyname = NULL; + struct registry_key *key = NULL; + const char *valuename = NULL; + struct registry_value *value = NULL; + uint32_t v; + uint32_t increment; + uint32_t newvalue; + NTSTATUS status; + WERROR werr; + int ret = -1; + + if (argc < 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net registry increment <key> <valuename> " + "[<increment>]\n")); + goto done; + } + + keyname = argv[0]; + valuename = argv[1]; + + increment = 1; + if (argc == 3) { + int error = 0; + + increment = smb_strtoul( + argv[2], NULL, 10, &error, SMB_STR_STANDARD); + if (error != 0) { + goto done; + } + } + + ctx = g_lock_ctx_init(c, c->msg_ctx); + if (ctx == NULL) { + d_fprintf(stderr, _("g_lock_ctx_init failed\n")); + goto done; + } + + status = g_lock_lock(ctx, + lock_key, + G_LOCK_WRITE, + timeval_set(600, 0), + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("g_lock_lock failed: %s\n"), + nt_errstr(status)); + goto done; + } + + werr = open_key(c, keyname, REG_KEY_READ|REG_KEY_WRITE, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), + win_errstr(werr)); + goto done; + } + + werr = reg_queryvalue(key, key, valuename, &value); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_queryvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + if (value->type != REG_DWORD) { + d_fprintf(stderr, _("value not a DWORD: %s\n"), + str_regtype(value->type)); + goto done; + } + + if (value->data.length < 4) { + d_fprintf(stderr, _("value too short for regular DWORD\n")); + goto done; + } + + v = IVAL(value->data.data, 0); + v += increment; + newvalue = v; + + SIVAL(value->data.data, 0, v); + + werr = reg_setvalue(key, valuename, value); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_setvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("increment failed: %s\n"), + win_errstr(werr)); + goto done; + } + + g_lock_unlock(ctx, lock_key); + + d_printf(_("%"PRIu32"\n"), newvalue); + + ret = 0; + +done: + TALLOC_FREE(value); + TALLOC_FREE(key); + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_deletevalue(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + struct registry_key *key = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + int ret = -1; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net registry deletevalue <key> <valuename>\n")); + goto done; + } + + werr = open_key(ctx, argv[0], REG_KEY_WRITE, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + werr = reg_deletevalue(key, argv[1]); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_deletevalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static WERROR net_registry_getsd_internal(struct net_context *c, + TALLOC_CTX *mem_ctx, + const char *keyname, + struct security_descriptor **sd) +{ + WERROR werr; + struct registry_key *key = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + uint32_t access_mask = REG_KEY_READ | + SEC_FLAG_MAXIMUM_ALLOWED | + SEC_FLAG_SYSTEM_SECURITY; + + /* + * net_rpc_regsitry uses SEC_FLAG_SYSTEM_SECURITY, but access + * is denied with these perms right now... + */ + access_mask = REG_KEY_READ; + + if (sd == NULL) { + d_fprintf(stderr, _("internal error: invalid argument\n")); + werr = WERR_INVALID_PARAMETER; + goto done; + } + + if (strlen(keyname) == 0) { + d_fprintf(stderr, _("error: zero length key name given\n")); + werr = WERR_INVALID_PARAMETER; + goto done; + } + + werr = open_key(ctx, keyname, access_mask, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "%s%s\n", _("open_key failed: "), + win_errstr(werr)); + goto done; + } + + werr = reg_getkeysecurity(mem_ctx, key, sd); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "%s%s\n", _("reg_getkeysecurity failed: "), + win_errstr(werr)); + goto done; + } + + werr = WERR_OK; + +done: + TALLOC_FREE(ctx); + return werr; +} + +static int net_registry_getsd(struct net_context *c, int argc, + const char **argv) +{ + WERROR werr; + int ret = -1; + struct security_descriptor *secdesc = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry getsd <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry getsd 'HKLM\\Software\\Samba'\n")); + goto done; + } + + werr = net_registry_getsd_internal(c, ctx, argv[0], &secdesc); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + display_sec_desc(secdesc); + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static int net_registry_getsd_sddl(struct net_context *c, + int argc, const char **argv) +{ + WERROR werr; + int ret = -1; + struct security_descriptor *secdesc = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry getsd_sddl <path>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry getsd_sddl 'HKLM\\Software\\Samba'\n")); + goto done; + } + + werr = net_registry_getsd_internal(c, ctx, argv[0], &secdesc); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + d_printf("%s\n", sddl_encode(ctx, secdesc, get_global_sam_sid())); + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +static WERROR net_registry_setsd_internal(struct net_context *c, + TALLOC_CTX *mem_ctx, + const char *keyname, + struct security_descriptor *sd) +{ + WERROR werr; + struct registry_key *key = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + uint32_t access_mask = REG_KEY_WRITE | + SEC_FLAG_MAXIMUM_ALLOWED | + SEC_FLAG_SYSTEM_SECURITY; + + /* + * net_rpc_regsitry uses SEC_FLAG_SYSTEM_SECURITY, but access + * is denied with these perms right now... + */ + access_mask = REG_KEY_WRITE; + + if (strlen(keyname) == 0) { + d_fprintf(stderr, _("error: zero length key name given\n")); + werr = WERR_INVALID_PARAMETER; + goto done; + } + + werr = open_key(ctx, keyname, access_mask, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "%s%s\n", _("open_key failed: "), + win_errstr(werr)); + goto done; + } + + werr = reg_setkeysecurity(key, sd); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "%s%s\n", _("reg_setkeysecurity failed: "), + win_errstr(werr)); + goto done; + } + + werr = WERR_OK; + +done: + TALLOC_FREE(ctx); + return werr; +} + +static int net_registry_setsd_sddl(struct net_context *c, + int argc, const char **argv) +{ + WERROR werr; + int ret = -1; + struct security_descriptor *secdesc = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry setsd_sddl <path> <security_descriptor>\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry setsd_sddl 'HKLM\\Software\\Samba'\n")); + goto done; + } + + secdesc = sddl_decode(ctx, argv[1], get_global_sam_sid()); + if (secdesc == NULL) { + goto done; + } + + werr = net_registry_setsd_internal(c, ctx, argv[0], secdesc); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + ret = 0; + +done: + TALLOC_FREE(ctx); + return ret; +} + +/******************************************************************************/ +/** + * @defgroup net_registry net registry + */ + +/** + * @defgroup net_registry_import Import + * @ingroup net_registry + * @{ + */ + +struct import_ctx { + TALLOC_CTX *mem_ctx; +}; + + +static WERROR import_create_key(struct import_ctx *ctx, + struct registry_key *parent, + const char *name, void **pkey, bool *existing) +{ + WERROR werr; + TALLOC_CTX *mem_ctx = talloc_new(ctx->mem_ctx); + + struct registry_key *key = NULL; + enum winreg_CreateAction action; + + if (parent == NULL) { + char *subkeyname = NULL; + werr = open_hive(mem_ctx, name, REG_KEY_WRITE, + &parent, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_hive failed: %s\n"), + win_errstr(werr)); + goto done; + } + name = subkeyname; + } + + action = REG_ACTION_NONE; + werr = reg_createkey(mem_ctx, parent, name, REG_KEY_WRITE, + &key, &action); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_createkey failed: %s\n"), + win_errstr(werr)); + goto done; + } + + if (action == REG_ACTION_NONE) { + d_fprintf(stderr, _("createkey did nothing -- huh?\n")); + werr = WERR_CREATE_FAILED; + goto done; + } + + if (existing != NULL) { + *existing = (action == REG_OPENED_EXISTING_KEY); + } + + if (pkey!=NULL) { + *pkey = talloc_steal(ctx->mem_ctx, key); + } + +done: + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_close_key(struct import_ctx *ctx, + struct registry_key *key) +{ + return WERR_OK; +} + +static WERROR import_delete_key(struct import_ctx *ctx, + struct registry_key *parent, const char *name) +{ + WERROR werr; + TALLOC_CTX *mem_ctx = talloc_new(talloc_tos()); + + if (parent == NULL) { + char *subkeyname = NULL; + werr = open_hive(mem_ctx, name, REG_KEY_WRITE, + &parent, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_hive failed: %s\n"), + win_errstr(werr)); + goto done; + } + name = subkeyname; + } + + werr = reg_deletekey_recursive(parent, name); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, "reg_deletekey_recursive %s: %s\n", + _("failed"), win_errstr(werr)); + goto done; + } + +done: + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_create_val (struct import_ctx *ctx, + struct registry_key *parent, const char *name, + const struct registry_value *value) +{ + WERROR werr; + + if (parent == NULL) { + return WERR_INVALID_PARAMETER; + } + + werr = reg_setvalue(parent, name, value); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_setvalue failed: %s\n"), + win_errstr(werr)); + } + return werr; +} + +static WERROR import_delete_val (struct import_ctx *ctx, + struct registry_key *parent, const char *name) +{ + WERROR werr; + + if (parent == NULL) { + return WERR_INVALID_PARAMETER; + } + + werr = reg_deletevalue(parent, name); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_deletevalue failed: %s\n"), + win_errstr(werr)); + } + + return werr; +} + +struct precheck_ctx { + TALLOC_CTX *mem_ctx; + bool failed; +}; + +static WERROR precheck_create_key(struct precheck_ctx *ctx, + struct registry_key *parent, + const char *name, void **pkey, bool *existing) +{ + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + struct registry_key *key = NULL; + + if (parent == NULL) { + char *subkeyname = NULL; + werr = open_hive(frame, name, REG_KEY_READ, + &parent, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Precheck: open_hive of [%s] failed: %s\n", + name, win_errstr(werr)); + goto done; + } + name = subkeyname; + } + + werr = reg_openkey(frame, parent, name, 0, &key); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Precheck: openkey [%s] failed: %s\n", + name, win_errstr(werr)); + goto done; + } + + if (existing != NULL) { + *existing = true; + } + + if (pkey != NULL) { + *pkey = talloc_steal(ctx->mem_ctx, key); + } + +done: + talloc_free(frame); + ctx->failed = !W_ERROR_IS_OK(werr); + return werr; +} + +static WERROR precheck_close_key(struct precheck_ctx *ctx, + struct registry_key *key) +{ + talloc_free(key); + return WERR_OK; +} + +static WERROR precheck_delete_key(struct precheck_ctx *ctx, + struct registry_key *parent, const char *name) +{ + WERROR werr; + TALLOC_CTX *frame = talloc_stackframe(); + struct registry_key *key; + + if (parent == NULL) { + char *subkeyname = NULL; + werr = open_hive(frame, name, REG_KEY_READ, + &parent, &subkeyname); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Precheck: open_hive of [%s] failed: %s\n", + name, win_errstr(werr)); + goto done; + } + name = subkeyname; + } + + werr = reg_openkey(ctx->mem_ctx, parent, name, 0, &key); + if (W_ERROR_IS_OK(werr)) { + d_printf("Precheck: key [%s\\%s] should not exist\n", + parent->key->name, name); + werr = WERR_FILE_EXISTS; + } else if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + werr = WERR_OK; + } else { + d_printf("Precheck: openkey [%s\\%s] failed: %s\n", + parent->key->name, name, win_errstr(werr)); + } + +done: + talloc_free(frame); + ctx->failed = !W_ERROR_IS_OK(werr); + return werr; +} + +static int registry_value_cmp( + const struct registry_value* v1, const struct registry_value* v2) +{ + if (v1->type == v2->type) { + return data_blob_cmp(&v1->data, &v2->data); + } + return v1->type - v2->type; +} + +static WERROR precheck_create_val(struct precheck_ctx *ctx, + struct registry_key *parent, + const char *name, + const struct registry_value *value) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct registry_value *old; + WERROR werr; + + SMB_ASSERT(parent); + + werr = reg_queryvalue(frame, parent, name, &old); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Precheck: queryvalue \"%s\" of [%s] failed: %s\n", + name, parent->key->name, win_errstr(werr)); + goto done; + } + if (registry_value_cmp(value, old) != 0) { + d_printf("Precheck: unexpected value \"%s\" of key [%s]\n", + name, parent->key->name); + ctx->failed = true; + } +done: + talloc_free(frame); + return werr; +} + +static WERROR precheck_delete_val(struct precheck_ctx *ctx, + struct registry_key *parent, + const char *name) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct registry_value *old; + WERROR werr; + + SMB_ASSERT(parent); + + werr = reg_queryvalue(frame, parent, name, &old); + if (W_ERROR_IS_OK(werr)) { + d_printf("Precheck: value \"%s\" of key [%s] should not exist\n", + name, parent->key->name); + werr = WERR_FILE_EXISTS; + } else if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + werr = WERR_OK; + } else { + printf("Precheck: queryvalue \"%s\" of key [%s] failed: %s\n", + name, parent->key->name, win_errstr(werr)); + } + + talloc_free(frame); + ctx->failed = !W_ERROR_IS_OK(werr); + return werr; +} + +static bool import_precheck(const char *fname, const char *parse_options) +{ + TALLOC_CTX *mem_ctx = talloc_tos(); + struct precheck_ctx precheck_ctx = { + .mem_ctx = mem_ctx, + .failed = false, + }; + struct reg_import_callback precheck_callback = { + .openkey = NULL, + .closekey = (reg_import_callback_closekey_t)&precheck_close_key, + .createkey = (reg_import_callback_createkey_t)&precheck_create_key, + .deletekey = (reg_import_callback_deletekey_t)&precheck_delete_key, + .deleteval = (reg_import_callback_deleteval_t)&precheck_delete_val, + .setval = { + .registry_value = (reg_import_callback_setval_registry_value_t) + &precheck_create_val, + }, + .setval_type = REGISTRY_VALUE, + .data = &precheck_ctx + }; + struct reg_parse_callback *parse_callback; + int ret; + + if (!fname) { + return true; + } + + parse_callback = reg_import_adapter(mem_ctx, precheck_callback); + if (parse_callback == NULL) { + d_printf("talloc failed\n"); + return false; + } + + ret = reg_parse_file(fname, parse_callback, parse_options); + + if (ret < 0 || precheck_ctx.failed) { + d_printf("Precheck failed\n"); + return false; + } + return true; +} + +static int import_with_precheck_action(const char *import_fname, + const char *precheck_fname, + const char *parse_options) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct import_ctx import_ctx = { + .mem_ctx = frame, + }; + struct reg_import_callback import_callback = { + .openkey = NULL, + .closekey = (reg_import_callback_closekey_t)&import_close_key, + .createkey = (reg_import_callback_createkey_t)&import_create_key, + .deletekey = (reg_import_callback_deletekey_t)&import_delete_key, + .deleteval = (reg_import_callback_deleteval_t)&import_delete_val, + .setval = { + .registry_value = (reg_import_callback_setval_registry_value_t) + &import_create_val, + }, + .setval_type = REGISTRY_VALUE, + .data = &import_ctx + }; + struct reg_parse_callback *parse_callback; + int ret = -1; + bool precheck_ok; + + precheck_ok = import_precheck(precheck_fname, parse_options); + if (!precheck_ok) { + goto done; + } + + parse_callback = reg_import_adapter(frame, import_callback); + if (parse_callback == NULL) { + d_printf("talloc failed\n"); + goto done; + } + + ret = reg_parse_file(import_fname, parse_callback, parse_options); + +done: + talloc_free(frame); + return ret; +} + +static int net_registry_import(struct net_context *c, int argc, + const char **argv) +{ + const char *parse_options = (argc > 1) ? argv[1] : NULL; + int ret = -1; + WERROR werr; + + if (argc < 1 || argc > 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry import <reg> [options]\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry import file.reg enc=CP1252\n")); + return -1; + } + + werr = regdb_open(); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to open regdb: %s\n", win_errstr(werr)); + return -1; + } + + werr = regdb_transaction_start(); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to start transaction on regdb: %s\n", + win_errstr(werr)); + goto done; + } + + ret = import_with_precheck_action(argv[0], c->opt_precheck, + parse_options); + + if (ret < 0) { + d_printf("Transaction canceled!\n"); + regdb_transaction_cancel(); + goto done; + } + + SMB_ASSERT(ret == 0); + + if (c->opt_testmode) { + d_printf("Testmode: not committing changes.\n"); + regdb_transaction_cancel(); + goto done; + } + + werr = regdb_transaction_commit(); + if (!W_ERROR_IS_OK(werr)) { + d_printf("Failed to commit transaction on regdb: %s\n", + win_errstr(werr)); + ret = -1; + } + +done: + regdb_close(); + return ret; +} +/**@}*/ + +/******************************************************************************/ + +/** + * @defgroup net_registry_export Export + * @ingroup net_registry + * @{ + */ + +static int registry_export(TALLOC_CTX *ctx, /*const*/ struct registry_key *key, + struct reg_format *f) +{ + int ret=-1; + WERROR werr; + uint32_t count; + + struct registry_value *valvalue = NULL; + char *valname = NULL; + + char *subkey_name = NULL; + NTTIME modtime = 0; + + reg_format_registry_key(f, key, false); + + /* print values */ + for (count = 0; + werr = reg_enumvalue(ctx, key, count, &valname, &valvalue), + W_ERROR_IS_OK(werr); + count++) + { + reg_format_registry_value(f, valname, valvalue); + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + d_fprintf(stderr, _("reg_enumvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + /* recurse on subkeys */ + for (count = 0; + werr = reg_enumkey(ctx, key, count, &subkey_name, &modtime), + W_ERROR_IS_OK(werr); + count++) + { + struct registry_key *subkey = NULL; + + werr = reg_openkey(ctx, key, subkey_name, REG_KEY_READ, + &subkey); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("reg_openkey failed: %s\n"), + win_errstr(werr)); + goto done; + } + + registry_export(ctx, subkey, f); + TALLOC_FREE(subkey); + } + if (!W_ERROR_EQUAL(WERR_NO_MORE_ITEMS, werr)) { + d_fprintf(stderr, _("reg_enumkey failed: %s\n"), + win_errstr(werr)); + goto done; + } + ret = 0; +done: + return ret; +} + +static int net_registry_export(struct net_context *c, int argc, + const char **argv) +{ + int ret=-1; + WERROR werr; + struct registry_key *key = NULL; + TALLOC_CTX *ctx = talloc_stackframe(); + struct reg_format *f=NULL; + + if (argc < 2 || argc > 3 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry export <path> <file> [opt]\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry export 'HKLM\\Software\\Samba' " + "samba.reg regedit5\n")); + goto done; + } + + werr = open_key(ctx, argv[0], REG_KEY_READ, &key); + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("open_key failed: %s\n"), win_errstr(werr)); + goto done; + } + + f = reg_format_file(ctx, argv[1], (argc > 2) ? argv[2] : NULL); + if (f == NULL) { + d_fprintf(stderr, _("open file failed: %s\n"), strerror(errno)); + goto done; + } + + ret = registry_export(ctx, key, f); + +done: + TALLOC_FREE(ctx); + return ret; +} +/**@}*/ + +/******************************************************************************/ +/** + * @defgroup net_registry_convert Convert + * @ingroup net_registry + * @{ + */ + +static int net_registry_convert(struct net_context *c, int argc, + const char **argv) +{ + int ret; + TALLOC_CTX *mem_ctx; + const char *in_opt = NULL; + const char *out_opt = NULL; + + if (argc < 2 || argc > 4|| c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry convert <in> <out> [in_opt] [out_opt]\n" + "net registry convert <in> <out> [out_opt]\n")); + d_printf("%s\n%s", + _("Example:"), + _("net registry convert in.reg out.reg regedit4,enc=CP1252\n")); + return -1; + } + + mem_ctx = talloc_stackframe(); + + switch (argc ) { + case 2: + break; + case 3: + out_opt = argv[2]; + break; + case 4: + out_opt = argv[3]; + in_opt = argv[2]; + break; + default: + assert(false); + } + + + ret = reg_parse_file(argv[0], (struct reg_parse_callback*) + reg_format_file(mem_ctx, argv[1], out_opt), + in_opt); + + talloc_free(mem_ctx); + + return ret; +} +/**@}*/ + +static int net_registry_check(struct net_context *c, int argc, + const char **argv) +{ + char *dbfile; + struct check_options opts; + int ret; + + if (argc > 1|| c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net registry check [-vraTfl] [-o <ODB>] [--wipe] [<TDB>]\n" + " Check a registry database.\n" + " -v|--verbose\t be verbose\n" + " -r|--repair\t\t interactive repair mode\n" + " -a|--auto\t\t noninteractive repair mode\n" + " -T|--test\t\t dry run\n" + " -f|--force\t\t force\n" + " -l|--lock\t\t lock <TDB> while doing the check\n" + " -o|--output=<ODB>\t output database\n" + " --reg-version=n\t assume database format version {n|1,2,3}\n" + " --wipe\t\t create a new database from scratch\n" + " --db=<TDB>\t\t registry database to open\n")); + return c->display_usage ? 0 : -1; + } + + if (c->opt_db != NULL) { + dbfile = talloc_strdup(talloc_tos(), c->opt_db); + } else if (argc > 0) { + dbfile = talloc_strdup(talloc_tos(), argv[0]); + } else { + dbfile = state_path(talloc_tos(), "registry.tdb"); + } + if (dbfile == NULL) { + return -1; + } + + opts = (struct check_options) { + .lock = c->opt_lock || c->opt_long_list_entries, + .test = c->opt_testmode, + .automatic = c->opt_auto, + .verbose = c->opt_verbose, + .force = c->opt_force, + .repair = c->opt_repair || c->opt_reboot, + .version = c->opt_reg_version, + .output = c->opt_output, + .wipe = c->opt_wipe, + .implicit_db = (c->opt_db == NULL) && (argc == 0), + }; + + ret = net_registry_check_db(dbfile, &opts); + talloc_free(dbfile); + return ret; +} + + +/******************************************************************************/ + +int net_registry(struct net_context *c, int argc, const char **argv) +{ + int ret = -1; + + struct functable func[] = { + { + "enumerate", + net_registry_enumerate, + NET_TRANSPORT_LOCAL, + N_("Enumerate registry keys and values"), + N_("net registry enumerate\n" + " Enumerate registry keys and values") + }, + { + "enumerate_recursive", + net_registry_enumerate_recursive, + NET_TRANSPORT_LOCAL, + N_("Enumerate registry keys and values"), + N_("net registry enumerate_recursive\n" + " Enumerate registry keys and values") + }, + { + "createkey", + net_registry_createkey, + NET_TRANSPORT_LOCAL, + N_("Create a new registry key"), + N_("net registry createkey\n" + " Create a new registry key") + }, + { + "deletekey", + net_registry_deletekey, + NET_TRANSPORT_LOCAL, + N_("Delete a registry key"), + N_("net registry deletekey\n" + " Delete a registry key") + }, + { + "deletekey_recursive", + net_registry_deletekey_recursive, + NET_TRANSPORT_LOCAL, + N_("Delete a registry key with subkeys"), + N_("net registry deletekey_recursive\n" + " Delete a registry key with subkeys") + }, + { + "getvalue", + net_registry_getvalue, + NET_TRANSPORT_LOCAL, + N_("Print a registry value"), + N_("net registry getvalue\n" + " Print a registry value") + }, + { + "getvalueraw", + net_registry_getvalueraw, + NET_TRANSPORT_LOCAL, + N_("Print a registry value (raw format)"), + N_("net registry getvalueraw\n" + " Print a registry value (raw format)") + }, + { + "getvaluesraw", + net_registry_getvaluesraw, + NET_TRANSPORT_LOCAL, + "Print all values of a key in raw format", + "net registry getvaluesraw <key>\n" + " Print a registry value (raw format)" + }, + { + "setvalue", + net_registry_setvalue, + NET_TRANSPORT_LOCAL, + N_("Set a new registry value"), + N_("net registry setvalue\n" + " Set a new registry value") + }, + { + "increment", + net_registry_increment, + NET_TRANSPORT_LOCAL, + N_("Increment a DWORD registry value under a lock"), + N_("net registry increment\n" + " Increment a DWORD registry value under a lock") + }, + { + "deletevalue", + net_registry_deletevalue, + NET_TRANSPORT_LOCAL, + N_("Delete a registry value"), + N_("net registry deletevalue\n" + " Delete a registry value") + }, + { + "getsd", + net_registry_getsd, + NET_TRANSPORT_LOCAL, + N_("Get security descriptor"), + N_("net registry getsd\n" + " Get security descriptor") + }, + { + "getsd_sddl", + net_registry_getsd_sddl, + NET_TRANSPORT_LOCAL, + N_("Get security descriptor in sddl format"), + N_("net registry getsd_sddl\n" + " Get security descriptor in sddl format") + }, + { + "setsd_sddl", + net_registry_setsd_sddl, + NET_TRANSPORT_LOCAL, + N_("Set security descriptor from sddl format string"), + N_("net registry setsd_sddl\n" + " Set security descriptor from sddl format string") + }, + { + "import", + net_registry_import, + NET_TRANSPORT_LOCAL, + N_("Import .reg file"), + N_("net registry import\n" + " Import .reg file") + }, + { + "export", + net_registry_export, + NET_TRANSPORT_LOCAL, + N_("Export .reg file"), + N_("net registry export\n" + " Export .reg file") + }, + { + "convert", + net_registry_convert, + NET_TRANSPORT_LOCAL, + N_("Convert .reg file"), + N_("net registry convert\n" + " Convert .reg file") + }, + { + "check", + net_registry_check, + NET_TRANSPORT_LOCAL, + N_("Check a registry database"), + N_("net registry check\n" + " Check a registry database") + }, + { NULL, NULL, 0, NULL, NULL } + }; + + if (!c->display_usage + && argc > 0 + && (strcasecmp_m(argv[0], "convert") != 0) + && (strcasecmp_m(argv[0], "check") != 0)) + { + if (!W_ERROR_IS_OK(registry_init_basic())) { + return -1; + } + } + + ret = net_run_function(c, argc, argv, "net registry", func); + + return ret; +} diff --git a/source3/utils/net_registry_check.c b/source3/utils/net_registry_check.c new file mode 100644 index 0000000..6cb9256 --- /dev/null +++ b/source3/utils/net_registry_check.c @@ -0,0 +1,1324 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Check the registry database. + * @author Gregor Beck <gb@sernet.de> + * @date Mar 2011 + */ + +#include "net_registry_check.h" + +#include "includes.h" +#include "system/filesys.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_open.h" +#include "lib/dbwrap/dbwrap_rbt.h" +#include "net.h" +#include "libcli/security/dom_sid.h" +#include "libcli/security/secdesc.h" +#include "cbuf.h" +#include "srprs.h" +#include <termios.h> +#include "util_tdb.h" +#include "registry/reg_db.h" +#include "libcli/registry/util_reg.h" +#include "registry/reg_parse_internal.h" +#include "interact.h" + +/* + check tree: + + every key has a subkeylist + + every key is referenced by the subkeylist of its parent + check path: + + starts with valid hive + + UTF-8 (option to convert ???) + + only uppercase + + separator ??? + check value: + + REG_DWORD has size 4 + + REG_QWORD has size 8 + + STRINGS are zero terminated UTF-16 +*/ + +struct regval { + char *name; + uint32_t type; + DATA_BLOB data; +}; + +struct regkey { + char *name; + char *path; + bool has_subkeylist; + bool needs_update; + struct regkey *parent; + size_t nsubkeys; + struct regkey **subkeys; + size_t nvalues; + struct regval **values; + struct security_descriptor *sd; +}; + +struct check_ctx { + char *fname; + struct check_options opt; + + uint32_t version; + char sep; + struct db_context *idb; + struct db_context *odb; + + struct regkey *root; /*dummy key to hold all basekeys*/ + struct db_context *reg; + struct db_context *del; + + bool transaction; + char auto_action; + char default_action; +}; + +static void* talloc_array_append(void *mem_ctx, void* array[], void *ptr) +{ + size_t size = array ? talloc_array_length(array) : 1; + void **tmp = talloc_realloc(mem_ctx, array, void*, size + 1); + if (tmp == NULL) { + talloc_free(array); + return NULL; + } + tmp[size-1] = ptr; + tmp[size] = NULL; + return tmp; +} + +static void regkey_add_subkey(struct regkey *key, struct regkey *subkey) +{ + key->subkeys = (struct regkey**) + talloc_array_append(key, (void**)key->subkeys, subkey); + if (key->subkeys != NULL) { + key->nsubkeys++; + } +} + +static struct regval* regval_copy(TALLOC_CTX *mem_ctx, const struct regval *val) +{ + struct regval *ret = talloc_zero(mem_ctx, struct regval); + if (ret == NULL) { + goto fail; + } + + ret->name = talloc_strdup(ret, val->name); + if (ret->name == NULL) { + goto fail; + } + + ret->data = data_blob_dup_talloc(ret, val->data); + if (ret->data.data == NULL) { + goto fail; + } + + ret->type = val->type; + + return ret; +fail: + talloc_free(ret); + return NULL; +} + +static void regkey_add_regval(struct regkey *key, struct regval *val) +{ + key->values = (struct regval**) + talloc_array_append(key, (void**)key->values, val); + if (key->values != NULL) { + key->nvalues++; + } +} + +static bool tdb_data_read_uint32(TDB_DATA *buf, uint32_t *result) +{ + const size_t len = sizeof(uint32_t); + if (buf->dsize >= len) { + *result = IVAL(buf->dptr, 0); + buf->dptr += len; + buf->dsize -= len; + return true; + } + return false; +} + +static bool tdb_data_read_cstr(TDB_DATA *buf, char **result) +{ + const size_t len = strnlen((char*)buf->dptr, buf->dsize) + 1; + if (buf->dsize >= len) { + *result = (char*)buf->dptr; + buf->dptr += len; + buf->dsize -= len; + return true; + } + return false; +} + +static bool tdb_data_read_blob(TDB_DATA *buf, DATA_BLOB *result) +{ + TDB_DATA tmp = *buf; + uint32_t len; + if (!tdb_data_read_uint32(&tmp, &len)) { + return false; + } + if (tmp.dsize >= len) { + *buf = tmp; + result->data = tmp.dptr; + result->length = len; + buf->dptr += len; + buf->dsize -= len; + return true; + } + return false; +} + +static bool tdb_data_read_regval(TDB_DATA *buf, struct regval *result) +{ + TDB_DATA tmp = *buf; + struct regval value; + if (!tdb_data_read_cstr(&tmp, &value.name) + || !tdb_data_read_uint32(&tmp, &value.type) + || !tdb_data_read_blob(&tmp, &value.data)) + { + return false; + } + *buf = tmp; + *result = value; + return true; +} + +static bool tdb_data_is_cstr(TDB_DATA d) { + if (tdb_data_is_empty(d) || (d.dptr[d.dsize-1] != '\0')) { + return false; + } + return strlen((char *)d.dptr) == d.dsize-1; +} + +static TDB_DATA cbuf_make_tdb_data(cbuf *b) +{ + return make_tdb_data((void*)cbuf_gets(b, 0), cbuf_getpos(b)); +} + +static void remove_all(char *str, char c) +{ + char *out=str; + while (*str) { + if (*str != c) { + *out = *str; + out++; + } + str++; + } + *out = '\0'; +} + +static char* parent_path(const char *path, char sep) +{ + const char *p = strrchr(path, sep); + return p ? talloc_strndup(talloc_tos(), path, p-path) : NULL; +} + +/* return the regkey corresponding to path, create if not yet existing */ +static struct regkey* +check_ctx_lookup_key(struct check_ctx *ctx, const char *path) { + struct regkey *ret = NULL; + NTSTATUS status; + TDB_DATA val = tdb_null; + + if ( path == NULL) { + return ctx->root; + } + + status = dbwrap_fetch(ctx->reg, ctx, string_term_tdb_data(path), &val); + if (NT_STATUS_IS_OK(status)) { + if (ctx->opt.verbose) { + printf("Open: %s\n", path); + } + ret = *(struct regkey**)val.dptr; + } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + /* not yet existing, create */ + char *pp; + if (ctx->opt.verbose) { + printf("New: %s\n", path); + } + ret = talloc_zero(ctx, struct regkey); + if (ret == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + ret->path = talloc_strdup(ret, path); + + pp = parent_path(path, ctx->sep); + ret->parent = check_ctx_lookup_key(ctx, pp); + regkey_add_subkey(ret->parent, ret); + TALLOC_FREE(pp); + + /* the dummy root key has no subkeylist so set the name */ + if (ret->parent == ctx->root) { + ret->name = talloc_strdup(ret, path); + } + + dbwrap_store(ctx->reg, string_term_tdb_data(path), + make_tdb_data((void*)&ret, sizeof(ret)), 0); + } else { + DEBUG(0, ("lookup key: failed to fetch %s: %s\n", path, + nt_errstr(status))); + } +done: + talloc_free(val.dptr); + return ret; +} + +static struct check_ctx* check_ctx_create(TALLOC_CTX *mem_ctx, const char *db, + const struct check_options *opt) +{ + struct check_ctx *ctx = talloc_zero(mem_ctx, struct check_ctx); + + ctx->opt = *opt; + ctx->reg = db_open_rbt(ctx); + ctx->del = db_open_rbt(ctx); + ctx->root = talloc_zero(ctx, struct regkey); + ctx->fname = talloc_strdup(ctx, db); + + if (opt->automatic && (opt->output == NULL)) { + ctx->opt.repair = true; + ctx->opt.output = ctx->fname; + } + + if (opt->repair) { + if (opt->output) { + d_fprintf(stderr, "You can not specify --output " + "with --repair\n"); + goto fail; + } else { + ctx->opt.output = ctx->fname; + } + } + + ctx->default_action = 'r'; + return ctx; +fail: + talloc_free(ctx); + return NULL; +} + +static bool check_ctx_open_output(struct check_ctx *ctx) +{ + int oflags = O_RDWR | O_CREAT ; + + if (ctx->opt.output == NULL) { + return true; + } + + if (!ctx->opt.repair) { + if (!ctx->opt.wipe) { + oflags |= O_EXCL; + } + ctx->opt.wipe = true; + } + + ctx->odb = db_open(ctx, ctx->opt.output, 0, TDB_DEFAULT, oflags, 0644, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (ctx->odb == NULL) { + d_fprintf(stderr, + _("Could not open db (%s) for writing: %s\n"), + ctx->opt.output, strerror(errno)); + return false; + } + return true; +} + + +static bool check_ctx_open_input(struct check_ctx *ctx) { + ctx->idb = db_open(ctx, ctx->fname, 0, TDB_DEFAULT, O_RDONLY, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + if (ctx->idb == NULL) { + d_fprintf(stderr, + _("Could not open db (%s) for reading: %s\n"), + ctx->fname, strerror(errno)); + return false; + } + return true; +} + +static bool check_ctx_transaction_start(struct check_ctx *ctx) { + if (ctx->odb == NULL) { + return true; + } + if (dbwrap_transaction_start(ctx->odb) != 0) { + DEBUG(0, ("transaction_start failed\n")); + return false; + } + ctx->transaction = true; + return true; +} + +static void check_ctx_transaction_stop(struct check_ctx *ctx, bool ok) { + if (!ctx->transaction) { + return; + } + if (!ctx->opt.test && ok) { + d_printf("Committing changes\n"); + if (dbwrap_transaction_commit(ctx->odb) != 0) { + DEBUG(0, ("transaction_commit failed\n")); + } + } else { + d_printf("Discarding changes\n"); + dbwrap_transaction_cancel(ctx->odb); + } +} + +static bool read_info(struct check_ctx *ctx, const char *key, TDB_DATA val) +{ + if (val.dsize==sizeof(uint32_t) && strcmp(key, "version")==0) { + uint32_t v = IVAL(val.dptr, 0); + printf("INFO: %s = %d\n", key, v); + return true; + } + printf("INFO: %s = <invalid>\n", key); + return false; +} + +static bool is_all_upper(const char *str) { + bool ret; + char *tmp = talloc_strdup(talloc_tos(), str); + if (!strupper_m(tmp)) { + talloc_free(tmp); + return false; + } + ret = (strcmp(tmp, str) == 0); + talloc_free(tmp); + return ret; +} + +static void move_to_back(struct regkey *key, struct regkey *subkey) +{ + struct regkey **ptr; + size_t nidx; + + DEBUG(5, ("Move to back subkey \"%s\" of \"%s\"\n", + subkey->path, key->path)); + + for (ptr=key->subkeys; *ptr != subkey; ptr++) + ; + + nidx = ptr + 1 - key->subkeys; + memmove(ptr, ptr+1, (key->nsubkeys - nidx) * sizeof(*ptr)); + + key->subkeys[key->nsubkeys-1] = subkey; +} + +static void set_subkey_name(struct check_ctx *ctx, struct regkey *key, + const char *name, int nlen) +{ + char *path = key->path; + TALLOC_CTX *mem_ctx = talloc_new(talloc_tos()); + char *p; + struct regkey *subkey; + char *nname = talloc_strndup(mem_ctx, name, nlen); + remove_all(nname, ctx->sep); + + if (strncmp(name, nname, nlen) != 0) { + /* XXX interaction: delete/edit */ + printf("Warning: invalid name: \"%s\" replace with \"%s\"\n", + name, nname); + key->needs_update = true; + } + p = talloc_asprintf_strupper_m(mem_ctx, "%s%c%s", + path, ctx->sep, nname); + subkey = check_ctx_lookup_key(ctx, p); + if (subkey->name) { + bool do_replace = false; + + if (strcmp(subkey->name, nname) != 0) { + int action; + char default_action; + + if (is_all_upper(nname)) { + default_action = 'o'; + } else { + default_action = 'n'; + } + + printf("Conflicting subkey names of [%s]: " + "old: \"%s\", new: \"%s\"\n", + key->path, subkey->name, nname); + + if (ctx->opt.output == NULL || ctx->opt.automatic) { + action = default_action; + } else { + do { + action = interact_prompt( + "choose spelling [o]ld, [n]ew," + "or [e]dit", "one", + default_action); + if (action == 'e') { + printf("Sorry, edit is not yet " + "implemented here...\n"); + } + } while (action == 'e'); + } + + if (action == 'n') { + do_replace = true; + } + } + + if (do_replace) { + if (ctx->opt.verbose) { + printf("Replacing name: %s: \"%s\"" + " -> \"%s\"\n", path, + subkey->name, nname); + } + TALLOC_FREE(subkey->name); + subkey->name = talloc_steal(subkey, nname); + key->needs_update = true; + } + } else { + if (ctx->opt.verbose) { + printf("Set name: %s: \"%s\"\n", path, nname); + } + subkey->name = talloc_steal(subkey, nname); + } + + move_to_back(key, subkey); + TALLOC_FREE(mem_ctx); +} + +static void +read_subkeys(struct check_ctx *ctx, const char *path, TDB_DATA val, bool update) +{ + uint32_t num_items, found_items = 0; + char *subkey; + struct regkey *key = check_ctx_lookup_key(ctx, path); + + key->needs_update |= update; + + /* printf("SUBKEYS: %s\n", path); */ + if (key->has_subkeylist) { + printf("Duplicate subkeylist \"%s\"\n", + path); + found_items = key->nsubkeys; + } + + /* exists as defined by regdb_key_exists() */ + key->has_subkeylist = true; + + /* name is set if a key is referenced by the */ + /* subkeylist of its parent. */ + + if (!tdb_data_read_uint32(&val, &num_items) ) { + printf("Invalid subkeylist: \"%s\"\n", path); + return; + } + + while (tdb_data_read_cstr(&val, &subkey)) { + /* printf(" SUBKEY: %s\n", subkey); */ + set_subkey_name(ctx, key, subkey, strlen(subkey)); + found_items++; + } + + if (val.dsize != 0) { + printf("Subkeylist of \"%s\": trailing: \"%.*s\"\n", + path, (int)val.dsize, val.dptr); + /* ask: best effort, delete or edit?*/ + set_subkey_name(ctx, key, (char*)val.dptr, val.dsize); + found_items++; + key->needs_update = true; + } + + if (num_items != found_items) { + printf("Subkeylist of \"%s\": invalid number of subkeys, " + "expected: %d got: %d\n", path, num_items, found_items); + key->needs_update = true; + } + +} + +static void read_values(struct check_ctx *ctx, const char *path, TDB_DATA val) +{ + struct regkey *key = check_ctx_lookup_key(ctx, path); + uint32_t num_items, found_items; + struct regval value; + + /* printf("VALUES: %s\n", path); */ + + if (!tdb_data_read_uint32(&val, &num_items) ) { + printf("Invalid valuelist: \"%s\"\n", path); + return; + } + + found_items=0; + while (tdb_data_read_regval(&val, &value)) { + /* printf(" VAL: %s type: %s(%d) length: %d\n", value.name, */ + /* str_regtype(value.type), value.type, */ + /* (int)value.data.length); */ + regkey_add_regval(key, regval_copy(key, &value)); + found_items++; + } + + if (num_items != found_items) { + printf("Valuelist of \"%s\": invalid number of values, " + "expected: %d got: %d\n", path, num_items, found_items); + key->needs_update = true; + } + + if (val.dsize != 0) { + printf("Valuelist of \"%s\": trailing: \"%*s\"\n", path, + (int)val.dsize, val.dptr); + key->needs_update = true; + /* XXX best effort ??? */ + /* ZERO_STRUCT(value); */ + /* if (tdb_data_read_cstr(&val, &value.name) */ + /* && tdb_data_read_uint32(&val, &value.type)) */ + /* { */ + /* uint32_t len = -1; */ + /* tdb_data_read_uint32(&val, &len); */ + /* ... */ + /* found_items ++; */ + /* regkey_add_regval(key, regval_copy(key, value)); */ + /* } */ + } + if (found_items == 0) { + printf("Valuelist of \"%s\" empty\n", path); + key->needs_update = true; + } +} + +static bool read_sorted(struct check_ctx *ctx, const char *path, TDB_DATA val) +{ + if (ctx->version >= 3) { + return false; + } + + if ((val.dptr == NULL) || (val.dsize<4)) { + return false; + } + + /* ToDo: check */ + /* struct regkey *key = check_ctx_lookup_key(ctx, path); */ + /* printf("SORTED: %s\n", path); */ + return true; +} + +static bool read_sd(struct check_ctx *ctx, const char *path, TDB_DATA val) +{ + NTSTATUS status; + struct regkey *key = check_ctx_lookup_key(ctx, path); + /* printf("SD: %s\n", path); */ + + status = unmarshall_sec_desc(key, val.dptr, val.dsize, &key->sd); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to read SD of %s: %s\n", + path, nt_errstr(status))); + } + return true; +} + +static bool srprs_path(const char **ptr, const char* prefix, char sep, + const char **ppath) +{ + const char *path, *pos = *ptr; + if (prefix != NULL) { + if (!srprs_str(&pos, prefix, -1) || !srprs_char(&pos, sep) ) { + return false; + } + } + path = pos; + if ( !srprs_hive(&pos, NULL) ) { + return false; + } + if ( !srprs_eos(&pos) && !srprs_char(&pos, sep) ) { + return false; + } + *ppath = path; + *ptr = strchr(pos, '\0'); + return true; +} + +/* Fixme: this doesn't work in the general multibyte char case. + see string_replace() +*/ +static bool normalize_path_internal(char* path, char sep) { + size_t len = strlen(path); + const char *orig = talloc_strndup(talloc_tos(), path, len); + char *optr = path, *iptr = path; + bool changed; + + while (*iptr == sep ) { + iptr++; + } + while (*iptr) { + *optr = *iptr; + if (*iptr == sep) { + while (*iptr == sep) { + iptr++; + } + if (*iptr) { + optr++; + } + } else { + iptr++; + optr++; + } + } + *optr = '\0'; + + if (!strupper_m(path)) { + talloc_free(discard_const(orig)); + return false; + } + changed = (strcmp(orig, path) != 0); + talloc_free(discard_const(orig)); + return changed; +} + +static bool normalize_path(char* path, char sep) { + static const char* SEPS = "\\/"; + char* firstsep = strpbrk(path, SEPS); + bool wrong_sep = (firstsep && (*firstsep != sep)); + + assert (strchr(SEPS, sep)); + + if (wrong_sep) { + string_replace(path, *firstsep, sep); + } + return normalize_path_internal(path, sep) || wrong_sep; +} + +static int check_tdb_action(struct db_record *rec, void *check_ctx) +{ + struct check_ctx *ctx = (struct check_ctx*)check_ctx; + TALLOC_CTX *frame = talloc_stackframe(); + TDB_DATA val = dbwrap_record_get_value(rec); + TDB_DATA rec_key = dbwrap_record_get_key(rec); + char *key; + bool invalid_path = false; + bool once_more; + bool first_iter = true; + + if (!tdb_data_is_cstr(rec_key)) { + printf("Key is not zero terminated: \"%.*s\"\ntry to go on.\n", + (int)rec_key.dsize, rec_key.dptr); + invalid_path = true; + } + key = talloc_strndup(frame, (char*)rec_key.dptr, rec_key.dsize); + + do { + const char *path, *pos = key; + once_more = false; + + if (srprs_str(&pos, "INFO/", -1)) { + if ( read_info(ctx, pos, val) ) { + break; + } + invalid_path = true; + /* ask: mark invalid */ + } else if (srprs_str(&pos, "__db_sequence_number__", -1)) { + printf("Skip key: \"%.*s\"\n", + (int)rec_key.dsize, rec_key.dptr); + /* skip: do nothing + break */ + break; + + } else if (normalize_path(key, ctx->sep)) { + printf("Unnormal key: \"%.*s\"\n", + (int)rec_key.dsize, rec_key.dptr); + printf("Normalize to: \"%s\"\n", key); + invalid_path = true; + } else if (srprs_path(&pos, NULL, + ctx->sep, &path)) + { + read_subkeys(ctx, path, val, invalid_path); + break; + } else if (srprs_path(&pos, REG_VALUE_PREFIX, + ctx->sep, &path)) + { + read_values(ctx, path, val); + break; + } else if (srprs_path(&pos, REG_SECDESC_PREFIX, + ctx->sep, &path)) + { + read_sd(ctx, path, val); + break; + } else if (srprs_path(&pos, REG_SORTED_SUBKEYS_PREFIX, + ctx->sep, &path)) + { + if (!read_sorted(ctx, path, val)) { + /* delete: mark invalid + break */ + printf("Invalid sorted subkeys for: \"%s\"\n", path); + invalid_path = true; + key = NULL; + } + break; + } else { + printf("Unrecognized key: \"%.*s\"\n", + (int)rec_key.dsize, rec_key.dptr); + invalid_path = true; + } + + if (invalid_path) { + unsigned char action; + if (ctx->opt.output == NULL) { + action = first_iter ? 'r' : 's'; + } else if (ctx->opt.automatic) { + action = first_iter ? 'r' : 'd'; + } else if (ctx->auto_action != '\0') { + action = ctx->auto_action; + } else { + action = interact_prompt("[s]kip,[S]kip all," + "[d]elete,[D]elete all" + ",[e]dit,[r]etry" + , "sder", + ctx->default_action); + } + if (isupper(action)) { + action = tolower(action); + ctx->auto_action = action; + } + ctx->default_action = action; + switch (action) { + case 's': /* skip */ + invalid_path = false; + break; + case 'd': /* delete */ + invalid_path = true; + key = NULL; + break; + case 'e': /* edit */ { + char *p = interact_edit(frame, key); + if (p) { + talloc_free(key); + key = p; + } + FALL_THROUGH; + } + case 'r': /* retry */ + once_more = true; + break; + } + } + first_iter = false; + } while (once_more); + + if (invalid_path) { + dbwrap_store(ctx->del, rec_key, string_term_tdb_data(key), 0); + } + + talloc_free(frame); + return 0; +} + +static bool get_version(struct check_ctx *ctx) { + static const uint32_t curr_version = REGDB_CODE_VERSION; + uint32_t version = ctx->opt.version ? ctx->opt.version : curr_version; + uint32_t info_version = 0; + NTSTATUS status; + + status = dbwrap_fetch_uint32_bystring(ctx->idb, "INFO/version", + &info_version); + if (!NT_STATUS_IS_OK(status)) { + printf("Warning: no INFO/version found!\n"); + /* info_version = guess_version(ctx); */ + } + + if (ctx->opt.version) { + version = ctx->opt.version; + } else if (ctx->opt.implicit_db) { + version = curr_version; + } else { + version = info_version; + } + + if (!version) { + printf("Couldn't determine registry format version, " + "specify with --reg-version\n"); + return false; + } + + + if ( version != info_version ) { + if (ctx->opt.force || !ctx->opt.repair) { + printf("Warning: overwrite registry format " + "version %d with %d\n", info_version, version); + } else { + printf("Warning: found registry format version %d but " + "expected %d, use --force to proceed.\n", info_version, version); + return false; + } + } + + ctx->version = version; + ctx->sep = (version > 1) ? '\\' : '/'; + + return true; +} + +static bool +dbwrap_store_verbose(struct db_context *db, const char *key, TDB_DATA nval) +{ + TALLOC_CTX *mem_ctx = talloc_new(talloc_tos()); + TDB_DATA oval; + NTSTATUS status; + + status = dbwrap_fetch_bystring(db, mem_ctx, key, &oval); + if (NT_STATUS_IS_OK(status)) { + if (tdb_data_equal(nval, oval)) { + goto done; + } + printf("store %s:\n overwrite: %s\n with: %s\n", key, + tdb_data_string(mem_ctx, oval), + tdb_data_string(mem_ctx, nval)); + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + printf("store %s:\n write: %s\n", key, + tdb_data_string(mem_ctx, nval)); + } else { + printf ("store %s:\n failed to fetch old value: %s\n", key, + nt_errstr(status)); + goto done; + } + + status = dbwrap_store_bystring(db, key, nval, 0); + if (!NT_STATUS_IS_OK(status)) { + printf ("store %s failed: %s\n", key, nt_errstr(status)); + } + +done: + talloc_free(mem_ctx); + return NT_STATUS_IS_OK(status); +} + +static bool +dbwrap_store_uint32_verbose(struct db_context *db, const char *key, uint32_t nval) +{ + uint32_t oval; + NTSTATUS status; + + status = dbwrap_fetch_uint32_bystring(db, key, &oval); + if (NT_STATUS_IS_OK(status)) { + if (nval == oval) { + goto done; + } + printf("store %s:\n overwrite: %d\n with: %d\n", key, + (int)oval, (int)nval); + + } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + printf("store %s:\n write: %d\n", key, (int)nval); + } else { + printf ("store %s:\n failed to fetch old value: %s\n", key, + nt_errstr(status)); + goto done; + } + + status = dbwrap_store_uint32_bystring(db, key, nval); + if (!NT_STATUS_IS_OK(status)) { + printf ("store %s failed: %s\n", key, nt_errstr(status)); + } + +done: + return NT_STATUS_IS_OK(status); +} + +static int cmp_keynames(char **p1, char **p2) +{ + return strcasecmp_m(*p1, *p2); +} + +static bool +write_subkeylist(struct db_context *db, struct regkey *key, char sep) +{ + cbuf *buf = cbuf_new(talloc_tos()); + size_t i; + bool ret; + + cbuf_putdw(buf, key->nsubkeys); + + for (i=0; i < key->nsubkeys; i++) { + struct regkey *subkey = key->subkeys[i]; + const char *name = subkey->name; + if (name == NULL) { + printf("Warning: no explicit name for key %s\n", + subkey->path); + name = strrchr_m(subkey->path, sep); + assert(name); + name ++; + } + cbuf_puts(buf, name, -1); + cbuf_putc(buf, '\0'); + } + + ret = dbwrap_store_verbose(db, key->path, cbuf_make_tdb_data(buf)); + + talloc_free(buf); + return ret; +} + +static bool write_sorted(struct db_context *db, struct regkey *key, char sep) +{ + cbuf *buf = cbuf_new(talloc_tos()); + char *path; + size_t i; + bool ret = false; + char **sorted = talloc_zero_array(buf, char*, key->nsubkeys); + int offset = (1 + key->nsubkeys) * sizeof(uint32_t); + + for (i=0; i < key->nsubkeys; i++) { + sorted[i] = talloc_strdup_upper(sorted, key->subkeys[i]->name); + } + TYPESAFE_QSORT(sorted, key->nsubkeys, cmp_keynames); + + cbuf_putdw(buf, key->nsubkeys); + for (i=0; i < key->nsubkeys; i++) { + cbuf_putdw(buf, offset); + offset += strlen(sorted[i]) + 1; + } + for (i=0; i < key->nsubkeys; i++) { + cbuf_puts(buf, sorted[i], -1); + cbuf_putc(buf, '\0'); + } + + path = talloc_asprintf(buf, "%s%c%s", REG_SORTED_SUBKEYS_PREFIX, sep, + key->path); + if (path == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf)); +done: + talloc_free(buf); + return ret; +} + +static bool write_values(struct db_context *db, struct regkey *key, char sep) +{ + cbuf *buf = cbuf_new(talloc_tos()); + char *path; + size_t i; + bool ret = false; + + cbuf_putdw(buf, key->nvalues); + for (i=0; i < key->nvalues; i++) { + struct regval *val = key->values[i]; + cbuf_puts(buf, val->name, -1); + cbuf_putc(buf, '\0'); + cbuf_putdw(buf, val->type); + cbuf_putdw(buf, val->data.length); + cbuf_puts(buf, (void*)val->data.data, val->data.length); + } + + path = talloc_asprintf(buf, "%s%c%s", REG_VALUE_PREFIX, sep, key->path); + if (path == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + ret = dbwrap_store_verbose(db, path, cbuf_make_tdb_data(buf)); +done: + talloc_free(buf); + return ret; +} + +static bool write_sd(struct db_context *db, struct regkey *key, char sep) +{ + TDB_DATA sd; + NTSTATUS status; + char *path; + bool ret = false; + TALLOC_CTX *mem_ctx = talloc_new(talloc_tos()); + + status = marshall_sec_desc(mem_ctx, key->sd, &sd.dptr, &sd.dsize); + if (!NT_STATUS_IS_OK(status)) { + printf("marshall sec desc %s failed: %s\n", + key->path, nt_errstr(status)); + goto done; + } + path = talloc_asprintf(mem_ctx, "%s%c%s", REG_SECDESC_PREFIX, + sep, key->path); + if (path == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + ret = dbwrap_store_verbose(db, path, sd); +done: + talloc_free(mem_ctx); + return ret; +} + + +static int check_write_db_action(struct db_record *rec, void *check_ctx) +{ + struct check_ctx *ctx = (struct check_ctx*)check_ctx; + TDB_DATA rec_val = dbwrap_record_get_value(rec); + struct regkey *key = *(struct regkey**)rec_val.dptr; + TALLOC_CTX *frame = talloc_stackframe(); + + /* write subkeylist */ + if ((ctx->version > 2) || (key->nsubkeys > 0) || (key->has_subkeylist)) { + write_subkeylist(ctx->odb, key, ctx->sep); + } + + /* write sorted subkeys */ + if ((ctx->version < 3) && (key->nsubkeys > 0)) { + write_sorted(ctx->odb, key, ctx->sep); + } + + /* write value list */ + if (key->nvalues > 0) { + write_values(ctx->odb, key, ctx->sep); + } + + /* write sd */ + if (key->sd) { + write_sd(ctx->odb, key, ctx->sep); + } + + talloc_free(frame); + return 0; +} + +static int fix_tree_action(struct db_record *rec, void *check_ctx) +{ + struct check_ctx *ctx = (struct check_ctx*)check_ctx; + TDB_DATA rec_key = dbwrap_record_get_key(rec); + TDB_DATA rec_val = dbwrap_record_get_value(rec); + struct regkey* key = *(struct regkey**)rec_val.dptr; + if (ctx->opt.verbose) { + printf("Check Tree: %s\n", key->path); + } + + assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0); + + /* assert(dbwrap_exists(ctx->db, string_term_tdb_data(key->path)) */ + /* == key->exists); */ + + if (key->needs_update) { + printf("Update key: \"%s\"\n", key->path); + if ((ctx->version > 2) || (key->nsubkeys > 0)) { + write_subkeylist(ctx->odb, key, ctx->sep); + } + if ((ctx->version <= 2) && (key->nsubkeys > 0)) { + write_sorted(ctx->odb, key, ctx->sep); + } + if (key->nvalues > 0) { + write_values(ctx->odb, key, ctx->sep); + } + if (key->sd) { + write_sd(ctx->odb, key, ctx->sep); + } + } else if (!key->has_subkeylist) { + if ((ctx->version > 2) || (key->nsubkeys > 0)) { + printf("Missing subkeylist: %s\n", key->path); + write_subkeylist(ctx->odb, key, ctx->sep); + } + } + + if (key->name == NULL && key->parent->has_subkeylist) { + printf("Key not referenced by the its parents subkeylist: %s\n", + key->path); + write_subkeylist(ctx->odb, key->parent, ctx->sep); + } + +/* XXX check that upcase(name) matches last part of path ??? */ + + return 0; +} + + +/* give the same warnings as fix_tree_action */ +static int check_tree_action(struct db_record *rec, void *check_ctx) +{ + struct check_ctx *ctx = (struct check_ctx*)check_ctx; + TDB_DATA rec_key = dbwrap_record_get_key(rec); + TDB_DATA rec_val = dbwrap_record_get_value(rec); + struct regkey* key = *(struct regkey**)rec_val.dptr; + if (ctx->opt.verbose) { + printf("Check Tree: %s\n", key->path); + } + + assert (strncmp(key->path, (char*)rec_key.dptr, rec_key.dsize) == 0); + + if (!key->has_subkeylist) { + if ((ctx->version > 2) || (key->nsubkeys > 0)) { + printf("Missing subkeylist: %s\n", key->path); + } + } + + if (key->name == NULL && key->parent->has_subkeylist) { + printf("Key not referenced by the its parents subkeylist: %s\n", + key->path); + } + + return 0; +} + +static int delete_invalid_action(struct db_record *rec, void* check_ctx) +{ + NTSTATUS status; + struct check_ctx *ctx = (struct check_ctx*)check_ctx; + TDB_DATA rec_key = dbwrap_record_get_key(rec); + TDB_DATA rec_val = dbwrap_record_get_value(rec); + + + printf("Delete key: \"%.*s\"",(int)rec_key.dsize, rec_key.dptr); + if (rec_val.dsize > 0) { + printf(" in favour of \"%s\"\n", rec_val.dptr); + } else { + putc('\n', stdout); + } + + status = dbwrap_delete(ctx->odb, rec_key); + if (!NT_STATUS_IS_OK(status)) { + d_printf("delete key \"%.*s\" failed!\n", + (int)rec_key.dsize, rec_key.dptr); + return -1; + } + return 0; +} + +static bool check_ctx_check_tree(struct check_ctx *ctx) { + NTSTATUS status; + + status = dbwrap_traverse(ctx->reg, check_tree_action, ctx, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("check traverse failed: %s\n", + nt_errstr(status))); + return false; + } + return true; +} +static bool check_ctx_fix_inplace(struct check_ctx *ctx) { + NTSTATUS status; + status = dbwrap_traverse(ctx->reg, fix_tree_action, ctx, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("fix traverse failed: %s\n", nt_errstr(status))); + return false; + } + + status = dbwrap_traverse(ctx->del, delete_invalid_action, ctx, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("delete traverse failed: %s\n", nt_errstr(status))); + return false; + } + + if (!dbwrap_store_uint32_verbose(ctx->odb, "INFO/version", ctx->version)) { + DEBUG(0, ("storing version failed: %s\n", nt_errstr(status))); + return false; + } + + return true; +} + +static bool check_ctx_write_new_db(struct check_ctx *ctx) { + NTSTATUS status; + + assert(ctx->odb); + + if (ctx->opt.wipe) { + int ret = dbwrap_wipe(ctx->odb); + if (ret != 0) { + DEBUG(0, ("wiping %s failed\n", ctx->opt.output)); + return false; + } + } + + status = dbwrap_traverse(ctx->reg, check_write_db_action, ctx, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("traverse2 failed: %s\n", nt_errstr(status))); + return false; + } + + status = dbwrap_store_uint32_bystring(ctx->odb, "INFO/version", + ctx->version); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("write version failed: %s\n", nt_errstr(status))); + return false; + } + return true; +} + +int net_registry_check_db(const char *name, const struct check_options *opt) +{ + NTSTATUS status; + int ret = -1; + struct check_ctx *ctx = check_ctx_create(talloc_tos(), name, opt); + if (ctx==NULL) { + goto done; + } + + d_printf("Check database: %s\n", name); + + /* 1. open output RW */ + if (!check_ctx_open_output(ctx)) { + goto done; + } + + /* 2. open input RO */ + if (!check_ctx_open_input(ctx)) { + goto done; + } + + if (opt->lock && !check_ctx_transaction_start(ctx)) { + goto done; + } + + if (!get_version(ctx)) { + goto done; + } + + status = dbwrap_traverse_read(ctx->idb, check_tdb_action, ctx, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("check traverse failed: %s\n", nt_errstr(status))); + goto done; + } + + if (!opt->lock && !check_ctx_transaction_start(ctx)) { + goto done; + } + + if (ctx->opt.repair && !ctx->opt.wipe) { + if (!check_ctx_fix_inplace(ctx)) { + goto done; + } + } else { + if (!check_ctx_check_tree(ctx)) { + goto done; + } + if (ctx->odb) { + if (!check_ctx_write_new_db(ctx)) { + goto done; + } + } + } + ret = 0; +done: + check_ctx_transaction_stop(ctx, ret == 0); + + talloc_free(ctx); + return ret; +} + +/*Local Variables:*/ +/*mode: c*/ +/*End:*/ diff --git a/source3/utils/net_registry_check.h b/source3/utils/net_registry_check.h new file mode 100644 index 0000000..6ed68d8 --- /dev/null +++ b/source3/utils/net_registry_check.h @@ -0,0 +1,52 @@ +/* + * Samba Unix/Linux SMB client library + * + * Copyright (C) Gregor Beck 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * @brief Check the registry database. + * @author Gregor Beck <gb@sernet.de> + * @date Jun 2011 + */ + +#ifndef NET_REGISTRY_CHECK_H +#define NET_REGISTRY_CHECK_H + +#include <stdbool.h> + +struct net_context; + +struct check_options { + bool test; + bool verbose; + bool lock; + bool automatic; + bool force; + bool repair; + int version; + const char *output; + bool wipe; + bool implicit_db; +}; + +int net_registry_check_db(const char* db, const struct check_options* opts); + +#endif /* NET_REGISTRY_CHECK_H */ + +/*Local Variables:*/ +/*mode: c*/ +/*End:*/ diff --git a/source3/utils/net_registry_util.c b/source3/utils/net_registry_util.c new file mode 100644 index 0000000..50b8a8a --- /dev/null +++ b/source3/utils/net_registry_util.c @@ -0,0 +1,177 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * registry utility functions + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "registry.h" +#include "utils/net_registry_util.h" +#include "utils/net.h" +#include "../libcli/registry/util_reg.h" + +void print_registry_key(const char *keyname, NTTIME *modtime) +{ + const char *ts = _("None"); + char *freeme = NULL; + + if (modtime != 0) { + freeme = http_timestring(talloc_tos(), + nt_time_to_unix(*modtime)); + ts = freeme; + } + + d_printf(_("Keyname = %s\n"), keyname); + d_printf(_("Modtime = %s\n"), ts); + d_printf("\n"); + + TALLOC_FREE(freeme); +} + +void print_registry_value(const struct registry_value *valvalue, bool raw) +{ + if (!raw) { + d_printf(_("Type = %s\n"), + str_regtype(valvalue->type)); + } + switch(valvalue->type) { + case REG_DWORD: { + uint32_t v = 0; + if (valvalue->data.length >= 4) { + v = IVAL(valvalue->data.data, 0); + } + if (!raw) { + d_printf(_("Value = ")); + } + d_printf("%u\n", v); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; + + if (!pull_reg_sz(talloc_tos(), &valvalue->data, &s)) { + break; + } + if (!raw) { + d_printf(_("Value = \"")); + } + d_printf("%s", s); + if (!raw) { + d_printf("\""); + } + d_printf("\n"); + break; + } + case REG_MULTI_SZ: { + uint32_t j; + const char **a; + + if (!pull_reg_multi_sz(talloc_tos(), &valvalue->data, &a)) { + break; + } + for (j = 0; a[j] != NULL; j++) { + if (!raw) { + d_printf(_("Value[%3.3d] = \""), j); + } + d_printf("%s", a[j]); + if (!raw) { + d_printf("\""); + } + d_printf("\n"); + } + break; + } + case REG_BINARY: + if (!raw) { + d_printf(_("Value = ")); + } + d_printf(_("%d bytes\n"), (int)valvalue->data.length); + break; + default: + if (!raw) { + d_printf(_("Value = ")); + } + d_printf(_("<unprintable>\n")); + break; + } +} + +void print_registry_value_with_name(const char *valname, + const struct registry_value *valvalue) +{ + d_printf(_("Valuename = %s\n"), valname); + print_registry_value(valvalue, false); + d_printf("\n"); +} + +/** + * Split path into hive name and subkeyname + * normalizations performed: + * - if the path contains no '\\' characters, + * assume that the legacy format of using '/' + * as a separator is used and convert '/' to '\\' + * - strip trailing '\\' chars + */ +WERROR split_hive_key(TALLOC_CTX *ctx, const char *path, char **hivename, + char **subkeyname) +{ + char *p; + const char *tmp_subkeyname; + + if ((path == NULL) || (hivename == NULL) || (subkeyname == NULL)) { + return WERR_INVALID_PARAMETER; + } + + if (strlen(path) == 0) { + return WERR_INVALID_PARAMETER; + } + + if (strchr(path, '\\') == NULL) { + *hivename = talloc_string_sub(ctx, path, "/", "\\"); + } else { + *hivename = talloc_strdup(ctx, path); + } + + if (*hivename == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* strip trailing '\\' chars */ + p = strrchr(*hivename, '\\'); + while ((p != NULL) && (p[1] == '\0')) { + *p = '\0'; + p = strrchr(*hivename, '\\'); + } + + p = strchr(*hivename, '\\'); + + if ((p == NULL) || (*p == '\0')) { + /* just the hive - no subkey given */ + tmp_subkeyname = ""; + } else { + *p = '\0'; + tmp_subkeyname = p+1; + } + *subkeyname = talloc_strdup(ctx, tmp_subkeyname); + if (*subkeyname == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + return WERR_OK; +} diff --git a/source3/utils/net_registry_util.h b/source3/utils/net_registry_util.h new file mode 100644 index 0000000..61fd834 --- /dev/null +++ b/source3/utils/net_registry_util.h @@ -0,0 +1,41 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * registry utility functions + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __NET_REGISTRY_UTIL_H__ +#define __NET_REGISTRY_UTIL_H__ + +void print_registry_key(const char *keyname, NTTIME *modtime); + +void print_registry_value(const struct registry_value *valvalue, bool raw); + +void print_registry_value_with_name(const char *valname, + const struct registry_value *valvalue); + +/** + * Split path into hive name and subkeyname + * normalizations performed: + * - convert '/' to '\\' + * - strip trailing '\\' chars + */ +WERROR split_hive_key(TALLOC_CTX *ctx, const char *path, char **hivename, + char **subkeyname); + +#endif diff --git a/source3/utils/net_rpc.c b/source3/utils/net_rpc.c new file mode 100644 index 0000000..2a12b1a --- /dev/null +++ b/source3/utils/net_rpc.c @@ -0,0 +1,8408 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2004,2008 Guenther Deschner (gd@samba.org) + Copyright (C) 2005 Jeremy Allison (jra@samba.org) + Copyright (C) 2006 Jelmer Vernooij (jelmer@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "utils/net.h" +#include "libsmb/namequery.h" +#include "rpc_client/cli_pipe.h" +#include "../libcli/auth/libcli_auth.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/cli_samr.h" +#include "rpc_client/init_samr.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" +#include "../librpc/gen_ndr/ndr_netlogon_c.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" +#include "../librpc/gen_ndr/ndr_spoolss.h" +#include "../librpc/gen_ndr/ndr_initshutdown_c.h" +#include "../librpc/gen_ndr/ndr_winreg_c.h" +#include "secrets.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_net.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "clirap2.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "passdb.h" +#include "../libcli/smb/smbXcli_base.h" +#include "libsmb/dsgetdcname.h" +#include "lib/util/string_wrappers.h" + +static int net_mode_share; +static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask); + +/** + * @file net_rpc.c + * + * @brief RPC based subcommands for the 'net' utility. + * + * This file should contain much of the functionality that used to + * be found in rpcclient, except that the commands should change + * less often, and the functionality should be sane (the user is not + * expected to know a rid/sid before they conduct an operation etc.) + * + * @todo Perhaps eventually these should be split out into a number + * of files, as this could get quite big. + **/ + + +/** + * Many of the RPC functions need the domain sid. This function gets + * it at the start of every run + * + * @param cli A cli_state already connected to the remote machine + * + * @return The Domain SID of the remote machine. + **/ + +NTSTATUS net_get_remote_domain_sid(struct cli_state *cli, TALLOC_CTX *mem_ctx, + struct dom_sid **domain_sid, + const char **domain_name) +{ + struct rpc_pipe_client *lsa_pipe = NULL; + struct policy_handle pol; + NTSTATUS status, result; + union lsa_PolicyInformation *info = NULL; + struct dcerpc_binding_handle *b; + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &lsa_pipe); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not initialise lsa pipe\n")); + return status; + } + + b = lsa_pipe->binding_handle; + + status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "open_policy %s: %s\n", + _("failed"), + nt_errstr(status)); + return status; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_ACCOUNT_DOMAIN, + &info, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + d_fprintf(stderr, "lsaquery %s: %s\n", + _("failed"), + nt_errstr(status)); + return status; + } + + *domain_name = info->account_domain.name.string; + *domain_sid = info->account_domain.sid; + + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + TALLOC_FREE(lsa_pipe); + + return NT_STATUS_OK; +} + +/** + * Run a single RPC command, from start to finish. + * + * @param pipe_name the pipe to connect to (usually a PIPE_ constant) + * @param conn_flag a NET_FLAG_ combination. Passed to + * net_make_ipc_connection. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * @return A shell status integer (0 for success). + */ + +int run_rpc_command(struct net_context *c, + struct cli_state *cli_arg, + const struct ndr_interface_table *table, + int conn_flags, + rpc_command_fn fn, + int argc, + const char **argv) +{ + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + TALLOC_CTX *mem_ctx; + NTSTATUS nt_status; + struct dom_sid *domain_sid; + const char *domain_name; + int ret = -1; + + /* make use of cli_state handed over as an argument, if possible */ + if (!cli_arg) { + nt_status = net_make_ipc_connection(c, conn_flags, &cli); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("failed to make ipc connection: %s\n", + nt_errstr(nt_status))); + return -1; + } + } else { + cli = cli_arg; + } + + if (!cli) { + return -1; + } + + /* Create mem_ctx */ + + if (!(mem_ctx = talloc_init("run_rpc_command"))) { + DEBUG(0, ("talloc_init() failed\n")); + goto fail; + } + + nt_status = net_get_remote_domain_sid(cli, mem_ctx, &domain_sid, + &domain_name); + if (!NT_STATUS_IS_OK(nt_status)) { + goto fail; + } + + if (!(conn_flags & NET_FLAGS_NO_PIPE)) { + if (lp_client_schannel() + && (ndr_syntax_id_equal(&table->syntax_id, + &ndr_table_netlogon.syntax_id))) { + const char *remote_name = + smbXcli_conn_remote_name(cli->conn); + const struct sockaddr_storage *remote_sockaddr = + smbXcli_conn_remote_sockaddr(cli->conn); + + /* Always try and create an schannel netlogon pipe. */ + TALLOC_FREE(c->netlogon_creds); + nt_status = cli_rpc_pipe_open_schannel( + cli, c->msg_ctx, table, NCACN_NP, + domain_name, + remote_name, + remote_sockaddr, + &pipe_hnd, c, &c->netlogon_creds); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise schannel netlogon pipe. Error was %s\n", + nt_errstr(nt_status) )); + goto fail; + } + } else { + if (conn_flags & NET_FLAGS_SEAL) { + nt_status = cli_rpc_pipe_open_with_creds( + cli, table, + (conn_flags & NET_FLAGS_TCP) ? + NCACN_IP_TCP : NCACN_NP, + DCERPC_AUTH_TYPE_NTLMSSP, + DCERPC_AUTH_LEVEL_PRIVACY, + smbXcli_conn_remote_name(cli->conn), + smbXcli_conn_remote_sockaddr(cli->conn), + c->creds, &pipe_hnd); + } else { + nt_status = cli_rpc_pipe_open_noauth( + cli, table, + &pipe_hnd); + } + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise pipe %s. Error was %s\n", + table->name, + nt_errstr(nt_status) )); + goto fail; + } + } + } + + nt_status = fn(c, domain_sid, domain_name, cli, pipe_hnd, mem_ctx, argc, argv); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status))); + } else { + ret = 0; + DEBUG(5, ("rpc command function succeeded\n")); + } + + if (!(conn_flags & NET_FLAGS_NO_PIPE)) { + if (pipe_hnd) { + TALLOC_FREE(pipe_hnd); + } + } + +fail: + /* close the connection only if it was opened here */ + if (!cli_arg) { + cli_shutdown(cli); + } + + talloc_destroy(mem_ctx); + return ret; +} + +/** + * Force a change of the trust account password. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_changetrustpw_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status; + const char *dcname = NULL; + + if (cli == NULL) { + return NT_STATUS_INTERNAL_ERROR; + } + + dcname = smbXcli_conn_remote_name(cli->conn); + + status = trust_pw_change(c->netlogon_creds, + c->msg_ctx, + pipe_hnd->binding_handle, + c->opt_target_workgroup, + dcname, + true); /* force */ + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Failed to change machine account password: %s\n"), + nt_errstr(status)); + return status; + } + + return NT_STATUS_OK; +} + +/** + * Force a change of the trust account password. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +int net_rpc_changetrustpw(struct net_context *c, int argc, const char **argv) +{ + int conn_flags = NET_FLAGS_PDC; + + if (!c->opt_user_specified && !c->opt_kerberos) { + conn_flags |= NET_FLAGS_ANONYMOUS; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc changetrustpw\n" + " %s\n", + _("Usage:"), + _("Change the machine trust password")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_netlogon, + conn_flags, + rpc_changetrustpw_internals, + argc, argv); +} + +/** + * Join a domain, the old way. This function exists to allow + * the message to be displayed when oldjoin was explicitly + * requested, but not when it was implied by "net rpc join". + * + * This uses 'machinename' as the initial password, and changes it. + * + * The password should be created with 'server manager' or equiv first. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int net_rpc_oldjoin(struct net_context *c, int argc, const char **argv) +{ + struct libnet_JoinCtx *r = NULL; + TALLOC_CTX *mem_ctx; + WERROR werr; + const char *domain = lp_workgroup(); /* FIXME */ + bool modify_config = lp_config_backend_is_registry(); + enum netr_SchannelType sec_chan_type; + char *pw = NULL; + + if (c->display_usage) { + d_printf("Usage:\n" + "net rpc oldjoin\n" + " Join a domain the old way\n"); + return 0; + } + + net_warn_member_options(); + + mem_ctx = talloc_init("net_rpc_oldjoin"); + if (!mem_ctx) { + return -1; + } + + werr = libnet_init_JoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* + check what type of join - if the user wants to join as + a BDC, the server must agree that we are a BDC. + */ + if (argc >= 0) { + sec_chan_type = get_sec_channel_type(argv[0]); + } else { + sec_chan_type = get_sec_channel_type(NULL); + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + werr = WERR_ACCESS_DENIED; + goto fail; + } + + pw = talloc_strndup(r, lp_netbios_name(), 14); + if (pw == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + + r->in.msg_ctx = c->msg_ctx; + r->in.domain_name = domain; + r->in.secure_channel_type = sec_chan_type; + r->in.dc_name = c->opt_host; + r->in.admin_account = ""; + r->in.admin_password = strlower_talloc(r, pw); + if (r->in.admin_password == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + r->in.debug = true; + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_JOIN_UNSECURE | + WKSSVC_JOIN_FLAGS_MACHINE_PWD_PASSED; + + werr = libnet_Join(mem_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* Check the short name of the domain */ + + if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { + d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE()); + d_printf("domain name obtained from the server.\n"); + d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name); + d_printf("You should set \"workgroup = %s\" in %s.\n", + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name); + + if (r->out.dns_domain_name) { + d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name, + r->out.dns_domain_name); + } else { + d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name, + r->out.netbios_domain_name); + } + + /* print out informative error string in case there is one */ + if (r->out.error_string != NULL) { + d_printf("%s\n", r->out.error_string); + } + + TALLOC_FREE(mem_ctx); + + return 0; + +fail: + if (c->opt_flags & NET_FLAGS_EXPECT_FALLBACK) { + goto cleanup; + } + + /* issue an overall failure message at the end. */ + d_fprintf(stderr, _("Failed to join domain: %s\n"), + r && r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + +cleanup: + TALLOC_FREE(mem_ctx); + + return -1; +} + +/** + * check that a join is OK + * + * @return A shell status integer (0 for success) + * + **/ +int net_rpc_testjoin(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + TALLOC_CTX *mem_ctx; + const char *domain = c->opt_target_workgroup; + const char *dc = c->opt_host; + + if (c->display_usage) { + d_printf("Usage\n" + "net rpc testjoin\n" + " Test if a join is OK\n"); + return 0; + } + + net_warn_member_options(); + + mem_ctx = talloc_init("net_rpc_testjoin"); + if (!mem_ctx) { + return -1; + } + + if (!dc) { + struct netr_DsRGetDCNameInfo *info; + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + talloc_destroy(mem_ctx); + return -1; + } + + status = dsgetdcname(mem_ctx, + c->msg_ctx, + domain, + NULL, + NULL, + DS_RETURN_DNS_NAME, + &info); + if (!NT_STATUS_IS_OK(status)) { + talloc_destroy(mem_ctx); + return -1; + } + + dc = strip_hostname(info->dc_unc); + } + + /* Display success or failure */ + status = libnet_join_ok(c->msg_ctx, + c->opt_workgroup, + dc, + c->opt_kerberos); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr,"Join to domain '%s' is not valid: %s\n", + domain, nt_errstr(status)); + talloc_destroy(mem_ctx); + return -1; + } + + printf("Join to '%s' is OK\n",domain); + talloc_destroy(mem_ctx); + + return 0; +} + +/** + * Join a domain using the administrator username and password + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped. Currently not used. + * @return A shell status integer (0 for success) + * + **/ + +static int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv) +{ + struct libnet_JoinCtx *r = NULL; + TALLOC_CTX *mem_ctx; + WERROR werr; + const char *domain = lp_workgroup(); /* FIXME */ + bool modify_config = lp_config_backend_is_registry(); + enum netr_SchannelType sec_chan_type; + + if (c->display_usage) { + d_printf("Usage:\n" + "net rpc join\n" + " Join a domain the new way\n"); + return 0; + } + + net_warn_member_options(); + + mem_ctx = talloc_init("net_rpc_join_newstyle"); + if (!mem_ctx) { + return -1; + } + + werr = libnet_init_JoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* + check what type of join - if the user wants to join as + a BDC, the server must agree that we are a BDC. + */ + if (argc >= 0) { + sec_chan_type = get_sec_channel_type(argv[0]); + } else { + sec_chan_type = get_sec_channel_type(NULL); + } + + if (!c->msg_ctx) { + d_fprintf(stderr, _("Could not initialise message context. " + "Try running as root\n")); + werr = WERR_ACCESS_DENIED; + goto fail; + } + + r->in.msg_ctx = c->msg_ctx; + r->in.domain_name = domain; + r->in.secure_channel_type = sec_chan_type; + r->in.dc_name = c->opt_host; + r->in.admin_account = c->opt_user_name; + r->in.admin_password = net_prompt_pass(c, c->opt_user_name); + r->in.debug = true; + r->in.use_kerberos = c->opt_kerberos; + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + + werr = libnet_Join(mem_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + goto fail; + } + + /* Check the short name of the domain */ + + if (!modify_config && !strequal(lp_workgroup(), r->out.netbios_domain_name)) { + d_printf("The workgroup in %s does not match the short\n", get_dyn_CONFIGFILE()); + d_printf("domain name obtained from the server.\n"); + d_printf("Using the name [%s] from the server.\n", r->out.netbios_domain_name); + d_printf("You should set \"workgroup = %s\" in %s.\n", + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + d_printf("Using short domain name -- %s\n", r->out.netbios_domain_name); + + if (r->out.dns_domain_name) { + d_printf("Joined '%s' to realm '%s'\n", r->in.machine_name, + r->out.dns_domain_name); + } else { + d_printf("Joined '%s' to domain '%s'\n", r->in.machine_name, + r->out.netbios_domain_name); + } + + /* print out informative error string in case there is one */ + if (r->out.error_string != NULL) { + d_printf("%s\n", r->out.error_string); + } + + TALLOC_FREE(mem_ctx); + + return 0; + +fail: + /* issue an overall failure message at the end. */ + d_printf("Failed to join domain: %s\n", + r && r->out.error_string ? r->out.error_string : + get_friendly_werror_msg(werr)); + + TALLOC_FREE(mem_ctx); + + return -1; +} + +/** + * 'net rpc join' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * Main 'net_rpc_join()' (where the admin username/password is used) is + * in net_rpc_join.c. + * Try to just change the password, but if that doesn't work, use/prompt + * for a username/password. + **/ + +int net_rpc_join(struct net_context *c, int argc, const char **argv) +{ + int ret; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc join -U <username>[%%password] <type>\n" + " Join a domain\n" + " username\tName of the admin user" + " password\tPassword of the admin user, will " + "prompt if not specified\n" + " type\tCan be one of the following:\n" + "\t\tMEMBER\tJoin as member server (default)\n" + "\t\tBDC\tJoin as BDC\n" + "\t\tPDC\tJoin as PDC\n")); + return 0; + } + + if (lp_server_role() == ROLE_STANDALONE) { + d_printf(_("cannot join as standalone machine\n")); + return -1; + } + + net_warn_member_options(); + + if (strlen(lp_netbios_name()) > 15) { + d_printf(_("Our netbios name can be at most 15 chars long, " + "\"%s\" is %u chars long\n"), + lp_netbios_name(), (unsigned int)strlen(lp_netbios_name())); + return -1; + } + + c->opt_flags |= NET_FLAGS_EXPECT_FALLBACK; + ret = net_rpc_oldjoin(c, argc, argv); + c->opt_flags &= ~NET_FLAGS_EXPECT_FALLBACK; + if (ret == 0) { + return 0; + } + + return net_rpc_join_newstyle(c, argc, argv); +} + +/** + * display info about a rpc domain + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_info_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + union samr_DomainInfo *info = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* Get sam policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not connect to SAM: %s\n"), + nt_errstr(status)); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Could not connect to SAM: %s\n"), + nt_errstr(result)); + goto done; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not open domain: %s\n"), + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Could not open domain: %s\n"), + nt_errstr(result)); + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + &domain_pol, + 2, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + status = result; + if (NT_STATUS_IS_OK(result)) { + struct dom_sid_buf sid_str; + + d_printf(_("Domain Name: %s\n"), + info->general.domain_name.string); + d_printf(_("Domain SID: %s\n"), + dom_sid_str_buf(domain_sid, &sid_str)); + d_printf(_("Sequence number: %llu\n"), + (unsigned long long)info->general.sequence_num); + d_printf(_("Num users: %u\n"), info->general.num_users); + d_printf(_("Num domain groups: %u\n"),info->general.num_groups); + d_printf(_("Num local groups: %u\n"),info->general.num_aliases); + } + + done: + return status; +} + +/** + * 'net rpc info' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_info(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc info\n" + " %s\n", + _("Usage:"), + _("Display information about the domain")); + return 0; + } + + net_warn_member_options(); + + return run_rpc_command(c, NULL, &ndr_table_samr, + NET_FLAGS_PDC, rpc_info_internals, + argc, argv); +} + +/** + * Fetch domain SID into the local secrets.tdb. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_getsid_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dom_sid_buf sid_str; + + d_printf(_("Storing SID %s for Domain %s in secrets.tdb\n"), + dom_sid_str_buf(domain_sid, &sid_str), + domain_name); + + if (!secrets_store_domain_sid(domain_name, domain_sid)) { + DEBUG(0,("Can't store domain SID\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +/** + * 'net rpc getsid' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_getsid(struct net_context *c, int argc, const char **argv) +{ + int conn_flags = NET_FLAGS_PDC; + + if (!c->opt_user_specified && !c->opt_kerberos) { + conn_flags |= NET_FLAGS_ANONYMOUS; + } + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc getsid\n" + " %s\n", + _("Usage:"), + _("Fetch domain SID into local secrets.tdb")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_samr, + conn_flags, + rpc_getsid_internals, + argc, argv); +} + +/****************************************************************************/ + +/** + * Basic usage function for 'net rpc user'. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +static int rpc_user_usage(struct net_context *c, int argc, const char **argv) +{ + return net_user_usage(c, argc, argv); +} + +/** + * Add a new user to a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_user_add(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct USER_INFO_1 info1; + uint32_t parm_error = 0; + + if (argc < 1 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + ZERO_STRUCT(info1); + + info1.usri1_name = argv[0]; + if (argc == 2) { + info1.usri1_password = argv[1]; + } + + status = NetUserAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); + + if (status != 0) { + d_fprintf(stderr,_("Failed to add user '%s' with error: %s.\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } else { + d_printf(_("Added user '%s'.\n"), argv[0]); + } + + return 0; +} + +/** + * Rename a user on a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_user_rename(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct USER_INFO_0 u0; + uint32_t parm_err = 0; + + if (argc != 2 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + u0.usri0_name = argv[1]; + + status = NetUserSetInfo(c->opt_host, argv[0], + 0, (uint8_t *)&u0, &parm_err); + if (status) { + d_fprintf(stderr, + _("Failed to rename user from %s to %s - %s\n"), + argv[0], argv[1], + libnetapi_get_error_string(c->netapi_ctx, status)); + } else { + d_printf(_("Renamed user from %s to %s\n"), argv[0], argv[1]); + } + + return status; +} + +/** + * Set a user's primary group + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_user_setprimarygroup(struct net_context *c, int argc, + const char **argv) +{ + NET_API_STATUS status; + uint8_t *buffer; + struct GROUP_INFO_2 *g2; + struct USER_INFO_1051 u1051; + uint32_t parm_err = 0; + + if (argc != 2 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + status = NetGroupGetInfo(c->opt_host, argv[1], 2, &buffer); + if (status) { + d_fprintf(stderr, _("Failed to find group name %s -- %s\n"), + argv[1], + libnetapi_get_error_string(c->netapi_ctx, status)); + return status; + } + g2 = (struct GROUP_INFO_2 *)buffer; + + u1051.usri1051_primary_group_id = g2->grpi2_group_id; + + NetApiBufferFree(buffer); + + status = NetUserSetInfo(c->opt_host, argv[0], 1051, + (uint8_t *)&u1051, &parm_err); + if (status) { + d_fprintf(stderr, + _("Failed to set user's primary group %s to %s - " + "%s\n"), argv[0], argv[1], + libnetapi_get_error_string(c->netapi_ctx, status)); + } else { + d_printf(_("Set primary group of user %s to %s\n"), argv[0], + argv[1]); + } + return status; +} + +/** + * Delete a user from a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_user_delete(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + if (argc < 1 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + status = NetUserDel(c->opt_host, argv[0]); + + if (status != 0) { + d_fprintf(stderr, _("Failed to delete user '%s' with: %s.\n"), + argv[0], + libnetapi_get_error_string(c->netapi_ctx, status)); + return -1; + } else { + d_printf(_("Deleted user '%s'.\n"), argv[0]); + } + + return 0; +} + +/** + * Set a user's password on a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_user_password(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + char *prompt = NULL; + struct USER_INFO_1003 u1003; + uint32_t parm_err = 0; + int ret; + + if (argc < 1 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + if (argv[1]) { + u1003.usri1003_password = argv[1]; + } else { + char pwd[256] = {0}; + ret = asprintf(&prompt, _("Enter new password for %s:"), + argv[0]); + if (ret == -1) { + return -1; + } + + ret = samba_getpass(prompt, pwd, sizeof(pwd), false, false); + SAFE_FREE(prompt); + if (ret < 0) { + return -1; + } + + u1003.usri1003_password = talloc_strdup(c, pwd); + if (u1003.usri1003_password == NULL) { + return -1; + } + } + + status = NetUserSetInfo(c->opt_host, argv[0], 1003, (uint8_t *)&u1003, &parm_err); + + /* Display results */ + if (status != 0) { + d_fprintf(stderr, + _("Failed to set password for '%s' with error: %s.\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } + + return 0; +} + +/** + * List a user's groups from a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_user_info(struct net_context *c, int argc, const char **argv) + +{ + NET_API_STATUS status; + struct GROUP_USERS_INFO_0 *u0 = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t i; + + + if (argc < 1 || c->display_usage) { + rpc_user_usage(c, argc, argv); + return 0; + } + + status = NetUserGetGroups(c->opt_host, + argv[0], + 0, + (uint8_t **)(void *)&u0, + (uint32_t)-1, + &entries_read, + &total_entries); + if (status != 0) { + d_fprintf(stderr, + _("Failed to get groups for '%s' with error: %s.\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } + + for (i=0; i < entries_read; i++) { + printf("%s\n", u0->grui0_name); + u0++; + } + + return 0; +} + +/** + * List users on a remote RPC server. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static int rpc_user_list(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + uint32_t start_idx=0, num_entries, i, loop_count = 0; + struct NET_DISPLAY_USER *info = NULL; + void *buffer = NULL; + + /* Query domain users */ + if (c->opt_long_list_entries) + d_printf(_("\nUser name Comment" + "\n-----------------------------\n")); + do { + uint32_t max_entries, max_size; + + dcerpc_get_query_dispinfo_params( + loop_count, &max_entries, &max_size); + + status = NetQueryDisplayInformation(c->opt_host, + 1, + start_idx, + max_entries, + max_size, + &num_entries, + &buffer); + if (status != 0 && status != ERROR_MORE_DATA) { + return status; + } + + info = (struct NET_DISPLAY_USER *)buffer; + + for (i = 0; i < num_entries; i++) { + + if (c->opt_long_list_entries) + printf("%-21.21s %s\n", info->usri1_name, + info->usri1_comment); + else + printf("%s\n", info->usri1_name); + info++; + } + + NetApiBufferFree(buffer); + + loop_count++; + start_idx += num_entries; + + } while (status == ERROR_MORE_DATA); + + return status; +} + +/** + * 'net rpc user' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_user(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "add", + rpc_user_add, + NET_TRANSPORT_RPC, + N_("Add specified user"), + N_("net rpc user add\n" + " Add specified user") + }, + { + "info", + rpc_user_info, + NET_TRANSPORT_RPC, + N_("List domain groups of user"), + N_("net rpc user info\n" + " List domain groups of user") + }, + { + "delete", + rpc_user_delete, + NET_TRANSPORT_RPC, + N_("Remove specified user"), + N_("net rpc user delete\n" + " Remove specified user") + }, + { + "password", + rpc_user_password, + NET_TRANSPORT_RPC, + N_("Change user password"), + N_("net rpc user password\n" + " Change user password") + }, + { + "rename", + rpc_user_rename, + NET_TRANSPORT_RPC, + N_("Rename specified user"), + N_("net rpc user rename\n" + " Rename specified user") + }, + { + "setprimarygroup", + rpc_user_setprimarygroup, + NET_TRANSPORT_RPC, + "Set a user's primary group", + "net rpc user setprimarygroup\n" + " Set a user's primary group" + }, + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + if (argc == 0) { + if (c->display_usage) { + d_printf( "%s\n" + "net rpc user\n" + " %s\n", + _("Usage:"), + _("List all users")); + net_display_usage_from_functable(func); + return 0; + } + + return rpc_user_list(c, argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc user", func); +} + +static NTSTATUS rpc_sh_user_list(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return werror_to_ntstatus(W_ERROR(rpc_user_list(c, argc, argv))); +} + +static NTSTATUS rpc_sh_user_info(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return werror_to_ntstatus(W_ERROR(rpc_user_info(c, argc, argv))); +} + +static NTSTATUS rpc_sh_handle_user(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv, + NTSTATUS (*fn)( + struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *user_hnd, + int argc, const char **argv)) +{ + struct policy_handle connect_pol, domain_pol, user_pol; + NTSTATUS status, result; + struct dom_sid sid; + uint32_t rid; + enum lsa_SidType type; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc == 0) { + d_fprintf(stderr, "%s %s <username>\n", _("Usage:"), + ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(connect_pol); + ZERO_STRUCT(domain_pol); + ZERO_STRUCT(user_pol); + + status = net_rpc_lookup_name(c, mem_ctx, ctx->cli, + argv[0], NULL, NULL, &sid, &type); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not lookup %s: %s\n"), argv[0], + nt_errstr(status)); + goto done; + } + + if (type != SID_NAME_USER) { + d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0], + sid_type_lookup(type)); + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + if (!sid_peek_check_rid(ctx->domain_sid, &sid, &rid)) { + d_fprintf(stderr, _("%s is not in our domain\n"), argv[0]); + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + ctx->domain_sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_OpenUser(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rid, + &user_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = fn(c, mem_ctx, ctx, pipe_hnd, &user_pol, argc-1, argv+1); + + done: + if (is_valid_policy_hnd(&user_pol)) { + dcerpc_samr_Close(b, mem_ctx, &user_pol, &result); + } + if (is_valid_policy_hnd(&domain_pol)) { + dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); + } + if (is_valid_policy_hnd(&connect_pol)) { + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + } + return status; +} + +static NTSTATUS rpc_sh_user_show_internals(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *user_hnd, + int argc, const char **argv) +{ + NTSTATUS status, result; + union samr_UserInfo *info = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 0) { + d_fprintf(stderr, "%s %s show <username>\n", _("Usage:"), + ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_samr_QueryUserInfo(b, mem_ctx, + user_hnd, + 21, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + d_printf(_("user rid: %d, group rid: %d\n"), + info->info21.rid, + info->info21.primary_gid); + + return result; +} + +static NTSTATUS rpc_sh_user_show(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, + rpc_sh_user_show_internals); +} + +#define FETCHSTR(name, rec) \ +do { if (strequal(ctx->thiscmd, name)) { \ + oldval = talloc_strdup(mem_ctx, info->info21.rec.string); } \ +} while (0); + +#define SETSTR(name, rec, flag) \ +do { if (strequal(ctx->thiscmd, name)) { \ + init_lsa_String(&(info->info21.rec), argv[0]); \ + info->info21.fields_present |= SAMR_FIELD_##flag; } \ +} while (0); + +static NTSTATUS rpc_sh_user_str_edit_internals(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *user_hnd, + int argc, const char **argv) +{ + NTSTATUS status, result; + const char *username; + const char *oldval = ""; + union samr_UserInfo *info = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc > 1) { + d_fprintf(stderr, "%s %s <username> [new value|NULL]\n", + _("Usage:"), ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_samr_QueryUserInfo(b, mem_ctx, + user_hnd, + 21, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + username = talloc_strdup(mem_ctx, info->info21.account_name.string); + + FETCHSTR("fullname", full_name); + FETCHSTR("homedir", home_directory); + FETCHSTR("homedrive", home_drive); + FETCHSTR("logonscript", logon_script); + FETCHSTR("profilepath", profile_path); + FETCHSTR("description", description); + + if (argc == 0) { + d_printf(_("%s's %s: [%s]\n"), username, ctx->thiscmd, oldval); + goto done; + } + + if (strcmp(argv[0], "NULL") == 0) { + argv[0] = ""; + } + + ZERO_STRUCT(info->info21); + + SETSTR("fullname", full_name, FULL_NAME); + SETSTR("homedir", home_directory, HOME_DIRECTORY); + SETSTR("homedrive", home_drive, HOME_DRIVE); + SETSTR("logonscript", logon_script, LOGON_SCRIPT); + SETSTR("profilepath", profile_path, PROFILE_PATH); + SETSTR("description", description, DESCRIPTION); + + status = dcerpc_samr_SetUserInfo(b, mem_ctx, + user_hnd, + 21, + info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = result; + + d_printf(_("Set %s's %s from [%s] to [%s]\n"), username, + ctx->thiscmd, oldval, argv[0]); + + done: + + return status; +} + +#define HANDLEFLG(name, rec) \ +do { if (strequal(ctx->thiscmd, name)) { \ + oldval = (oldflags & ACB_##rec) ? "yes" : "no"; \ + if (newval) { \ + newflags = oldflags | ACB_##rec; \ + } else { \ + newflags = oldflags & ~ACB_##rec; \ + } } } while (0); + +static NTSTATUS rpc_sh_user_str_edit(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, + rpc_sh_user_str_edit_internals); +} + +static NTSTATUS rpc_sh_user_flag_edit_internals(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *user_hnd, + int argc, const char **argv) +{ + NTSTATUS status, result; + const char *username; + const char *oldval = "unknown"; + uint32_t oldflags, newflags; + bool newval; + union samr_UserInfo *info = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if ((argc > 1) || + ((argc == 1) && !strequal(argv[0], "yes") && + !strequal(argv[0], "no"))) { + /* TRANSLATORS: The yes|no here are program keywords. Please do + not translate. */ + d_fprintf(stderr, _("Usage: %s <username> [yes|no]\n"), + ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + newval = strequal(argv[0], "yes"); + + status = dcerpc_samr_QueryUserInfo(b, mem_ctx, + user_hnd, + 21, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + username = talloc_strdup(mem_ctx, info->info21.account_name.string); + oldflags = info->info21.acct_flags; + newflags = info->info21.acct_flags; + + HANDLEFLG("disabled", DISABLED); + HANDLEFLG("pwnotreq", PWNOTREQ); + HANDLEFLG("autolock", AUTOLOCK); + HANDLEFLG("pwnoexp", PWNOEXP); + + if (argc == 0) { + d_printf(_("%s's %s flag: %s\n"), username, ctx->thiscmd, + oldval); + goto done; + } + + ZERO_STRUCT(info->info21); + + info->info21.acct_flags = newflags; + info->info21.fields_present = SAMR_FIELD_ACCT_FLAGS; + + status = dcerpc_samr_SetUserInfo(b, mem_ctx, + user_hnd, + 21, + info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + status = result; + if (NT_STATUS_IS_OK(result)) { + d_printf(_("Set %s's %s flag from [%s] to [%s]\n"), username, + ctx->thiscmd, oldval, argv[0]); + } + + done: + + return status; +} + +static NTSTATUS rpc_sh_user_flag_edit(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_handle_user(c, mem_ctx, ctx, pipe_hnd, argc, argv, + rpc_sh_user_flag_edit_internals); +} + +struct rpc_sh_cmd *net_rpc_user_edit_cmds(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx) +{ + static struct rpc_sh_cmd cmds[] = { + + { "fullname", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's full name") }, + + { "homedir", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's home directory") }, + + { "homedrive", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's home drive") }, + + { "logonscript", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's logon script") }, + + { "profilepath", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's profile path") }, + + { "description", NULL, &ndr_table_samr, rpc_sh_user_str_edit, + N_("Show/Set a user's description") }, + + { "disabled", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, + N_("Show/Set whether a user is disabled") }, + + { "autolock", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, + N_("Show/Set whether a user locked out") }, + + { "pwnotreq", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, + N_("Show/Set whether a user does not need a password") }, + + { "pwnoexp", NULL, &ndr_table_samr, rpc_sh_user_flag_edit, + N_("Show/Set whether a user's password does not expire") }, + + { NULL, NULL, 0, NULL, NULL } + }; + + return cmds; +} + +struct rpc_sh_cmd *net_rpc_user_cmds(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx) +{ + static struct rpc_sh_cmd cmds[] = { + + { "list", NULL, &ndr_table_samr, rpc_sh_user_list, + N_("List available users") }, + + { "info", NULL, &ndr_table_samr, rpc_sh_user_info, + N_("List the domain groups a user is member of") }, + + { "show", NULL, &ndr_table_samr, rpc_sh_user_show, + N_("Show info about a user") }, + + { "edit", net_rpc_user_edit_cmds, 0, NULL, + N_("Show/Modify a user's fields") }, + + { NULL, NULL, 0, NULL, NULL } + }; + + return cmds; +} + +/****************************************************************************/ + +/** + * Basic usage function for 'net rpc group'. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +static int rpc_group_usage(struct net_context *c, int argc, const char **argv) +{ + return net_group_usage(c, argc, argv); +} + +/** + * Delete group on a remote RPC server. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_group_delete_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle connect_pol, domain_pol, group_pol, user_pol; + bool group_is_primary = false; + NTSTATUS status, result; + uint32_t group_rid; + struct samr_RidAttrArray *rids = NULL; + /* char **names; */ + uint32_t i; + /* struct samr_RidWithAttribute *user_gids; */ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct samr_Ids group_rids, name_types; + struct lsa_String lsa_acct_name; + union samr_UserInfo *info = NULL; + + if (argc < 1 || c->display_usage) { + rpc_group_usage(c, argc,argv); + return NT_STATUS_OK; /* ok? */ + } + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Request samr_Connect2 failed\n")); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Request samr_Connect2 failed\n")); + goto done; + } + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Request open_domain failed\n")); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Request open_domain failed\n")); + goto done; + } + + init_lsa_String(&lsa_acct_name, argv[0]); + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &group_rids, + &name_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Lookup of '%s' failed\n"),argv[0]); + goto done; + } + if (group_rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + + switch (name_types.ids[0]) + { + case SID_NAME_DOM_GRP: + status = dcerpc_samr_OpenGroup(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rids.ids[0], + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Request open_group failed")); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Request open_group failed")); + goto done; + } + + group_rid = group_rids.ids[0]; + + status = dcerpc_samr_QueryGroupMember(b, mem_ctx, + &group_pol, + &rids, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to query group members of %s"), + argv[0]); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, + _("Unable to query group members of %s"), + argv[0]); + goto done; + } + + if (c->opt_verbose) { + d_printf( + _("Domain Group %s (rid: %d) has %d members\n"), + argv[0],group_rid, rids->count); + } + + /* Check if group is anyone's primary group */ + for (i = 0; i < rids->count; i++) + { + status = dcerpc_samr_OpenUser(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rids->rids[i], + &user_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to open group member %d\n"), + rids->rids[i]); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, + _("Unable to open group member %d\n"), + rids->rids[i]); + goto done; + } + + status = dcerpc_samr_QueryUserInfo(b, mem_ctx, + &user_pol, + 21, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Unable to lookup userinfo for group " + "member %d\n"), + rids->rids[i]); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, + _("Unable to lookup userinfo for group " + "member %d\n"), + rids->rids[i]); + goto done; + } + + if (info->info21.primary_gid == group_rid) { + if (c->opt_verbose) { + d_printf(_("Group is primary group " + "of %s\n"), + info->info21.account_name.string); + } + group_is_primary = true; + } + + dcerpc_samr_Close(b, mem_ctx, &user_pol, &result); + } + + if (group_is_primary) { + d_fprintf(stderr, _("Unable to delete group because " + "some of it's members have it as primary " + "group\n")); + status = NT_STATUS_MEMBERS_PRIMARY_GROUP; + goto done; + } + + /* remove all group members */ + for (i = 0; i < rids->count; i++) + { + if (c->opt_verbose) + d_printf(_("Remove group member %d..."), + rids->rids[i]); + status = dcerpc_samr_DeleteGroupMember(b, mem_ctx, + &group_pol, + rids->rids[i], + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + status = result; + if (NT_STATUS_IS_OK(result)) { + if (c->opt_verbose) + d_printf(_("ok\n")); + } else { + if (c->opt_verbose) + d_printf("%s\n", _("failed")); + goto done; + } + } + + status = dcerpc_samr_DeleteDomainGroup(b, mem_ctx, + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + break; + } + + status = result; + + break; + /* removing a local group is easier... */ + case SID_NAME_ALIAS: + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rids.ids[0], + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Request open_alias failed\n")); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Request open_alias failed\n")); + goto done; + } + + status = dcerpc_samr_DeleteDomAlias(b, mem_ctx, + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + break; + } + + status = result; + + break; + default: + d_fprintf(stderr, _("%s is of type %s. This command is only " + "for deleting local or global groups\n"), + argv[0],sid_type_lookup(name_types.ids[0])); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (NT_STATUS_IS_OK(status)) { + if (c->opt_verbose) + d_printf(_("Deleted %s '%s'\n"), + sid_type_lookup(name_types.ids[0]), argv[0]); + } else { + d_fprintf(stderr, _("Deleting of %s failed: %s\n"), argv[0], + get_friendly_nt_error_msg(status)); + } + + done: + return status; + +} + +static int rpc_group_delete(struct net_context *c, int argc, const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_delete_internals, argc,argv); +} + +static int rpc_group_add_internals(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct GROUP_INFO_1 info1; + uint32_t parm_error = 0; + + if (argc != 1 || c->display_usage) { + rpc_group_usage(c, argc, argv); + return 0; + } + + ZERO_STRUCT(info1); + + info1.grpi1_name = argv[0]; + if (c->opt_comment && strlen(c->opt_comment) > 0) { + info1.grpi1_comment = c->opt_comment; + } + + status = NetGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); + + if (status != 0) { + d_fprintf(stderr, + _("Failed to add group '%s' with error: %s.\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } else { + d_printf(_("Added group '%s'.\n"), argv[0]); + } + + return 0; +} + +static int rpc_alias_add_internals(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct LOCALGROUP_INFO_1 info1; + uint32_t parm_error = 0; + + if (argc != 1 || c->display_usage) { + rpc_group_usage(c, argc, argv); + return 0; + } + + ZERO_STRUCT(info1); + + info1.lgrpi1_name = argv[0]; + if (c->opt_comment && strlen(c->opt_comment) > 0) { + info1.lgrpi1_comment = c->opt_comment; + } + + status = NetLocalGroupAdd(c->opt_host, 1, (uint8_t *)&info1, &parm_error); + + if (status != 0) { + d_fprintf(stderr, + _("Failed to add alias '%s' with error: %s.\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } else { + d_printf(_("Added alias '%s'.\n"), argv[0]); + } + + return 0; +} + +static int rpc_group_add(struct net_context *c, int argc, const char **argv) +{ + if (c->opt_localgroup) + return rpc_alias_add_internals(c, argc, argv); + + return rpc_group_add_internals(c, argc, argv); +} + +static NTSTATUS get_sid_from_name(struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *name, + struct dom_sid *sid, + enum lsa_SidType *type) +{ + struct dom_sid *sids = NULL; + enum lsa_SidType *types = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + struct policy_handle lsa_pol; + NTSTATUS status, result; + struct dcerpc_binding_handle *b; + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + b = pipe_hnd->binding_handle; + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, false, + SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &lsa_pol, 1, + &name, NULL, 1, &sids, &types); + + if (NT_STATUS_IS_OK(status)) { + sid_copy(sid, &sids[0]); + *type = types[0]; + } + + dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result); + + done: + if (pipe_hnd) { + TALLOC_FREE(pipe_hnd); + } + + if (!NT_STATUS_IS_OK(status) && (strncasecmp_m(name, "S-", 2) == 0)) { + + /* Try as S-1-5-whatever */ + + struct dom_sid tmp_sid; + + if (string_to_sid(&tmp_sid, name)) { + sid_copy(sid, &tmp_sid); + *type = SID_NAME_UNKNOWN; + status = NT_STATUS_OK; + } + } + + return status; +} + +static NTSTATUS rpc_add_groupmem(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + const struct dom_sid *group_sid, + const char *member) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + uint32_t group_rid; + struct policy_handle group_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct samr_Ids rids, rid_types; + struct lsa_String lsa_acct_name; + + struct dom_sid sid; + + sid_copy(&sid, group_sid); + + if (!sid_split_rid(&sid, &group_rid)) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* Get sam policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + init_lsa_String(&lsa_acct_name, member); + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &rids, + &rid_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + goto done; + } + if (rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (rid_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rid, + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_AddGroupMember(b, mem_ctx, + &group_pol, + rids.ids[0], + 0x0005, /* unknown flags */ + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = result; + + done: + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + return status; +} + +static NTSTATUS rpc_add_aliasmem(struct rpc_pipe_client *pipe_hnd, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const struct dom_sid *alias_sid, + const char *member) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + uint32_t alias_rid; + struct policy_handle alias_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct dom_sid member_sid; + enum lsa_SidType member_type; + + struct dom_sid sid; + + sid_copy(&sid, alias_sid); + + if (!sid_split_rid(&sid, &alias_rid)) { + return NT_STATUS_UNSUCCESSFUL; + } + + result = get_sid_from_name(cli, mem_ctx, + member, &member_sid, &member_type); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + return result; + } + + /* Get sam policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + alias_rid, + &alias_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + status = dcerpc_samr_AddAliasMember(b, mem_ctx, + &alias_pol, + &member_sid, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = result; + + done: + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + return status; +} + +static NTSTATUS rpc_group_addmem_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dom_sid group_sid; + enum lsa_SidType group_type; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc group addmem <group> <member>\n" + " Add a member to a group\n" + " group\tGroup to add member to\n" + " member\tMember to add to group\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], + &group_sid, &group_type))) { + d_fprintf(stderr, _("Could not lookup group name %s\n"), + argv[0]); + return NT_STATUS_UNSUCCESSFUL; + } + + if (group_type == SID_NAME_DOM_GRP) { + NTSTATUS result = rpc_add_groupmem(pipe_hnd, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not add %s to %s: %s\n"), + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + if (group_type == SID_NAME_ALIAS) { + NTSTATUS result = rpc_add_aliasmem(pipe_hnd, cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not add %s to %s: %s\n"), + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + d_fprintf(stderr, _("Can only add members to global or local groups " + "which %s is not\n"), argv[0]); + + return NT_STATUS_UNSUCCESSFUL; +} + +static int rpc_group_addmem(struct net_context *c, int argc, const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_addmem_internals, + argc, argv); +} + +static NTSTATUS rpc_del_groupmem(struct net_context *c, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + const struct dom_sid *group_sid, + const char *member) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + uint32_t group_rid; + struct policy_handle group_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct samr_Ids rids, rid_types; + struct lsa_String lsa_acct_name; + + struct dom_sid sid; + + sid_copy(&sid, group_sid); + + if (!sid_split_rid(&sid, &group_rid)) + return NT_STATUS_UNSUCCESSFUL; + + /* Get sam policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + init_lsa_String(&lsa_acct_name, member); + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &rids, + &rid_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + goto done; + } + if (rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (rid_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + + status = dcerpc_samr_OpenGroup(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rid, + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_DeleteGroupMember(b, mem_ctx, + &group_pol, + rids.ids[0], + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = result; + done: + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + return status; +} + +static NTSTATUS rpc_del_aliasmem(struct rpc_pipe_client *pipe_hnd, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const struct dom_sid *alias_sid, + const char *member) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + uint32_t alias_rid; + struct policy_handle alias_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct dom_sid member_sid; + enum lsa_SidType member_type; + + struct dom_sid sid; + + sid_copy(&sid, alias_sid); + + if (!sid_split_rid(&sid, &alias_rid)) + return NT_STATUS_UNSUCCESSFUL; + + result = get_sid_from_name(cli, mem_ctx, + member, &member_sid, &member_type); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not lookup up group member %s\n"), + member); + return result; + } + + /* Get sam policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + alias_rid, + &alias_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + status = dcerpc_samr_DeleteAliasMember(b, mem_ctx, + &alias_pol, + &member_sid, + &result); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = result; + + done: + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + return status; +} + +static NTSTATUS rpc_group_delmem_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dom_sid group_sid; + enum lsa_SidType group_type; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc group delmem <group> <member>\n" + " Delete a member from a group\n" + " group\tGroup to delete member from\n" + " member\tMember to delete from group\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], + &group_sid, &group_type))) { + d_fprintf(stderr, _("Could not lookup group name %s\n"), + argv[0]); + return NT_STATUS_UNSUCCESSFUL; + } + + if (group_type == SID_NAME_DOM_GRP) { + NTSTATUS result = rpc_del_groupmem(c, pipe_hnd, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not del %s from %s: %s\n"), + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + if (group_type == SID_NAME_ALIAS) { + NTSTATUS result = rpc_del_aliasmem(pipe_hnd, cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Could not del %s from %s: %s\n"), + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + d_fprintf(stderr, _("Can only delete members from global or local " + "groups which %s is not\n"), argv[0]); + + return NT_STATUS_UNSUCCESSFUL; +} + +static int rpc_group_delmem(struct net_context *c, int argc, const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_delmem_internals, + argc, argv); +} + +/** + * List groups on a remote RPC server. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_group_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + uint32_t start_idx=0, max_entries=250, num_entries, i, loop_count = 0; + struct samr_SamArray *groups = NULL; + bool global = false; + bool local = false; + bool builtin = false; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc group list [global] [local] [builtin]\n" + " List groups on RPC server\n" + " global\tList global groups\n" + " local\tList local groups\n" + " builtin\tList builtin groups\n" + " If none of global, local or builtin is " + "specified, all three options are considered " + "set\n")); + return NT_STATUS_OK; + } + + if (argc == 0) { + global = true; + local = true; + builtin = true; + } + + for (i=0; i<argc; i++) { + if (strequal(argv[i], "global")) + global = true; + + if (strequal(argv[i], "local")) + local = true; + + if (strequal(argv[i], "builtin")) + builtin = true; + } + + /* Get sam policy handle */ + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Query domain groups */ + if (c->opt_long_list_entries) + d_printf(_("\nGroup name Comment" + "\n-----------------------------\n")); + do { + uint32_t max_size, total_size, returned_size; + union samr_DispInfo info; + + if (!global) break; + + dcerpc_get_query_dispinfo_params( + loop_count, &max_entries, &max_size); + + status = dcerpc_samr_QueryDisplayInfo(b, mem_ctx, + &domain_pol, + 3, + start_idx, + max_entries, + max_size, + &total_size, + &returned_size, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + num_entries = info.info3.count; + start_idx += info.info3.count; + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + for (i = 0; i < num_entries; i++) { + + const char *group = NULL; + const char *desc = NULL; + + group = info.info3.entries[i].account_name.string; + desc = info.info3.entries[i].description.string; + + if (c->opt_long_list_entries) + printf("%-21.21s %-50.50s\n", + group, desc); + else + printf("%s\n", group); + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + /* query domain aliases */ + start_idx = 0; + do { + if (!local) break; + + status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, + &domain_pol, + &start_idx, + &groups, + 0xffff, + &num_entries, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + for (i = 0; i < num_entries; i++) { + + const char *description = NULL; + + if (c->opt_long_list_entries) { + + struct policy_handle alias_pol; + union samr_AliasInfo *info = NULL; + NTSTATUS _result; + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + 0x8, + groups->entries[i].idx, + &alias_pol, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + status = dcerpc_samr_QueryAliasInfo(b, mem_ctx, + &alias_pol, + 3, + &info, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + status = dcerpc_samr_Close(b, mem_ctx, + &alias_pol, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + description = info->description.string; + } + } + } + } + + if (description != NULL) { + printf("%-21.21s %-50.50s\n", + groups->entries[i].name.string, + description); + } else { + printf("%s\n", groups->entries[i].name.string); + } + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); + /* Get builtin policy handle */ + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, &global_sid_Builtin), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* query builtin aliases */ + start_idx = 0; + do { + if (!builtin) break; + + status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, + &domain_pol, + &start_idx, + &groups, + max_entries, + &num_entries, + &result); + if (!NT_STATUS_IS_OK(status)) { + break; + } + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { + status = result; + break; + } + + for (i = 0; i < num_entries; i++) { + + const char *description = NULL; + + if (c->opt_long_list_entries) { + + struct policy_handle alias_pol; + union samr_AliasInfo *info = NULL; + NTSTATUS _result; + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + 0x8, + groups->entries[i].idx, + &alias_pol, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + status = dcerpc_samr_QueryAliasInfo(b, mem_ctx, + &alias_pol, + 3, + &info, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + status = dcerpc_samr_Close(b, mem_ctx, + &alias_pol, + &_result); + if (NT_STATUS_IS_OK(status) && NT_STATUS_IS_OK(_result)) { + description = info->description.string; + } + } + } + } + + if (description != NULL) { + printf("%-21.21s %-50.50s\n", + groups->entries[i].name.string, + description); + } else { + printf("%s\n", groups->entries[i].name.string); + } + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + status = result; + + done: + return status; +} + +static int rpc_group_list(struct net_context *c, int argc, const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_list_internals, + argc, argv); +} + +static NTSTATUS rpc_list_group_members(struct net_context *c, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + const char *domain_name, + const struct dom_sid *domain_sid, + struct policy_handle *domain_pol, + uint32_t rid) +{ + NTSTATUS result, status; + struct policy_handle group_pol; + uint32_t num_members, *group_rids; + uint32_t i; + struct samr_RidAttrArray *rids = NULL; + struct lsa_Strings names; + struct samr_Ids types; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_samr_OpenGroup(b, mem_ctx, + domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rid, + &group_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + status = dcerpc_samr_QueryGroupMember(b, mem_ctx, + &group_pol, + &rids, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + num_members = rids->count; + group_rids = rids->rids; + + while (num_members > 0) { + uint32_t this_time = 512; + + if (num_members < this_time) + this_time = num_members; + + status = dcerpc_samr_LookupRids(b, mem_ctx, + domain_pol, + this_time, + group_rids, + &names, + &types, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + if (names.count != this_time) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (types.count != this_time) { + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + /* We only have users as members, but make the output + the same as the output of alias members */ + + for (i = 0; i < this_time; i++) { + + if (c->opt_long_list_entries) { + struct dom_sid sid; + struct dom_sid_buf sid_str; + + sid_compose(&sid, domain_sid, group_rids[i]); + + printf("%s %s\\%s %d\n", + dom_sid_str_buf(&sid, &sid_str), + domain_name, + names.names[i].string, + SID_NAME_USER); + } else { + printf("%s\\%s\n", domain_name, + names.names[i].string); + } + } + + num_members -= this_time; + group_rids += 512; + } + + return NT_STATUS_OK; +} + +static NTSTATUS rpc_list_alias_members(struct net_context *c, + struct rpc_pipe_client *pipe_hnd, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + struct policy_handle *domain_pol, + uint32_t rid) +{ + NTSTATUS result, status; + struct rpc_pipe_client *lsa_pipe; + struct policy_handle alias_pol, lsa_pol; + uint32_t num_members; + struct dom_sid *alias_sids; + char **domains; + char **names; + enum lsa_SidType *types; + uint32_t i; + struct lsa_SidArray sid_array; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rid, + &alias_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + status = dcerpc_samr_GetMembersInAlias(b, mem_ctx, + &alias_pol, + &sid_array, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Couldn't list alias members\n")); + return status; + } + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Couldn't list alias members\n")); + return result; + } + + num_members = sid_array.num_sids; + + if (num_members == 0) { + return NT_STATUS_OK; + } + + result = cli_rpc_pipe_open_noauth(cli, + &ndr_table_lsarpc, + &lsa_pipe); + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Couldn't open LSA pipe. Error was %s\n"), + nt_errstr(result) ); + return result; + } + + result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, &lsa_pol); + + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Couldn't open LSA policy handle\n")); + TALLOC_FREE(lsa_pipe); + return result; + } + + alias_sids = talloc_zero_array(mem_ctx, struct dom_sid, num_members); + if (!alias_sids) { + d_fprintf(stderr, _("Out of memory\n")); + TALLOC_FREE(lsa_pipe); + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<num_members; i++) { + sid_copy(&alias_sids[i], sid_array.sids[i].sid); + } + + result = rpccli_lsa_lookup_sids(lsa_pipe, mem_ctx, &lsa_pol, + num_members, alias_sids, + &domains, &names, &types); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { + d_fprintf(stderr, _("Couldn't lookup SIDs\n")); + TALLOC_FREE(lsa_pipe); + return result; + } + + for (i = 0; i < num_members; i++) { + struct dom_sid_buf sid_str; + dom_sid_str_buf(&alias_sids[i], &sid_str); + + if (c->opt_long_list_entries) { + printf("%s %s\\%s %d\n", sid_str.buf, + domains[i] ? domains[i] : _("*unknown*"), + names[i] ? names[i] : _("*unknown*"), types[i]); + } else { + if (domains[i]) + printf("%s\\%s\n", domains[i], names[i]); + else + printf("%s\n", sid_str.buf); + } + } + + TALLOC_FREE(lsa_pipe); + return NT_STATUS_OK; +} + +static NTSTATUS rpc_group_members_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS result, status; + struct policy_handle connect_pol, domain_pol; + struct samr_Ids rids, rid_types; + struct lsa_String lsa_acct_name; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* Get sam policy handle */ + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + /* Get domain policy handle */ + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + init_lsa_String(&lsa_acct_name, argv[0]); /* sure? */ + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &rids, + &rid_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (!NT_STATUS_IS_OK(result)) { + + /* Ok, did not find it in the global sam, try with builtin */ + + struct dom_sid sid_Builtin; + + dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); + + sid_copy(&sid_Builtin, &global_sid_Builtin); + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid_Builtin, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Couldn't find group %s\n"), + argv[0]); + return result; + } + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &rids, + &rid_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, _("Couldn't find group %s\n"), + argv[0]); + return result; + } + } + + if (rids.count != 1) { + d_fprintf(stderr, _("Couldn't find group %s\n"), + argv[0]); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + if (rid_types.count != 1) { + d_fprintf(stderr, _("Couldn't find group %s\n"), + argv[0]); + return NT_STATUS_INVALID_NETWORK_RESPONSE; + } + + + if (rid_types.ids[0] == SID_NAME_DOM_GRP) { + return rpc_list_group_members(c, pipe_hnd, mem_ctx, domain_name, + domain_sid, &domain_pol, + rids.ids[0]); + } + + if (rid_types.ids[0] == SID_NAME_ALIAS) { + return rpc_list_alias_members(c, pipe_hnd, cli, mem_ctx, &domain_pol, + rids.ids[0]); + } + + return NT_STATUS_NO_SUCH_GROUP; +} + +static int rpc_group_members(struct net_context *c, int argc, const char **argv) +{ + if (argc != 1 || c->display_usage) { + return rpc_group_usage(c, argc, argv); + } + + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_members_internals, + argc, argv); +} + +static int rpc_group_rename_internals(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct GROUP_INFO_0 g0; + uint32_t parm_err; + + if (argc != 2) { + d_printf(_("Usage:\n")); + d_printf("net rpc group rename group newname\n"); + return -1; + } + + g0.grpi0_name = argv[1]; + + status = NetGroupSetInfo(c->opt_host, + argv[0], + 0, + (uint8_t *)&g0, + &parm_err); + + if (status != 0) { + d_fprintf(stderr, _("Renaming group %s failed with: %s\n"), + argv[0], libnetapi_get_error_string(c->netapi_ctx, + status)); + return -1; + } + + return 0; +} + +static int rpc_group_rename(struct net_context *c, int argc, const char **argv) +{ + if (argc != 2 || c->display_usage) { + return rpc_group_usage(c, argc, argv); + } + + return rpc_group_rename_internals(c, argc, argv); +} + +/** + * 'net rpc group' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_group(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "add", + rpc_group_add, + NET_TRANSPORT_RPC, + N_("Create specified group"), + N_("net rpc group add\n" + " Create specified group") + }, + { + "delete", + rpc_group_delete, + NET_TRANSPORT_RPC, + N_("Delete specified group"), + N_("net rpc group delete\n" + " Delete specified group") + }, + { + "addmem", + rpc_group_addmem, + NET_TRANSPORT_RPC, + N_("Add member to group"), + N_("net rpc group addmem\n" + " Add member to group") + }, + { + "delmem", + rpc_group_delmem, + NET_TRANSPORT_RPC, + N_("Remove member from group"), + N_("net rpc group delmem\n" + " Remove member from group") + }, + { + "list", + rpc_group_list, + NET_TRANSPORT_RPC, + N_("List groups"), + N_("net rpc group list\n" + " List groups") + }, + { + "members", + rpc_group_members, + NET_TRANSPORT_RPC, + N_("List group members"), + N_("net rpc group members\n" + " List group members") + }, + { + "rename", + rpc_group_rename, + NET_TRANSPORT_RPC, + N_("Rename group"), + N_("net rpc group rename\n" + " Rename group") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + if (argc == 0) { + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rpc group\n" + " Alias for net rpc group list global " + "local builtin\n")); + net_display_usage_from_functable(func); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_group_list_internals, + argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc group", func); +} + +/****************************************************************************/ + +static int rpc_share_usage(struct net_context *c, int argc, const char **argv) +{ + return net_share_usage(c, argc, argv); +} + +/** + * Add a share on a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_share_add(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + char *sharename; + char *path; + uint32_t type = STYPE_DISKTREE; /* only allow disk shares to be added */ + uint32_t num_users=0, perms=0; + char *password=NULL; /* don't allow a share password */ + struct SHARE_INFO_2 i2; + uint32_t parm_error = 0; + + if ((argc < 1) || !strchr(argv[0], '=') || c->display_usage) { + return rpc_share_usage(c, argc, argv); + } + + if ((sharename = talloc_strdup(c, argv[0])) == NULL) { + return -1; + } + + path = strchr(sharename, '='); + if (!path) { + return -1; + } + + *path++ = '\0'; + + i2.shi2_netname = sharename; + i2.shi2_type = type; + i2.shi2_remark = c->opt_comment; + i2.shi2_permissions = perms; + i2.shi2_max_uses = c->opt_maxusers; + i2.shi2_current_uses = num_users; + i2.shi2_path = path; + i2.shi2_passwd = password; + + status = NetShareAdd(c->opt_host, + 2, + (uint8_t *)&i2, + &parm_error); + if (status != 0) { + printf(_("NetShareAdd failed with: %s\n"), + libnetapi_get_error_string(c->netapi_ctx, status)); + } + + return status; +} + +/** + * Delete a share on a remote RPC server. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_share_delete(struct net_context *c, int argc, const char **argv) +{ + if (argc < 1 || c->display_usage) { + return rpc_share_usage(c, argc, argv); + } + + return NetShareDel(c->opt_host, argv[0], 0); +} + +/** + * Formatted print of share info + * + * @param r pointer to SHARE_INFO_1 to format + **/ + +static void display_share_info_1(struct net_context *c, + struct SHARE_INFO_1 *r) +{ + if (c->opt_long_list_entries) { + d_printf("%-12s %-8.8s %-50s\n", + r->shi1_netname, + net_share_type_str(r->shi1_type & ~(STYPE_TEMPORARY|STYPE_HIDDEN)), + r->shi1_remark); + } else { + d_printf("%s\n", r->shi1_netname); + } +} + +static WERROR get_share_info(struct net_context *c, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + uint32_t level, + int argc, + const char **argv, + struct srvsvc_NetShareInfoCtr *info_ctr) +{ + WERROR result; + NTSTATUS status; + union srvsvc_NetShareInfo info; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* no specific share requested, enumerate all */ + if (argc == 0) { + + uint32_t preferred_len = 0xffffffff; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + + info_ctr->level = level; + + status = dcerpc_srvsvc_NetShareEnumAll(b, mem_ctx, + pipe_hnd->desthost, + info_ctr, + preferred_len, + &total_entries, + &resume_handle, + &result); + if (!NT_STATUS_IS_OK(status)) { + return ntstatus_to_werror(status); + } + return result; + } + + /* request just one share */ + status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, + pipe_hnd->desthost, + argv[0], + level, + &info, + &result); + + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + goto done; + } + + if (!W_ERROR_IS_OK(result)) { + goto done; + } + + /* construct ctr */ + ZERO_STRUCTP(info_ctr); + + info_ctr->level = level; + + switch (level) { + case 1: + { + struct srvsvc_NetShareCtr1 *ctr1; + + ctr1 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr1); + W_ERROR_HAVE_NO_MEMORY(ctr1); + + ctr1->count = 1; + ctr1->array = info.info1; + + info_ctr->ctr.ctr1 = ctr1; + + break; + } + case 2: + { + struct srvsvc_NetShareCtr2 *ctr2; + + ctr2 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr2); + W_ERROR_HAVE_NO_MEMORY(ctr2); + + ctr2->count = 1; + ctr2->array = info.info2; + + info_ctr->ctr.ctr2 = ctr2; + + break; + } + case 502: + { + struct srvsvc_NetShareCtr502 *ctr502; + + ctr502 = talloc_zero(mem_ctx, struct srvsvc_NetShareCtr502); + W_ERROR_HAVE_NO_MEMORY(ctr502); + + ctr502->count = 1; + ctr502->array = info.info502; + + info_ctr->ctr.ctr502 = ctr502; + + break; + } + } /* switch */ +done: + return result; +} + +/*** + * 'net rpc share list' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ +static int rpc_share_list(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + struct SHARE_INFO_1 *i1 = NULL; + uint32_t entries_read = 0; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + uint32_t i, level = 1; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc share list\n" + " %s\n", + _("Usage:"), + _("List shares on remote server")); + return 0; + } + + status = NetShareEnum(c->opt_host, + level, + (uint8_t **)(void *)&i1, + (uint32_t)-1, + &entries_read, + &total_entries, + &resume_handle); + if (status != 0) { + goto done; + } + + /* Display results */ + + if (c->opt_long_list_entries) { + d_printf(_( + "\nEnumerating shared resources (exports) on remote server:\n\n" + "\nShare name Type Description\n" + "---------- ---- -----------\n")); + } + for (i = 0; i < entries_read; i++) + display_share_info_1(c, &i1[i]); + done: + return status; +} + +static bool check_share_availability(struct cli_state *cli, const char *netname) +{ + NTSTATUS status; + + status = cli_tree_connect(cli, netname, "A:", NULL); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("skipping [%s]: not a file share.\n"), netname); + return false; + } + + status = cli_tdis(cli); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("cli_tdis returned %s\n"), nt_errstr(status)); + return false; + } + + return true; +} + +static bool check_share_sanity(struct net_context *c, struct cli_state *cli, + const char *netname, uint32_t type) +{ + /* only support disk shares */ + if (! ( type == STYPE_DISKTREE || type == (STYPE_DISKTREE | STYPE_HIDDEN)) ) { + printf(_("share [%s] is not a diskshare (type: %x)\n"), netname, + type); + return false; + } + + /* skip builtin shares */ + /* FIXME: should print$ be added too ? */ + if (strequal(netname,"IPC$") || strequal(netname,"ADMIN$") || + strequal(netname,"global")) + return false; + + if (c->opt_exclude && in_list(netname, c->opt_exclude, false)) { + printf(_("excluding [%s]\n"), netname); + return false; + } + + return check_share_availability(cli, netname); +} + +/** + * Migrate shares from a remote RPC server to the local RPC server. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_share_migrate_shares_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + WERROR result; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct srvsvc_NetShareInfoCtr ctr_src; + uint32_t i; + struct rpc_pipe_client *srvsvc_pipe = NULL; + struct cli_state *cli_dst = NULL; + uint32_t level = 502; /* includes secdesc */ + uint32_t parm_error = 0; + struct dcerpc_binding_handle *b; + + result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, + &ctr_src); + if (!W_ERROR_IS_OK(result)) + goto done; + + /* connect destination PI_SRVSVC */ + nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe, + &ndr_table_srvsvc); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + b = srvsvc_pipe->binding_handle; + + for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { + + union srvsvc_NetShareInfo info; + struct srvsvc_NetShareInfo502 info502 = + ctr_src.ctr.ctr502->array[i]; + + /* reset error-code */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + if (!check_share_sanity(c, cli, info502.name, info502.type)) + continue; + + /* finally add the share on the dst server */ + + printf(_("migrating: [%s], path: %s, comment: %s, without " + "share-ACLs\n"), + info502.name, info502.path, info502.comment); + + info.info502 = &info502; + + nt_status = dcerpc_srvsvc_NetShareAdd(b, mem_ctx, + srvsvc_pipe->desthost, + 502, + &info, + &parm_error, + &result); + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("cannot add share: %s\n"), + nt_errstr(nt_status)); + goto done; + } + if (W_ERROR_V(result) == W_ERROR_V(WERR_FILE_EXISTS)) { + printf(_(" [%s] does already exist\n"), + info502.name); + continue; + } + + if (!W_ERROR_IS_OK(result)) { + nt_status = werror_to_ntstatus(result); + printf(_("cannot add share: %s\n"), + win_errstr(result)); + goto done; + } + + } + + nt_status = NT_STATUS_OK; + +done: + if (cli_dst) { + cli_shutdown(cli_dst); + } + + return nt_status; + +} + +/** + * Migrate shares from a RPC server to another. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_share_migrate_shares(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc share migrate shares\n" + " %s\n", + _("Usage:"), + _("Migrate shares to local server")); + return 0; + } + + if (!c->opt_host) { + printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_shares_internals, + argc, argv); +} + +/** + * Copy a file/dir + * + * @param f file_info + * @param mask current search mask + * @param state arg-pointer + * + **/ +static NTSTATUS copy_fn(struct file_info *f, + const char *mask, void *state) +{ + static NTSTATUS nt_status; + static struct copy_clistate *local_state; + static fstring filename, new_mask; + fstring dir; + char *old_dir; + struct net_context *c; + + local_state = (struct copy_clistate *)state; + nt_status = NT_STATUS_UNSUCCESSFUL; + + c = local_state->c; + + if (strequal(f->name, ".") || strequal(f->name, "..")) + return NT_STATUS_OK; + + DEBUG(3,("got mask: %s, name: %s\n", mask, f->name)); + + /* DIRECTORY */ + if (f->attr & FILE_ATTRIBUTE_DIRECTORY) { + + DEBUG(3,("got dir: %s\n", f->name)); + + fstrcpy(dir, local_state->cwd); + fstrcat(dir, "\\"); + fstrcat(dir, f->name); + + switch (net_mode_share) + { + case NET_MODE_SHARE_MIGRATE: + /* create that directory */ + nt_status = net_copy_file(c, local_state->mem_ctx, + local_state->cli_share_src, + local_state->cli_share_dst, + dir, dir, + c->opt_acls? true : false, + c->opt_attrs? true : false, + c->opt_timestamps? true:false, + false); + break; + default: + d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share); + return NT_STATUS_INTERNAL_ERROR; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("could not handle dir %s: %s\n"), + dir, nt_errstr(nt_status)); + return nt_status; + } + + /* search below that directory */ + if (strlcpy(new_mask, dir, sizeof(new_mask)) >= sizeof(new_mask)) { + return NT_STATUS_NO_MEMORY; + } + if (strlcat(new_mask, "\\*", sizeof(new_mask)) >= sizeof(new_mask)) { + return NT_STATUS_NO_MEMORY; + } + + old_dir = local_state->cwd; + local_state->cwd = dir; + nt_status = sync_files(local_state, new_mask); + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("could not handle files\n")); + } + local_state->cwd = old_dir; + + return nt_status; + } + + + /* FILE */ + fstrcpy(filename, local_state->cwd); + fstrcat(filename, "\\"); + fstrcat(filename, f->name); + + DEBUG(3,("got file: %s\n", filename)); + + switch (net_mode_share) + { + case NET_MODE_SHARE_MIGRATE: + nt_status = net_copy_file(c, local_state->mem_ctx, + local_state->cli_share_src, + local_state->cli_share_dst, + filename, filename, + c->opt_acls? true : false, + c->opt_attrs? true : false, + c->opt_timestamps? true: false, + true); + break; + default: + d_fprintf(stderr, _("Unsupported file mode %d\n"), + net_mode_share); + return NT_STATUS_INTERNAL_ERROR; + } + + if (!NT_STATUS_IS_OK(nt_status)) + printf(_("could not handle file %s: %s\n"), + filename, nt_errstr(nt_status)); + return nt_status; +} + +/** + * sync files, can be called recursively to list files + * and then call copy_fn for each file + * + * @param cp_clistate pointer to the copy_clistate we work with + * @param mask the current search mask + * + * @return Boolean result + **/ +static NTSTATUS sync_files(struct copy_clistate *cp_clistate, const char *mask) +{ + struct cli_state *targetcli; + char *targetpath = NULL; + NTSTATUS status; + + DEBUG(3,("calling cli_list with mask: %s\n", mask)); + + status = cli_resolve_path(talloc_tos(), "", NULL, + cp_clistate->cli_share_src, + mask, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("cli_resolve_path %s failed with error: " + "%s\n"), + mask, nt_errstr(status)); + return status; + } + + status = cli_list(targetcli, targetpath, cp_clistate->attribute, + copy_fn, cp_clistate); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("listing %s failed with error: %s\n"), + mask, nt_errstr(status)); + } + + return status; +} + + +/** + * Set the top level directory permissions before we do any further copies. + * Should set up ACL inheritance. + **/ + +bool copy_top_level_perms(struct net_context *c, + struct copy_clistate *cp_clistate, + const char *sharename) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + + switch (net_mode_share) { + case NET_MODE_SHARE_MIGRATE: + DEBUG(3,("calling net_copy_fileattr for '.' directory in share %s\n", sharename)); + nt_status = net_copy_fileattr(c, + cp_clistate->mem_ctx, + cp_clistate->cli_share_src, + cp_clistate->cli_share_dst, + "\\", "\\", + c->opt_acls? true : false, + c->opt_attrs? true : false, + c->opt_timestamps? true: false, + false); + break; + default: + d_fprintf(stderr, _("Unsupported mode %d\n"), net_mode_share); + break; + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("Could handle directory attributes for top level " + "directory of share %s. Error %s\n"), + sharename, nt_errstr(nt_status)); + return false; + } + + return true; +} + +/** + * Sync all files inside a remote share to another share (over smb). + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_share_migrate_files_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + WERROR result; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct srvsvc_NetShareInfoCtr ctr_src; + uint32_t i; + uint32_t level = 502; + struct copy_clistate cp_clistate; + bool got_src_share = false; + bool got_dst_share = false; + const char *mask = "\\*"; + char *dst = NULL; + + dst = SMB_STRDUP(c->opt_destination?c->opt_destination:"127.0.0.1"); + if (dst == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + + result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, + &ctr_src); + + if (!W_ERROR_IS_OK(result)) + goto done; + + for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { + + struct srvsvc_NetShareInfo502 info502 = + ctr_src.ctr.ctr502->array[i]; + + if (!check_share_sanity(c, cli, info502.name, info502.type)) + continue; + + /* one might not want to mirror whole discs :) */ + if (strequal(info502.name, "print$") || info502.name[1] == '$') { + d_printf(_("skipping [%s]: builtin/hidden share\n"), + info502.name); + continue; + } + + switch (net_mode_share) + { + case NET_MODE_SHARE_MIGRATE: + printf("syncing"); + break; + default: + d_fprintf(stderr, _("Unsupported mode %d\n"), + net_mode_share); + break; + } + printf(_(" [%s] files and directories %s ACLs, %s DOS " + "Attributes %s\n"), + info502.name, + c->opt_acls ? _("including") : _("without"), + c->opt_attrs ? _("including") : _("without"), + c->opt_timestamps ? _("(preserving timestamps)") : ""); + + cp_clistate.mem_ctx = mem_ctx; + cp_clistate.cli_share_src = NULL; + cp_clistate.cli_share_dst = NULL; + cp_clistate.cwd = NULL; + cp_clistate.attribute = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_DIRECTORY; + cp_clistate.c = c; + + /* open share source */ + nt_status = connect_to_service(c, &cp_clistate.cli_share_src, + smbXcli_conn_remote_sockaddr(cli->conn), + smbXcli_conn_remote_name(cli->conn), + info502.name, "A:"); + if (!NT_STATUS_IS_OK(nt_status)) + goto done; + + got_src_share = true; + + if (net_mode_share == NET_MODE_SHARE_MIGRATE) { + /* open share destination */ + nt_status = connect_to_service(c, &cp_clistate.cli_share_dst, + NULL, dst, info502.name, "A:"); + if (!NT_STATUS_IS_OK(nt_status)) + goto done; + + got_dst_share = true; + } + + if (!copy_top_level_perms(c, &cp_clistate, info502.name)) { + d_fprintf(stderr, _("Could not handle the top level " + "directory permissions for the " + "share: %s\n"), info502.name); + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + nt_status = sync_files(&cp_clistate, mask); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("could not handle files for share: " + "%s\n"), info502.name); + goto done; + } + } + + nt_status = NT_STATUS_OK; + +done: + + if (got_src_share) + cli_shutdown(cp_clistate.cli_share_src); + + if (got_dst_share) + cli_shutdown(cp_clistate.cli_share_dst); + + SAFE_FREE(dst); + return nt_status; + +} + +static int rpc_share_migrate_files(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net share migrate files\n" + " %s\n", + _("Usage:"), + _("Migrate files to local server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_files_internals, + argc, argv); +} + +/** + * Migrate share-ACLs from a remote RPC server to the local RPC server. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_share_migrate_security_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + WERROR result; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + struct srvsvc_NetShareInfoCtr ctr_src; + union srvsvc_NetShareInfo info; + uint32_t i; + struct rpc_pipe_client *srvsvc_pipe = NULL; + struct cli_state *cli_dst = NULL; + uint32_t level = 502; /* includes secdesc */ + uint32_t parm_error = 0; + struct dcerpc_binding_handle *b; + + result = get_share_info(c, pipe_hnd, mem_ctx, level, argc, argv, + &ctr_src); + + if (!W_ERROR_IS_OK(result)) + goto done; + + /* connect destination PI_SRVSVC */ + nt_status = connect_dst_pipe(c, &cli_dst, &srvsvc_pipe, + &ndr_table_srvsvc); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + b = srvsvc_pipe->binding_handle; + + for (i = 0; i < ctr_src.ctr.ctr502->count; i++) { + + struct srvsvc_NetShareInfo502 info502 = + ctr_src.ctr.ctr502->array[i]; + + /* reset error-code */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + if (!check_share_sanity(c, cli, info502.name, info502.type)) + continue; + + printf(_("migrating: [%s], path: %s, comment: %s, including " + "share-ACLs\n"), + info502.name, info502.path, info502.comment); + + if (c->opt_verbose) + display_sec_desc(info502.sd_buf.sd); + + /* FIXME: shouldn't we be able to just set the security descriptor ? */ + info.info502 = &info502; + + /* finally modify the share on the dst server */ + nt_status = dcerpc_srvsvc_NetShareSetInfo(b, mem_ctx, + srvsvc_pipe->desthost, + info502.name, + level, + &info, + &parm_error, + &result); + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("cannot set share-acl: %s\n"), + nt_errstr(nt_status)); + goto done; + } + if (!W_ERROR_IS_OK(result)) { + nt_status = werror_to_ntstatus(result); + printf(_("cannot set share-acl: %s\n"), + win_errstr(result)); + goto done; + } + + } + + nt_status = NT_STATUS_OK; + +done: + if (cli_dst) { + cli_shutdown(cli_dst); + } + + return nt_status; + +} + +/** + * Migrate share-acls from a RPC server to another. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_share_migrate_security(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc share migrate security\n" + " %s\n", + _("Usage:"), + _("Migrate share-acls to local server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_security_internals, + argc, argv); +} + +/** + * Migrate shares (including share-definitions, share-acls and files with acls/attrs) + * from one server to another. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + * + **/ +static int rpc_share_migrate_all(struct net_context *c, int argc, + const char **argv) +{ + int ret; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc share migrate all\n" + " %s\n", + _("Usage:"), + _("Migrates shares including all share settings")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + /* order is important. we don't want to be locked out by the share-acl + * before copying files - gd */ + + ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_shares_internals, argc, argv); + if (ret) + return ret; + + ret = run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_files_internals, argc, argv); + if (ret) + return ret; + + return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_migrate_security_internals, argc, + argv); +} + + +/** + * 'net rpc share migrate' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ +static int rpc_share_migrate(struct net_context *c, int argc, const char **argv) +{ + + struct functable func[] = { + { + "all", + rpc_share_migrate_all, + NET_TRANSPORT_RPC, + N_("Migrate shares from remote to local server"), + N_("net rpc share migrate all\n" + " Migrate shares from remote to local server") + }, + { + "files", + rpc_share_migrate_files, + NET_TRANSPORT_RPC, + N_("Migrate files from remote to local server"), + N_("net rpc share migrate files\n" + " Migrate files from remote to local server") + }, + { + "security", + rpc_share_migrate_security, + NET_TRANSPORT_RPC, + N_("Migrate share-ACLs from remote to local server"), + N_("net rpc share migrate security\n" + " Migrate share-ACLs from remote to local server") + }, + { + "shares", + rpc_share_migrate_shares, + NET_TRANSPORT_RPC, + N_("Migrate shares from remote to local server"), + N_("net rpc share migrate shares\n" + " Migrate shares from remote to local server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + net_mode_share = NET_MODE_SHARE_MIGRATE; + + return net_run_function(c, argc, argv, "net rpc share migrate", func); +} + +struct full_alias { + struct dom_sid sid; + uint32_t num_members; + struct dom_sid *members; +}; + +static int num_server_aliases; +static struct full_alias *server_aliases; + +/* + * Add an alias to the static list. + */ +static void push_alias(struct full_alias *alias) +{ + size_t array_size; + + if (server_aliases == NULL) { + server_aliases = talloc_array(NULL, struct full_alias, 100); + if (server_aliases == NULL) { + smb_panic("talloc_array failed"); + } + } + + array_size = talloc_array_length(server_aliases); + if (array_size == num_server_aliases) { + server_aliases = talloc_realloc(NULL, server_aliases, + struct full_alias, array_size + 100); + if (server_aliases == NULL) { + smb_panic("talloc_realloc failed"); + } + } + + server_aliases[num_server_aliases] = *alias; + num_server_aliases += 1; +} + +/* + * For a specific domain on the server, fetch all the aliases + * and their members. Add all of them to the server_aliases. + */ + +static NTSTATUS rpc_fetch_domain_aliases(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *connect_pol, + const struct dom_sid *domain_sid) +{ + uint32_t start_idx, max_entries, num_entries, i; + struct samr_SamArray *groups = NULL; + NTSTATUS result, status; + struct policy_handle domain_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* Get domain policy handle */ + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + start_idx = 0; + max_entries = 250; + + do { + status = dcerpc_samr_EnumDomainAliases(b, mem_ctx, + &domain_pol, + &start_idx, + &groups, + max_entries, + &num_entries, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + for (i = 0; i < num_entries; i++) { + + struct policy_handle alias_pol; + struct full_alias alias; + struct lsa_SidArray sid_array; + int j; + NTSTATUS _result; + + status = dcerpc_samr_OpenAlias(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + groups->entries[i].idx, + &alias_pol, + &_result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(_result)) { + status = _result; + goto done; + } + + status = dcerpc_samr_GetMembersInAlias(b, mem_ctx, + &alias_pol, + &sid_array, + &_result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(_result)) { + status = _result; + goto done; + } + + alias.num_members = sid_array.num_sids; + + status = dcerpc_samr_Close(b, mem_ctx, &alias_pol, &_result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(_result)) { + status = _result; + goto done; + } + + alias.members = NULL; + + if (alias.num_members > 0) { + alias.members = SMB_MALLOC_ARRAY(struct dom_sid, alias.num_members); + if (alias.members == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (j = 0; j < alias.num_members; j++) + sid_copy(&alias.members[j], + sid_array.sids[j].sid); + } + + sid_compose(&alias.sid, domain_sid, + groups->entries[i].idx); + + push_alias(&alias); + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + status = NT_STATUS_OK; + + done: + dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); + + return status; +} + +/* + * Dump server_aliases as names for debugging purposes. + */ + +static NTSTATUS rpc_aliaslist_dump(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + uint32_t i; + NTSTATUS result; + struct policy_handle lsa_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &lsa_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + for (i=0; i<num_server_aliases; i++) { + char **names; + char **domains; + enum lsa_SidType *types; + int j; + + struct full_alias *alias = &server_aliases[i]; + + result = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &lsa_pol, 1, + &alias->sid, + &domains, &names, &types); + if (!NT_STATUS_IS_OK(result)) + continue; + + DEBUG(1, ("%s\\%s %d: ", domains[0], names[0], types[0])); + + if (alias->num_members == 0) { + DEBUG(1, ("\n")); + continue; + } + + result = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &lsa_pol, + alias->num_members, + alias->members, + &domains, &names, &types); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) + continue; + + for (j=0; j<alias->num_members; j++) + DEBUG(1, ("%s\\%s (%d); ", + domains[j] ? domains[j] : "*unknown*", + names[j] ? names[j] : "*unknown*",types[j])); + DEBUG(1, ("\n")); + } + + dcerpc_lsa_Close(b, mem_ctx, &lsa_pol, &result); + + return NT_STATUS_OK; +} + +/* + * Fetch a list of all server aliases and their members into + * server_aliases. + */ + +static NTSTATUS rpc_aliaslist_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS result, status; + struct policy_handle connect_pol; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol, + &global_sid_Builtin); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol, + domain_sid); + + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + done: + return status; +} + +struct user_token { + fstring name; + struct security_token *token; +}; + +static void add_sid_to_token(struct security_token *token, const struct dom_sid *sid) +{ + NTSTATUS status = add_sid_to_array_unique(token, sid, + &token->sids, &token->num_sids); + /* + * This is both very unlikely and mostly harmless in a command + * line tool + */ + SMB_ASSERT(NT_STATUS_IS_OK(status)); +} + +static void init_user_token(struct user_token *token_list, + struct security_token **token, + struct dom_sid *user_sid) +{ + /* + * This token is not from the auth stack, only has user SIDs + * and must fail if conditional ACEs are found in the security + * descriptor + */ + *token = security_token_initialise(token_list, CLAIMS_EVALUATION_INVALID_STATE); + SMB_ASSERT(*token); + + add_sid_to_token(*token, + user_sid); + + add_sid_to_token(*token, + &global_sid_World); + + add_sid_to_token(*token, + &global_sid_Network); + + add_sid_to_token(*token, + &global_sid_Authenticated_Users); +} + +static void dump_user_token(struct user_token *token) +{ + uint32_t i; + + d_printf("%s\n", token->name); + + for (i=0; i<token->token->num_sids; i++) { + struct dom_sid_buf buf; + d_printf(" %s\n", + dom_sid_str_buf(&token->token->sids[i], &buf)); + } +} + +static bool is_alias_member(struct dom_sid *sid, struct full_alias *alias) +{ + uint32_t i; + + for (i=0; i<alias->num_members; i++) { + if (dom_sid_equal(sid, &alias->members[i])) { + return true; + } + } + + return false; +} + +static void collect_sid_memberships(struct security_token *token, struct dom_sid sid) +{ + int i; + + for (i=0; i<num_server_aliases; i++) { + if (is_alias_member(&sid, &server_aliases[i])) + add_sid_to_token(token, &server_aliases[i].sid); + } +} + +/* + * We got a user token with all the SIDs we can know about without asking the + * server directly. These are the user and domain group sids. All of these can + * be members of aliases. So scan the list of aliases for each of the SIDs and + * add them to the token. + */ + +static void collect_alias_memberships(struct security_token *token) +{ + int num_global_sids = token->num_sids; + int i; + + for (i=0; i<num_global_sids; i++) { + collect_sid_memberships(token, token->sids[i]); + } +} + +static bool get_user_sids(const char *domain, const char *user, + struct user_token *token_list, + struct security_token **token) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + enum wbcSidType type; + fstring full_name; + struct wbcDomainSid wsid; + char sid_str[WBC_SID_STRING_BUFLEN]; + struct dom_sid user_sid; + uint32_t num_groups; + gid_t *groups = NULL; + uint32_t i; + + fstr_sprintf(full_name, "%s%c%s", + domain, *lp_winbind_separator(), user); + + /* First let's find out the user sid */ + + wbc_status = wbcLookupName(domain, user, &wsid, &type); + + if (!WBC_ERROR_IS_OK(wbc_status)) { + DEBUG(1, ("winbind could not find %s: %s\n", + full_name, wbcErrorString(wbc_status))); + return false; + } + + wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str)); + + if (type != WBC_SID_NAME_USER) { + DEBUG(1, ("%s is not a user\n", full_name)); + return false; + } + + if (!string_to_sid(&user_sid, sid_str)) { + DEBUG(1,("Could not convert sid %s from string\n", sid_str)); + return false; + } + + init_user_token(token_list, token, &user_sid); + + /* And now the groups winbind knows about */ + + wbc_status = wbcGetGroups(full_name, &num_groups, &groups); + if (!WBC_ERROR_IS_OK(wbc_status)) { + DEBUG(1, ("winbind could not get groups of %s: %s\n", + full_name, wbcErrorString(wbc_status))); + return false; + } + + for (i = 0; i < num_groups; i++) { + gid_t gid = groups[i]; + struct dom_sid sid; + bool ok; + + wbc_status = wbcGidToSid(gid, &wsid); + if (!WBC_ERROR_IS_OK(wbc_status)) { + DEBUG(1, ("winbind could not find SID of gid %u: %s\n", + (unsigned int)gid, wbcErrorString(wbc_status))); + wbcFreeMemory(groups); + return false; + } + + wbcSidToStringBuf(&wsid, sid_str, sizeof(sid_str)); + + DEBUG(3, (" %s\n", sid_str)); + + ok = string_to_sid(&sid, sid_str); + if (!ok) { + DEBUG(1, ("Failed to convert string to SID\n")); + wbcFreeMemory(groups); + return false; + } + add_sid_to_token(*token, &sid); + } + wbcFreeMemory(groups); + + return true; +} + +/** + * Get a list of all user tokens we want to look at + **/ + +static bool get_user_tokens(struct net_context *c, int *num_tokens, + struct user_token **user_tokens) +{ + wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE; + uint32_t i, num_users; + const char **users; + struct user_token *result; + TALLOC_CTX *frame = NULL; + + if (lp_winbind_use_default_domain() && + (c->opt_target_workgroup == NULL)) { + d_fprintf(stderr, _("winbind use default domain = yes set, " + "please specify a workgroup\n")); + return false; + } + + /* Send request to winbind daemon */ + + wbc_status = wbcListUsers(NULL, &num_users, &users); + if (!WBC_ERROR_IS_OK(wbc_status)) { + DEBUG(1, (_("winbind could not list users: %s\n"), + wbcErrorString(wbc_status))); + return false; + } + + result = talloc_zero_array(NULL, struct user_token, num_users); + + if (result == NULL) { + DBG_ERR("Could not talloc token array\n"); + wbcFreeMemory(users); + return false; + } + + frame = talloc_stackframe(); + for (i=0; i < num_users; i++) { + fstring domain, user; + char *p; + + fstrcpy(result[i].name, users[i]); + + p = strchr(users[i], *lp_winbind_separator()); + + DEBUG(3, ("%s\n", users[i])); + + if (p == NULL) { + fstrcpy(domain, c->opt_target_workgroup); + fstrcpy(user, users[i]); + } else { + *p++ = '\0'; + fstrcpy(domain, users[i]); + if (!strupper_m(domain)) { + DEBUG(1, ("strupper_m %s failed\n", domain)); + wbcFreeMemory(users); + return false; + } + fstrcpy(user, p); + } + + get_user_sids(domain, user, result, &(result[i].token)); + } + TALLOC_FREE(frame); + wbcFreeMemory(users); + + *num_tokens = num_users; + *user_tokens = result; + + return true; +} + +static bool get_user_tokens_from_file(FILE *f, + int *num_tokens, + struct user_token **tokens) +{ + struct user_token *token = NULL; + + while (!feof(f)) { + fstring line; + + if (fgets(line, sizeof(line)-1, f) == NULL) { + return true; + } + + if ((strlen(line) > 0) && (line[strlen(line)-1] == '\n')) { + line[strlen(line)-1] = '\0'; + } + + if (line[0] == ' ') { + /* We have a SID */ + + struct dom_sid sid; + if(!string_to_sid(&sid, &line[1])) { + DEBUG(1,("get_user_tokens_from_file: Could " + "not convert sid %s \n",&line[1])); + return false; + } + + if (token == NULL) { + DEBUG(0, ("File does not begin with username\n")); + return false; + } + + add_sid_to_token(token->token, &sid); + continue; + } + + /* And a new user... */ + + *num_tokens += 1; + *tokens = talloc_realloc(NULL, + *tokens, + struct user_token, + *num_tokens); + if (*tokens == NULL) { + DBG_ERR("Could not talloc_realloc tokens\n"); + return false; + } + + token = &((*tokens)[*num_tokens-1]); + + if (strlcpy(token->name, line, sizeof(token->name)) >= sizeof(token->name)) { + return false; + } + token->token + = security_token_initialise(*tokens, + CLAIMS_EVALUATION_INVALID_STATE); + if (token->token == NULL) { + DBG_ERR("security_token_initialise() failed: " + "Could not allocate security_token with \n"); + return false; + } + + continue; + } + + return false; +} + + +/* + * Show the list of all users that have access to a share + */ + +static void show_userlist(struct rpc_pipe_client *pipe_hnd, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *netname, + int num_tokens, + struct user_token *tokens) +{ + uint16_t fnum; + struct security_descriptor *share_sd = NULL; + struct security_descriptor *root_sd = NULL; + int i; + union srvsvc_NetShareInfo info; + WERROR result; + NTSTATUS status; + struct smbXcli_tcon *orig_tcon = NULL; + char *orig_share = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, + pipe_hnd->desthost, + netname, + 502, + &info, + &result); + + if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) { + DEBUG(1, ("Could not query secdesc for share %s\n", + netname)); + return; + } + + share_sd = info.info502->sd_buf.sd; + if (share_sd == NULL) { + DEBUG(1, ("Got no secdesc for share %s\n", + netname)); + } + + if (cli_state_has_tcon(cli)) { + cli_state_save_tcon_share(cli, &orig_tcon, &orig_share); + } + + if (!NT_STATUS_IS_OK(cli_tree_connect(cli, netname, "A:", NULL))) { + cli_state_restore_tcon_share(cli, orig_tcon, orig_share); + return; + } + + if (!NT_STATUS_IS_OK(cli_ntcreate(cli, "\\", 0, READ_CONTROL_ACCESS, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, 0x0, 0x0, &fnum, NULL))) { + cli_query_secdesc(cli, fnum, mem_ctx, &root_sd); + } + + for (i=0; i<num_tokens; i++) { + uint32_t acc_granted; + + if (share_sd != NULL) { + status = se_access_check(share_sd, tokens[i].token, + 1, &acc_granted); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not check share_sd for " + "user %s\n", + tokens[i].name)); + continue; + } + } + + if (root_sd == NULL) { + d_printf(" %s\n", tokens[i].name); + continue; + } + + status = se_access_check(root_sd, tokens[i].token, + 1, &acc_granted); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not check root_sd for user %s\n", + tokens[i].name)); + continue; + } + d_printf(" %s\n", tokens[i].name); + } + + if (fnum != (uint16_t)-1) + cli_close(cli, fnum); + cli_tdis(cli); + cli_state_restore_tcon_share(cli, orig_tcon, orig_share); + + return; +} + +/** + * List shares on a remote RPC server, including the security descriptors. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_share_allowedusers_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + bool r; + FILE *f; + NTSTATUS nt_status = NT_STATUS_OK; + uint32_t total_entries = 0; + uint32_t resume_handle = 0; + uint32_t preferred_len = 0xffffffff; + uint32_t i; + struct dcerpc_binding_handle *b = NULL; + struct srvsvc_NetShareInfoCtr info_ctr; + struct srvsvc_NetShareCtr1 ctr1; + WERROR result; + + struct user_token *tokens = NULL; + int num_tokens = 0; + + if (argc == 0) { + f = stdin; + } else { + if (strequal(argv[0], "-")) { + f = stdin; + } else { + f = fopen(argv[0], "r"); + } + argv++; + argc--; + } + + if (f == NULL) { + DEBUG(0, ("Could not open userlist: %s\n", strerror(errno))); + return NT_STATUS_UNSUCCESSFUL; + } + + r = get_user_tokens_from_file(f, &num_tokens, &tokens); + + if (f != stdin) + fclose(f); + + if (!r) { + DEBUG(0, ("Could not read users from file\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + for (i=0; i<num_tokens; i++) + collect_alias_memberships(tokens[i].token); + + ZERO_STRUCT(info_ctr); + ZERO_STRUCT(ctr1); + + info_ctr.level = 1; + info_ctr.ctr.ctr1 = &ctr1; + + b = pipe_hnd->binding_handle; + + if (argc != 0) { + /* Show results only for shares listed on the command line. */ + while (*argv) { + const char *netname = *argv++; + d_printf("%s\n", netname); + show_userlist(pipe_hnd, cli, mem_ctx, netname, + num_tokens, tokens); + } + goto done; + } + + /* Issue the NetShareEnum RPC call and retrieve the response */ + nt_status = dcerpc_srvsvc_NetShareEnumAll(b, + talloc_tos(), + pipe_hnd->desthost, + &info_ctr, + preferred_len, + &total_entries, + &resume_handle, + &result); + + /* Was it successful? */ + if (!NT_STATUS_IS_OK(nt_status)) { + /* Nope. Go clean up. */ + goto done; + } + + if (!W_ERROR_IS_OK(result)) { + /* Nope. Go clean up. */ + nt_status = werror_to_ntstatus(result); + goto done; + } + + if (total_entries == 0) { + goto done; + } + + /* For each returned entry... */ + for (i = 0; i < info_ctr.ctr.ctr1->count; i++) { + const char *netname = info_ctr.ctr.ctr1->array[i].name; + + if (info_ctr.ctr.ctr1->array[i].type != STYPE_DISKTREE) { + continue; + } + + d_printf("%s\n", netname); + + show_userlist(pipe_hnd, cli, mem_ctx, netname, + num_tokens, tokens); + } + done: + TALLOC_FREE(tokens); + TALLOC_FREE(server_aliases); + + return nt_status; +} + +static int rpc_share_allowedusers(struct net_context *c, int argc, + const char **argv) +{ + int result; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc share allowedusers\n" + " %s\n", + _("Usage:"), + _("List allowed users")); + return 0; + } + + result = run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_aliaslist_internals, + argc, argv); + if (result != 0) + return result; + + result = run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_aliaslist_dump, + argc, argv); + if (result != 0) + return result; + + return run_rpc_command(c, NULL, &ndr_table_srvsvc, 0, + rpc_share_allowedusers_internals, + argc, argv); +} + +int net_usersidlist(struct net_context *c, int argc, const char **argv) +{ + int num_tokens = 0; + struct user_token *tokens = NULL; + int i; + + if (argc != 0) { + net_usersidlist_usage(c, argc, argv); + return 0; + } + + if (!get_user_tokens(c, &num_tokens, &tokens)) { + DEBUG(0, ("Could not get the user/sid list\n")); + return -1; + } + + for (i=0; i<num_tokens; i++) { + dump_user_token(&tokens[i]); + } + + TALLOC_FREE(tokens); + return 0; +} + +int net_usersidlist_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net usersidlist\n" + "\tprints out a list of all users the running winbind knows\n" + "\tabout, together with all their SIDs. This is used as\n" + "\tinput to the 'net rpc share allowedusers' command.\n\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +/** + * 'net rpc share' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_share(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "add", + rpc_share_add, + NET_TRANSPORT_RPC, + N_("Add share"), + N_("net rpc share add\n" + " Add share") + }, + { + "delete", + rpc_share_delete, + NET_TRANSPORT_RPC, + N_("Remove share"), + N_("net rpc share delete\n" + " Remove share") + }, + { + "allowedusers", + rpc_share_allowedusers, + NET_TRANSPORT_RPC, + N_("List allowed users"), + N_("net rpc share allowedusers\n" + " List allowed users") + }, + { + "migrate", + rpc_share_migrate, + NET_TRANSPORT_RPC, + N_("Migrate share to local server"), + N_("net rpc share migrate\n" + " Migrate share to local server") + }, + { + "list", + rpc_share_list, + NET_TRANSPORT_RPC, + N_("List shares"), + N_("net rpc share list\n" + " List shares") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + if (argc == 0) { + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc share\n" + " List shares\n" + " Alias for net rpc share list\n")); + net_display_usage_from_functable(func); + return 0; + } + + return rpc_share_list(c, argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc share", func); +} + +static NTSTATUS rpc_sh_share_list(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + + return werror_to_ntstatus(W_ERROR(rpc_share_list(c, argc, argv))); +} + +static NTSTATUS rpc_sh_share_add(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + NET_API_STATUS status; + uint32_t parm_err = 0; + struct SHARE_INFO_2 i2; + + if ((argc < 2) || (argc > 3)) { + d_fprintf(stderr, _("Usage: %s <share> <path> [comment]\n"), + ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + i2.shi2_netname = argv[0]; + i2.shi2_type = STYPE_DISKTREE; + i2.shi2_remark = (argc == 3) ? argv[2] : ""; + i2.shi2_permissions = 0; + i2.shi2_max_uses = 0; + i2.shi2_current_uses = 0; + i2.shi2_path = argv[1]; + i2.shi2_passwd = NULL; + + status = NetShareAdd(pipe_hnd->desthost, + 2, + (uint8_t *)&i2, + &parm_err); + + return werror_to_ntstatus(W_ERROR(status)); +} + +static NTSTATUS rpc_sh_share_delete(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, "%s %s <share>\n", _("Usage:"), ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + return werror_to_ntstatus(W_ERROR(NetShareDel(pipe_hnd->desthost, argv[0], 0))); +} + +static NTSTATUS rpc_sh_share_info(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + union srvsvc_NetShareInfo info; + WERROR result; + NTSTATUS status; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1) { + d_fprintf(stderr, "%s %s <share>\n", _("Usage:"), ctx->whoami); + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_srvsvc_NetShareGetInfo(b, mem_ctx, + pipe_hnd->desthost, + argv[0], + 2, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + goto done; + } + if (!W_ERROR_IS_OK(result)) { + goto done; + } + + d_printf(_("Name: %s\n"), info.info2->name); + d_printf(_("Comment: %s\n"), info.info2->comment); + d_printf(_("Path: %s\n"), info.info2->path); + d_printf(_("Password: %s\n"), info.info2->password); + + done: + return werror_to_ntstatus(result); +} + +struct rpc_sh_cmd *net_rpc_share_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx) +{ + static struct rpc_sh_cmd cmds[] = { + + { "list", NULL, &ndr_table_srvsvc, rpc_sh_share_list, + N_("List available shares") }, + + { "add", NULL, &ndr_table_srvsvc, rpc_sh_share_add, + N_("Add a share") }, + + { "delete", NULL, &ndr_table_srvsvc, rpc_sh_share_delete, + N_("Delete a share") }, + + { "info", NULL, &ndr_table_srvsvc, rpc_sh_share_info, + N_("Get information about a share") }, + + { NULL, NULL, 0, NULL, NULL } + }; + + return cmds; +} + +/****************************************************************************/ + +static int rpc_file_usage(struct net_context *c, int argc, const char **argv) +{ + return net_file_usage(c, argc, argv); +} + +/** + * Close a file on a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_file_close(struct net_context *c, int argc, const char **argv) +{ + if (argc < 1 || c->display_usage) { + return rpc_file_usage(c, argc, argv); + } + + return NetFileClose(c->opt_host, atoi(argv[0])); +} + +/** + * Formatted print of open file info + * + * @param r struct FILE_INFO_3 contents + **/ + +static void display_file_info_3(struct FILE_INFO_3 *r) +{ + d_printf("%-7.1" PRIu32 " %-20.20s 0x%-4.2x %-6.1u %s\n", + r->fi3_id, r->fi3_username, r->fi3_permissions, + r->fi3_num_locks, r->fi3_pathname); +} + +/** + * List files for a user on a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success).. + **/ + +static int rpc_file_user(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + uint32_t preferred_len = 0xffffffff, i; + char *username=NULL; + uint32_t total_entries = 0; + uint32_t entries_read = 0; + uint32_t resume_handle = 0; + struct FILE_INFO_3 *i3 = NULL; + + if (c->display_usage) { + return rpc_file_usage(c, argc, argv); + } + + /* if argc > 0, must be user command */ + if (argc > 0) { + username = smb_xstrdup(argv[0]); + } + + status = NetFileEnum(c->opt_host, + NULL, + username, + 3, + (uint8_t **)(void *)&i3, + preferred_len, + &entries_read, + &total_entries, + &resume_handle); + + if (status != 0) { + goto done; + } + + /* Display results */ + + d_printf(_( + "\nEnumerating open files on remote server:\n\n" + "\nFileId Opened by Perms Locks Path" + "\n------ --------- ----- ----- ---- \n")); + for (i = 0; i < entries_read; i++) { + display_file_info_3(&i3[i]); + } + done: + SAFE_FREE(username); + return status; +} + +/** + * 'net rpc file' entrypoint. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc_file(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "close", + rpc_file_close, + NET_TRANSPORT_RPC, + N_("Close opened file"), + N_("net rpc file close\n" + " Close opened file") + }, + { + "user", + rpc_file_user, + NET_TRANSPORT_RPC, + N_("List files opened by user"), + N_("net rpc file user\n" + " List files opened by user") + }, +#if 0 + { + "info", + rpc_file_info, + NET_TRANSPORT_RPC, + N_("Display information about opened file"), + N_("net rpc file info\n" + " Display information about opened file") + }, +#endif + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + if (argc == 0) { + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rpc file\n" + " List opened files\n")); + net_display_usage_from_functable(func); + return 0; + } + + return rpc_file_user(c, argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc file", func); +} + +/** + * ABORT the shutdown of a remote RPC Server, over initshutdown pipe. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_shutdown_abort_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + WERROR result; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_initshutdown_Abort(b, mem_ctx, NULL, &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (W_ERROR_IS_OK(result)) { + d_printf(_("\nShutdown successfully aborted\n")); + DEBUG(5,("cmd_shutdown_abort: query succeeded\n")); + } else + DEBUG(5,("cmd_shutdown_abort: query failed\n")); + + return werror_to_ntstatus(result); +} + +/** + * ABORT the shutdown of a remote RPC Server, over winreg pipe. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_reg_shutdown_abort_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + WERROR werr; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + result = dcerpc_winreg_AbortSystemShutdown(b, mem_ctx, NULL, &werr); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(5,("cmd_reg_abort_shutdown: query failed\n")); + return result; + } + if (W_ERROR_IS_OK(werr)) { + d_printf(_("\nShutdown successfully aborted\n")); + DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n")); + } else + DEBUG(5,("cmd_reg_abort_shutdown: query failed\n")); + + return werror_to_ntstatus(werr); +} + +/** + * ABORT the shutdown of a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_shutdown_abort(struct net_context *c, int argc, + const char **argv) +{ + int rc = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc abortshutdown\n" + " %s\n", + _("Usage:"), + _("Abort a scheduled shutdown")); + return 0; + } + + rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0, + rpc_shutdown_abort_internals, argc, argv); + + if (rc == 0) + return rc; + + DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n")); + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_reg_shutdown_abort_internals, + argc, argv); +} + +/** + * Shut down a remote RPC Server via initshutdown pipe. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_init_shutdown_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + WERROR result; + const char *msg = N_("This machine will be shutdown shortly"); + uint32_t timeout = 20; + struct lsa_StringLarge msg_string; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (c->opt_comment) { + msg = c->opt_comment; + } + if (c->opt_timeout) { + timeout = c->opt_timeout; + } + + msg_string.string = msg; + + /* create an entry */ + status = dcerpc_initshutdown_Init(b, mem_ctx, NULL, + &msg_string, timeout, c->opt_force, c->opt_reboot, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (W_ERROR_IS_OK(result)) { + d_printf(_("\nShutdown of remote machine succeeded\n")); + DEBUG(5,("Shutdown of remote machine succeeded\n")); + } else { + DEBUG(1,("Shutdown of remote machine failed!\n")); + } + return werror_to_ntstatus(result); +} + +/** + * Shut down a remote RPC Server via winreg pipe. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the remote server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_reg_shutdown_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + const char *msg = N_("This machine will be shutdown shortly"); + uint32_t timeout = 20; + struct lsa_StringLarge msg_string; + NTSTATUS result; + WERROR werr; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (c->opt_comment) { + msg = c->opt_comment; + } + msg_string.string = msg; + + if (c->opt_timeout) { + timeout = c->opt_timeout; + } + + /* create an entry */ + result = dcerpc_winreg_InitiateSystemShutdown(b, mem_ctx, NULL, + &msg_string, timeout, c->opt_force, c->opt_reboot, + &werr); + if (!NT_STATUS_IS_OK(result)) { + d_fprintf(stderr, "\nShutdown of remote machine failed\n"); + return result; + } + + if (W_ERROR_IS_OK(werr)) { + d_printf(_("\nShutdown of remote machine succeeded\n")); + } else { + d_fprintf(stderr, "\nShutdown of remote machine failed\n"); + if ( W_ERROR_EQUAL(werr, WERR_MACHINE_LOCKED) ) + d_fprintf(stderr, "\nMachine locked, use -f switch to force\n"); + else + d_fprintf(stderr, "\nresult was: %s\n", win_errstr(werr)); + } + + return werror_to_ntstatus(werr); +} + +/** + * Shut down a remote RPC server. + * + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ + +static int rpc_shutdown(struct net_context *c, int argc, const char **argv) +{ + int rc = -1; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc shutdown\n" + " %s\n", + _("Usage:"), + _("Shut down a remote RPC server")); + return 0; + } + + rc = run_rpc_command(c, NULL, &ndr_table_initshutdown, 0, + rpc_init_shutdown_internals, argc, argv); + + if (rc) { + DEBUG(1, ("initshutdown pipe failed, trying winreg pipe\n")); + rc = run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_reg_shutdown_internals, argc, argv); + } + + return rc; +} + +/*************************************************************************** + NT Domain trusts code (i.e. 'net rpc trustdom' functionality) + ***************************************************************************/ + +/** + * Add interdomain trust account to the RPC server. + * All parameters (except for argc and argv) are passed by run_rpc_command + * function. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return normal NTSTATUS return code. + */ + +static NTSTATUS rpc_trustdom_add_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle connect_pol, domain_pol, user_pol; + NTSTATUS status, result; + char *acct_name; + struct lsa_String lsa_acct_name; + uint32_t acb_info; + uint32_t acct_flags=0; + uint32_t user_rid; + uint32_t access_granted = 0; + union samr_UserInfo info; + unsigned int orig_timeout; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + DATA_BLOB session_key = data_blob_null; + TALLOC_CTX *frame = NULL; + + if (argc != 2) { + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc trustdom add <domain_name> " + "<trust password>\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + frame = talloc_stackframe(); + + /* + * Make valid trusting domain account (ie. uppercased and with '$' appended) + */ + + if (asprintf(&acct_name, "%s$", argv[0]) < 0) { + status = NT_STATUS_NO_MEMORY; + } + + if (!strupper_m(acct_name)) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + init_lsa_String(&lsa_acct_name, acct_name); + + status = cli_get_session_key(frame, pipe_hnd, &session_key); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Error getting session_key of SAM pipe. Error was %s\n", + nt_errstr(status))); + goto done; + } + + /* Get samr policy handle */ + status = dcerpc_samr_Connect2(b, frame, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, frame, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* This call can take a long time - allow the server to time out. + * 35 seconds should do it. */ + + orig_timeout = rpccli_set_timeout(pipe_hnd, 35000); + + /* Create trusting domain's account */ + acb_info = ACB_NORMAL; + acct_flags = SEC_GENERIC_READ | SEC_GENERIC_WRITE | SEC_GENERIC_EXECUTE | + SEC_STD_WRITE_DAC | SEC_STD_DELETE | + SAMR_USER_ACCESS_SET_PASSWORD | + SAMR_USER_ACCESS_GET_ATTRIBUTES | + SAMR_USER_ACCESS_SET_ATTRIBUTES; + + status = dcerpc_samr_CreateUser2(b, frame, + &domain_pol, + &lsa_acct_name, + acb_info, + acct_flags, + &user_pol, + &access_granted, + &user_rid, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + /* And restore our original timeout. */ + rpccli_set_timeout(pipe_hnd, orig_timeout); + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_printf(_("net rpc trustdom add: create user %s failed %s\n"), + acct_name, nt_errstr(result)); + goto done; + } + + { + struct samr_CryptPassword crypt_pwd; + + ZERO_STRUCT(info.info23); + + status = init_samr_CryptPassword(argv[1], + &session_key, + &crypt_pwd); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + info.info23.info.fields_present = SAMR_FIELD_ACCT_FLAGS | + SAMR_FIELD_NT_PASSWORD_PRESENT; + info.info23.info.acct_flags = ACB_DOMTRUST; + info.info23.password = crypt_pwd; + + status = dcerpc_samr_SetUserInfo2(b, frame, + &user_pol, + 23, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + DEBUG(0,("Could not set trust account password: %s\n", + nt_errstr(result))); + goto done; + } + } + + status = NT_STATUS_OK; + done: + SAFE_FREE(acct_name); + data_blob_clear_free(&session_key); + TALLOC_FREE(frame); + return status; +} + +/** + * Create interdomain trust account for a remote domain. + * + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + **/ + +static int rpc_trustdom_add(struct net_context *c, int argc, const char **argv) +{ + if (argc > 0 && !c->display_usage) { + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_trustdom_add_internals, argc, argv); + } else { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc trustdom add <domain_name> <trust " + "password>\n")); + return -1; + } +} + + +/** + * Remove interdomain trust account from the RPC server. + * All parameters (except for argc and argv) are passed by run_rpc_command + * function. + * + * @param c A net_context structure. + * @param domain_sid The domain sid acquired from the server. + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return normal NTSTATUS return code. + */ + +static NTSTATUS rpc_trustdom_del_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle connect_pol, domain_pol, user_pol; + NTSTATUS status, result; + char *acct_name; + struct dom_sid trust_acct_sid; + struct samr_Ids user_rids, name_types; + struct lsa_String lsa_acct_name; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1) { + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc trustdom del <domain_name>\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Make valid trusting domain account (ie. uppercased and with '$' appended) + */ + acct_name = talloc_asprintf(mem_ctx, "%s$", argv[0]); + + if (acct_name == NULL) + return NT_STATUS_NO_MEMORY; + + if (!strupper_m(acct_name)) { + TALLOC_FREE(acct_name); + return NT_STATUS_INVALID_PARAMETER; + } + + /* Get samr policy handle */ + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + discard_const_p(struct dom_sid2, domain_sid), + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + init_lsa_String(&lsa_acct_name, acct_name); + + status = dcerpc_samr_LookupNames(b, mem_ctx, + &domain_pol, + 1, + &lsa_acct_name, + &user_rids, + &name_types, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("net rpc trustdom del: LookupNames on user %s " + "failed %s\n"), + acct_name, nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_printf(_("net rpc trustdom del: LookupNames on user %s " + "failed %s\n"), + acct_name, nt_errstr(result) ); + goto done; + } + if (user_rids.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + if (name_types.count != 1) { + status = NT_STATUS_INVALID_NETWORK_RESPONSE; + goto done; + } + + status = dcerpc_samr_OpenUser(b, mem_ctx, + &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + user_rids.ids[0], + &user_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("net rpc trustdom del: OpenUser on user %s failed " + "%s\n"), + acct_name, nt_errstr(status) ); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_printf(_("net rpc trustdom del: OpenUser on user %s failed " + "%s\n"), + acct_name, nt_errstr(result) ); + goto done; + } + + /* append the rid to the domain sid */ + if (!sid_compose(&trust_acct_sid, domain_sid, user_rids.ids[0])) { + goto done; + } + + /* remove the sid */ + + status = dcerpc_samr_RemoveMemberFromForeignDomain(b, mem_ctx, + &user_pol, + &trust_acct_sid, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain" + " on user %s failed %s\n"), + acct_name, nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_printf(_("net rpc trustdom del: RemoveMemberFromForeignDomain" + " on user %s failed %s\n"), + acct_name, nt_errstr(result) ); + goto done; + } + + + /* Delete user */ + + status = dcerpc_samr_DeleteUser(b, mem_ctx, + &user_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("net rpc trustdom del: DeleteUser on user %s failed " + "%s\n"), + acct_name, nt_errstr(status)); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + result = status; + d_printf(_("net rpc trustdom del: DeleteUser on user %s failed " + "%s\n"), + acct_name, nt_errstr(result) ); + goto done; + } + + if (!NT_STATUS_IS_OK(result)) { + d_printf(_("Could not set trust account password: %s\n"), + nt_errstr(result)); + goto done; + } + + done: + return status; +} + +/** + * Delete interdomain trust account for a remote domain. + * + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + **/ + +static int rpc_trustdom_del(struct net_context *c, int argc, const char **argv) +{ + if (argc > 0 && !c->display_usage) { + return run_rpc_command(c, NULL, &ndr_table_samr, 0, + rpc_trustdom_del_internals, argc, argv); + } else { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc trustdom del <domain>\n")); + return -1; + } +} + +static NTSTATUS rpc_trustdom_get_pdc(struct net_context *c, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + const char *domain_name) +{ + char *dc_name = NULL; + const char *buffer = NULL; + struct rpc_pipe_client *netr; + NTSTATUS status; + WERROR result; + struct dcerpc_binding_handle *b; + + /* Use NetServerEnum2 */ + + if (cli_get_pdc_name(cli, domain_name, &dc_name)) { + SAFE_FREE(dc_name); + return NT_STATUS_OK; + } + + DEBUG(1,("NetServerEnum2 error: Couldn't find primary domain controller " + "for domain %s\n", domain_name)); + + /* Try netr_GetDcName */ + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon, + &netr); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + b = netr->binding_handle; + + status = dcerpc_netr_GetDcName(b, mem_ctx, + netr->desthost, + domain_name, + &buffer, + &result); + TALLOC_FREE(netr); + + if (NT_STATUS_IS_OK(status) && W_ERROR_IS_OK(result)) { + return status; + } + + DEBUG(1,("netr_GetDcName error: Couldn't find primary domain controller " + "for domain %s\n", domain_name)); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + return werror_to_ntstatus(result); +} + +/** + * Establish trust relationship to a trusting domain. + * Interdomain account must already be created on remote PDC. + * + * @param c A net_context structure. + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + **/ + +static int rpc_trustdom_establish(struct net_context *c, int argc, + const char **argv) +{ + struct cli_state *cli = NULL; + struct sockaddr_storage server_ss; + struct rpc_pipe_client *pipe_hnd = NULL; + struct policy_handle connect_hnd; + TALLOC_CTX *mem_ctx; + NTSTATUS nt_status, result; + struct dom_sid *domain_sid; + char* domain_name; + char* acct_name; + const char *pwd = NULL; + fstring pdc_name; + union lsa_PolicyInformation *info = NULL; + struct dcerpc_binding_handle *b; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + /* + * Connect to \\server\ipc$ as 'our domain' account with password + */ + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc trustdom establish <domain_name>\n")); + return -1; + } + + domain_name = smb_xstrdup(argv[0]); + if (!strupper_m(domain_name)) { + SAFE_FREE(domain_name); + return -1; + } + + /* account name used at first is our domain's name with '$' */ + if (asprintf(&acct_name, "%s$", lp_workgroup()) == -1) { + return -1; + } + if (!strupper_m(acct_name)) { + SAFE_FREE(domain_name); + SAFE_FREE(acct_name); + return -1; + } + cli_credentials_set_username(c->creds, acct_name, CRED_SPECIFIED); + + /* + * opt_workgroup will be used by connection functions further, + * hence it should be set to remote domain name instead of ours + */ + if (c->opt_workgroup) { + c->opt_workgroup = smb_xstrdup(domain_name); + }; + + /* find the domain controller */ + if (!net_find_pdc(&server_ss, pdc_name, domain_name)) { + DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name)); + return -1; + } + + /* connect to ipc$ as username/password */ + nt_status = connect_to_ipc(c, &cli, &server_ss, pdc_name); + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + + /* Is it trusting domain account for sure ? */ + DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + /* store who we connected to */ + + saf_store( domain_name, pdc_name ); + + /* + * Connect to \\server\ipc$ again (this time anonymously) + */ + + nt_status = connect_to_ipc_anonymous(c, &cli, &server_ss, + (char*)pdc_name); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n", + domain_name, nt_errstr(nt_status))); + return -1; + } + + if (!(mem_ctx = talloc_init("establishing trust relationship to " + "domain %s", domain_name))) { + DEBUG(0, ("talloc_init() failed\n")); + cli_shutdown(cli); + return -1; + } + + /* Make sure we're talking to a proper server */ + + nt_status = rpc_trustdom_get_pdc(c, cli, mem_ctx, domain_name); + if (!NT_STATUS_IS_OK(nt_status)) { + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + /* + * Call LsaOpenPolicy and LsaQueryInfo + */ + + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", nt_errstr(nt_status) )); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + b = pipe_hnd->binding_handle; + + nt_status = dcerpc_lsa_open_policy_fallback(b, + mem_ctx, + pipe_hnd->srv_name_slash, + true, + KEY_QUERY_VALUE, + &out_version, + &out_revision_info, + &connect_hnd, + &result); + if (any_nt_status_not_ok(nt_status, result, &nt_status)) { + DBG_ERR("Couldn't open policy handle: %s\n", + nt_errstr(nt_status)); + cli_shutdown(cli); + talloc_free(mem_ctx); + return -1; + } + + /* Querying info level 5 */ + + nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &connect_hnd, + LSA_POLICY_INFO_ACCOUNT_DOMAIN, + &info, + &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + if (NT_STATUS_IS_ERR(result)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + domain_sid = info->account_domain.sid; + + /* There should be actually query info level 3 (following nt serv behaviour), + but I still don't know if it's _really_ necessary */ + + /* + * Store the password in secrets db + */ + + pwd = cli_credentials_get_password(c->creds); + + if (!pdb_set_trusteddom_pw(domain_name, pwd, domain_sid)) { + DEBUG(0, ("Storing password for trusted domain failed.\n")); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + /* + * Close the pipes and clean up + */ + + nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + cli_shutdown(cli); + + talloc_destroy(mem_ctx); + + d_printf(_("Trust to domain %s established\n"), domain_name); + return 0; +} + +/** + * Revoke trust relationship to the remote domain. + * + * @param c A net_context structure. + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + **/ + +static int rpc_trustdom_revoke(struct net_context *c, int argc, + const char **argv) +{ + char* domain_name; + int rc = -1; + + if (argc < 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc trustdom revoke <domain_name>\n" + " Revoke trust relationship\n" + " domain_name\tName of domain to revoke trust\n")); + return -1; + } + + /* generate upper cased domain name */ + domain_name = smb_xstrdup(argv[0]); + if (!strupper_m(domain_name)) { + SAFE_FREE(domain_name); + return -1; + } + + /* delete password of the trust */ + if (!pdb_del_trusteddom_pw(domain_name)) { + DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n", + domain_name)); + goto done; + }; + + rc = 0; +done: + SAFE_FREE(domain_name); + return rc; +} + +static NTSTATUS rpc_query_domain_sid(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dom_sid_buf sid_str; + d_printf("%s\n", dom_sid_str_buf(domain_sid, &sid_str)); + return NT_STATUS_OK; +} + +static void print_trusted_domain(struct dom_sid *dom_sid, const char *trusted_dom_name) +{ + struct dom_sid_buf sid_str; + + d_printf("%-20s%s\n", + trusted_dom_name, + dom_sid_str_buf(dom_sid, &sid_str)); +} + +static NTSTATUS vampire_trusted_domain(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *pol, + struct dom_sid dom_sid, + const char *trusted_dom_name) +{ + NTSTATUS nt_status, result; + union lsa_TrustedDomainInfo *info = NULL; + char *cleartextpwd = NULL; + DATA_BLOB session_key; + DATA_BLOB data = data_blob_null; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + nt_status = dcerpc_lsa_QueryTrustedDomainInfoBySid(b, mem_ctx, + pol, + &dom_sid, + LSA_TRUSTED_DOMAIN_INFO_PASSWORD, + &info, + &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0,("Could not query trusted domain info. Error was %s\n", + nt_errstr(nt_status))); + goto done; + } + if (NT_STATUS_IS_ERR(result)) { + nt_status = result; + DEBUG(0,("Could not query trusted domain info. Error was %s\n", + nt_errstr(result))); + goto done; + } + + data = data_blob(info->password.password->data, + info->password.password->length); + + nt_status = cli_get_session_key(mem_ctx, pipe_hnd, &session_key); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not retrieve session key: %s\n", nt_errstr(nt_status))); + goto done; + } + + cleartextpwd = sess_decrypt_string(mem_ctx, &data, &session_key); + data_blob_free(&session_key); + + if (cleartextpwd == NULL) { + DEBUG(0,("retrieved NULL password\n")); + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!pdb_set_trusteddom_pw(trusted_dom_name, cleartextpwd, &dom_sid)) { + DEBUG(0, ("Storing password for trusted domain failed.\n")); + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + +#ifdef DEBUG_PASSWORD + { + struct dom_sid_buf buf; + DEBUG(100,("successfully vampired trusted domain [%s], " + "sid: [%s], password: [%s]\n", + trusted_dom_name, + dom_sid_str_buf(&dom_sid, &buf), + cleartextpwd)); + } +#endif + +done: + SAFE_FREE(cleartextpwd); + data_blob_free(&data); + + return nt_status; +} + +static int rpc_trustdom_vampire(struct net_context *c, int argc, + const char **argv) +{ + /* common variables */ + TALLOC_CTX* mem_ctx; + struct cli_state *cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS nt_status, result; + const char *domain_name = NULL; + struct policy_handle connect_hnd; + union lsa_PolicyInformation *info = NULL; + + /* trusted domains listing variables */ + unsigned int enum_ctx = 0; + struct lsa_DomainList dom_list; + fstring pdc_name; + struct dcerpc_binding_handle *b; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc trustdom vampire\n" + " %s\n", + _("Usage:"), + _("Vampire trust relationship from remote server")); + return 0; + } + + /* + * Listing trusted domains (stored in secrets.tdb, if local) + */ + + mem_ctx = talloc_init("trust relationships vampire"); + + /* + * set domain and pdc name to local samba server (default) + * or to remote one given in command line + */ + + if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) { + domain_name = c->opt_workgroup; + c->opt_target_workgroup = c->opt_workgroup; + } else { + fstrcpy(pdc_name, lp_netbios_name()); + domain_name = talloc_strdup(mem_ctx, lp_workgroup()); + c->opt_target_workgroup = domain_name; + }; + + /* open \PIPE\lsarpc and open policy handle */ + nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't connect to domain controller: %s\n", + nt_errstr(nt_status))); + talloc_destroy(mem_ctx); + return -1; + }; + + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", + nt_errstr(nt_status) )); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + b = pipe_hnd->binding_handle; + + nt_status = dcerpc_lsa_open_policy_fallback(b, + mem_ctx, + pipe_hnd->srv_name_slash, + false, + KEY_QUERY_VALUE, + &out_version, + &out_revision_info, + &connect_hnd, + &result); + if (any_nt_status_not_ok(nt_status, result, &nt_status)) { + DBG_ERR("Couldn't open policy handle: %s\n", + nt_errstr(nt_status)); + cli_shutdown(cli); + talloc_free(mem_ctx); + return -1; + } + + /* query info level 5 to obtain sid of a domain being queried */ + nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &connect_hnd, + LSA_POLICY_INFO_ACCOUNT_DOMAIN, + &info, + &result); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + if (NT_STATUS_IS_ERR(result)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + /* + * Keep calling LsaEnumTrustdom over opened pipe until + * the end of enumeration is reached + */ + + d_printf(_("Vampire trusted domains:\n\n")); + + do { + uint32_t i; + + nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx, + &connect_hnd, + &enum_ctx, + &dom_list, + (uint32_t)-1, + &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + if (NT_STATUS_IS_ERR(result)) { + nt_status = result; + DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + + for (i = 0; i < dom_list.count; i++) { + + print_trusted_domain(dom_list.domains[i].sid, + dom_list.domains[i].name.string); + + nt_status = vampire_trusted_domain(pipe_hnd, mem_ctx, &connect_hnd, + *dom_list.domains[i].sid, + dom_list.domains[i].name.string); + if (!NT_STATUS_IS_OK(nt_status)) { + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + }; + + /* + * in case of no trusted domains say something rather + * than just display blank line + */ + if (!dom_list.count) d_printf(_("none\n")); + + } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); + + /* close this connection before doing next one */ + nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + /* close lsarpc pipe and connection to IPC$ */ + cli_shutdown(cli); + + talloc_destroy(mem_ctx); + return 0; +} + +static int rpc_trustdom_list(struct net_context *c, int argc, const char **argv) +{ + /* common variables */ + TALLOC_CTX* mem_ctx; + struct cli_state *cli = NULL, *remote_cli = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS nt_status, result; + const char *domain_name = NULL; + struct dom_sid *queried_dom_sid; + int ascii_dom_name_len; + struct policy_handle connect_hnd; + union lsa_PolicyInformation *info = NULL; + struct dcerpc_binding_handle *b = NULL; + + /* trusted domains listing variables */ + unsigned int num_domains, enum_ctx = 0; + uint32_t i; + struct lsa_DomainList dom_list; + fstring pdc_name; + bool found_domain; + + /* trusting domains listing variables */ + struct policy_handle domain_hnd; + struct samr_SamArray *trusts = NULL; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc trustdom list\n" + " %s\n", + _("Usage:"), + _("List incoming and outgoing trust relationships")); + return 0; + } + + /* + * Listing trusted domains (stored in secrets.tdb, if local) + */ + + mem_ctx = talloc_init("trust relationships listing"); + + /* + * set domain and pdc name to local samba server (default) + * or to remote one given in command line + */ + + if (strcasecmp_m(c->opt_workgroup, lp_workgroup())) { + domain_name = c->opt_workgroup; + c->opt_target_workgroup = c->opt_workgroup; + } else { + fstrcpy(pdc_name, lp_netbios_name()); + domain_name = talloc_strdup(mem_ctx, lp_workgroup()); + c->opt_target_workgroup = domain_name; + }; + + /* open \PIPE\lsarpc and open policy handle */ + nt_status = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't connect to domain controller: %s\n", + nt_errstr(nt_status))); + talloc_destroy(mem_ctx); + return -1; + }; + + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise lsa pipe. Error was %s\n", + nt_errstr(nt_status) )); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + b = pipe_hnd->binding_handle; + + nt_status = dcerpc_lsa_open_policy_fallback(b, + mem_ctx, + pipe_hnd->srv_name_slash, + true, + KEY_QUERY_VALUE, + &out_version, + &out_revision_info, + &connect_hnd, + &result); + if (any_nt_status_not_ok(nt_status, result, &nt_status)) { + DBG_ERR("Couldn't open policy handle: %s\n", + nt_errstr(nt_status)); + cli_shutdown(cli); + talloc_free(mem_ctx); + return -1; + } + + /* query info level 5 to obtain sid of a domain being queried */ + nt_status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &connect_hnd, + LSA_POLICY_INFO_ACCOUNT_DOMAIN, + &info, + &result); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + if (NT_STATUS_IS_ERR(result)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + + queried_dom_sid = info->account_domain.sid; + + /* + * Keep calling LsaEnumTrustdom over opened pipe until + * the end of enumeration is reached + */ + + d_printf(_("Trusted domains list:\n\n")); + + found_domain = false; + + do { + nt_status = dcerpc_lsa_EnumTrustDom(b, mem_ctx, + &connect_hnd, + &enum_ctx, + &dom_list, + (uint32_t)-1, + &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + if (NT_STATUS_IS_ERR(result)) { + DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + + for (i = 0; i < dom_list.count; i++) { + print_trusted_domain(dom_list.domains[i].sid, + dom_list.domains[i].name.string); + found_domain = true; + }; + + + } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); + + /* + * in case of no trusted domains say something rather + * than just display blank line + */ + if (!found_domain) { + d_printf(_("none\n")); + } + + /* close this connection before doing next one */ + nt_status = dcerpc_lsa_Close(b, mem_ctx, &connect_hnd, &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + TALLOC_FREE(pipe_hnd); + + /* + * Listing trusting domains (stored in passdb backend, if local) + */ + + d_printf(_("\nTrusting domains list:\n\n")); + + /* + * Open \PIPE\samr and get needed policy handles + */ + nt_status = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Could not initialise samr pipe. Error was %s\n", nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + b = pipe_hnd->binding_handle; + + /* SamrConnect2 */ + nt_status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + SAMR_ACCESS_LOOKUP_DOMAIN, + &connect_hnd, + &result); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + if (!NT_STATUS_IS_OK(result)) { + nt_status = result; + DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + /* SamrOpenDomain - we have to open domain policy handle in order to be + able to enumerate accounts*/ + nt_status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_hnd, + SAMR_DOMAIN_ACCESS_ENUM_ACCOUNTS, + queried_dom_sid, + &domain_hnd, + &result); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't open domain object. Error was %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + if (!NT_STATUS_IS_OK(result)) { + nt_status = result; + DEBUG(0, ("Couldn't open domain object. Error was %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + /* + * perform actual enumeration + */ + + found_domain = false; + + enum_ctx = 0; /* reset enumeration context from last enumeration */ + do { + + nt_status = dcerpc_samr_EnumDomainUsers(b, mem_ctx, + &domain_hnd, + &enum_ctx, + ACB_DOMTRUST, + &trusts, + 0xffff, + &num_domains, + &result); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n", + nt_errstr(nt_status))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + if (NT_STATUS_IS_ERR(result)) { + nt_status = result; + DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n", + nt_errstr(result))); + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + }; + + for (i = 0; i < num_domains; i++) { + + char *str = discard_const_p(char, trusts->entries[i].name.string); + + found_domain = true; + + /* + * get each single domain's sid (do we _really_ need this ?): + * 1) connect to domain's pdc + * 2) query the pdc for domain's sid + */ + + /* get rid of '$' tail */ + ascii_dom_name_len = strlen(str); + if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN) + str[ascii_dom_name_len - 1] = '\0'; + + /* set opt_* variables to remote domain */ + if (!strupper_m(str)) { + cli_shutdown(cli); + talloc_destroy(mem_ctx); + return -1; + } + c->opt_workgroup = talloc_strdup(mem_ctx, str); + c->opt_target_workgroup = c->opt_workgroup; + + d_printf("%-20s", str); + + /* connect to remote domain controller */ + nt_status = net_make_ipc_connection(c, + NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS, + &remote_cli); + if (NT_STATUS_IS_OK(nt_status)) { + /* query for domain's sid */ + if (run_rpc_command( + c, remote_cli, + &ndr_table_lsarpc, 0, + rpc_query_domain_sid, argc, + argv)) + d_printf(_("strange - couldn't get domain's sid\n")); + + cli_shutdown(remote_cli); + + } else { + d_fprintf(stderr, _("domain controller is not " + "responding: %s\n"), + nt_errstr(nt_status)); + d_printf(_("couldn't get domain's sid\n")); + } + } + + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + if (!found_domain) { + d_printf("none\n"); + } + + /* close opened samr and domain policy handles */ + nt_status = dcerpc_samr_Close(b, mem_ctx, &domain_hnd, &result); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name)); + }; + + nt_status = dcerpc_samr_Close(b, mem_ctx, &connect_hnd, &result); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name)); + }; + + /* close samr pipe and connection to IPC$ */ + cli_shutdown(cli); + + talloc_destroy(mem_ctx); + return 0; +} + +/** + * Entrypoint for 'net rpc trustdom' code. + * + * @param argc Standard argc. + * @param argv Standard argv without initial components. + * + * @return Integer status (0 means success). + */ + +static int rpc_trustdom(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "add", + rpc_trustdom_add, + NET_TRANSPORT_RPC, + N_("Add trusting domain's account"), + N_("net rpc trustdom add\n" + " Add trusting domain's account") + }, + { + "del", + rpc_trustdom_del, + NET_TRANSPORT_RPC, + N_("Remove trusting domain's account"), + N_("net rpc trustdom del\n" + " Remove trusting domain's account") + }, + { + "establish", + rpc_trustdom_establish, + NET_TRANSPORT_RPC, + N_("Establish outgoing trust relationship"), + N_("net rpc trustdom establish\n" + " Establish outgoing trust relationship") + }, + { + "revoke", + rpc_trustdom_revoke, + NET_TRANSPORT_RPC, + N_("Revoke outgoing trust relationship"), + N_("net rpc trustdom revoke\n" + " Revoke outgoing trust relationship") + }, + { + "list", + rpc_trustdom_list, + NET_TRANSPORT_RPC, + N_("List in- and outgoing domain trusts"), + N_("net rpc trustdom list\n" + " List in- and outgoing domain trusts") + }, + { + "vampire", + rpc_trustdom_vampire, + NET_TRANSPORT_RPC, + N_("Vampire trusts from remote server"), + N_("net rpc trustdom vampire\n" + " Vampire trusts from remote server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc trustdom", func); +} + +/** + * Check if a server will take rpc commands + * @param flags Type of server to connect to (PDC, DMB, localhost) + * if the host is not explicitly specified + * @return bool (true means rpc supported) + */ +bool net_rpc_check(struct net_context *c, unsigned flags) +{ + struct cli_state *cli; + bool ret = false; + struct sockaddr_storage server_ss; + char *server_name = NULL; + NTSTATUS status; + + /* flags (i.e. server type) may depend on command */ + if (!net_find_server(c, NULL, flags, &server_ss, &server_name)) + return false; + + status = cli_connect_nb(server_name, &server_ss, 0, 0x20, + lp_netbios_name(), SMB_SIGNING_IPC_DEFAULT, + 0, &cli); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + DBG_ERR("NetBIOS support disabled, unable to connect\n"); + } + return false; + } + status = smbXcli_negprot(cli->conn, + cli->timeout, + lp_client_min_protocol(), + lp_client_max_protocol(), + NULL, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) + goto done; + if (smbXcli_conn_protocol(cli->conn) < PROTOCOL_NT1) + goto done; + + ret = true; + done: + cli_shutdown(cli); + return ret; +} + +/* synchronise sam database via samsync rpc calls */ +static int rpc_vampire(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "keytab", + rpc_vampire_keytab, + NET_TRANSPORT_RPC, + N_("Dump remote SAM database to Kerberos Keytab"), + N_("net rpc vampire keytab\n" + " Dump remote SAM database to Kerberos keytab " + "file") + }, + { + "passdb", + rpc_vampire_passdb, + NET_TRANSPORT_RPC, + N_("Dump remote SAM database to passdb"), + N_("net rpc vampire passdb\n" + " Dump remote SAM database to passdb") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + if (c->display_usage) { + d_printf( "%s\n" + "net rpc vampire\n" + " %s\n", + _("Usage:"), + _("Vampire remote SAM database")); + return 0; + } + + return rpc_vampire_passdb(c, argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc vampire", func); +} + +/** + * Migrate everything from a print server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + * + * The order is important ! + * To successfully add drivers the print queues have to exist ! + * Applying ACLs should be the last step, because you're easily locked out. + * + **/ +static int rpc_printer_migrate_all(struct net_context *c, int argc, + const char **argv) +{ + int ret; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate all\n" + " %s\n", + _("Usage:"), + _("Migrate everything from a print server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_printers_internals, argc, + argv); + if (ret) + return ret; + + ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_drivers_internals, argc, + argv); + if (ret) + return ret; + + ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_forms_internals, argc, argv); + if (ret) + return ret; + + ret = run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_settings_internals, argc, + argv); + if (ret) + return ret; + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_security_internals, argc, + argv); + +} + +/** + * Migrate print drivers from a print server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_migrate_drivers(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate drivers\n" + " %s\n", + _("Usage:"), + _("Migrate print-drivers from a print-server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_drivers_internals, + argc, argv); +} + +/** + * Migrate print-forms from a print-server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_migrate_forms(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate forms\n" + " %s\n", + _("Usage:"), + _("Migrate print-forms from a print-server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_forms_internals, + argc, argv); +} + +/** + * Migrate printers from a print-server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_migrate_printers(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate printers\n" + " %s\n", + _("Usage:"), + _("Migrate printers from a print-server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_printers_internals, + argc, argv); +} + +/** + * Migrate printer-ACLs from a print-server + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_migrate_security(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate security\n" + " %s\n", + _("Usage:"), + _("Migrate printer-ACLs from a print-server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_security_internals, + argc, argv); +} + +/** + * Migrate printer-settings from a print-server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_migrate_settings(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer migrate settings\n" + " %s\n", + _("Usage:"), + _("Migrate printer-settings from a " + "print-server")); + return 0; + } + + if (!c->opt_host) { + d_printf(_("no server to migrate\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_migrate_settings_internals, + argc, argv); +} + +/** + * 'net rpc printer' entrypoint. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int rpc_printer_migrate(struct net_context *c, int argc, const char **argv) +{ + + /* ouch: when addriver and setdriver are called from within + rpc_printer_migrate_drivers_internals, the printer-queue already + *has* to exist */ + + struct functable func[] = { + { + "all", + rpc_printer_migrate_all, + NET_TRANSPORT_RPC, + N_("Migrate all from remote to local print server"), + N_("net rpc printer migrate all\n" + " Migrate all from remote to local print server") + }, + { + "drivers", + rpc_printer_migrate_drivers, + NET_TRANSPORT_RPC, + N_("Migrate drivers to local server"), + N_("net rpc printer migrate drivers\n" + " Migrate drivers to local server") + }, + { + "forms", + rpc_printer_migrate_forms, + NET_TRANSPORT_RPC, + N_("Migrate forms to local server"), + N_("net rpc printer migrate forms\n" + " Migrate forms to local server") + }, + { + "printers", + rpc_printer_migrate_printers, + NET_TRANSPORT_RPC, + N_("Migrate printers to local server"), + N_("net rpc printer migrate printers\n" + " Migrate printers to local server") + }, + { + "security", + rpc_printer_migrate_security, + NET_TRANSPORT_RPC, + N_("Migrate printer ACLs to local server"), + N_("net rpc printer migrate security\n" + " Migrate printer ACLs to local server") + }, + { + "settings", + rpc_printer_migrate_settings, + NET_TRANSPORT_RPC, + N_("Migrate printer settings to local server"), + N_("net rpc printer migrate settings\n" + " Migrate printer settings to local server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc printer migrate",func); +} + + +/** + * List printers on a remote RPC server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_list(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer list\n" + " %s\n", + _("Usage:"), + _("List printers on a remote RPC server")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_list_internals, + argc, argv); +} + +/** + * List printer-drivers on a remote RPC server. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_driver_list(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer driver\n" + " %s\n", + _("Usage:"), + _("List printer-drivers on a remote RPC server")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_driver_list_internals, + argc, argv); +} + +/** + * Publish printer in ADS via MSRPC. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_publish_publish(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer publish publish\n" + " %s\n", + _("Usage:"), + _("Publish printer in ADS via MSRPC")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_publish_publish_internals, + argc, argv); +} + +/** + * Update printer in ADS via MSRPC. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_publish_update(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer publish update\n" + " %s\n", + _("Usage:"), + _("Update printer in ADS via MSRPC")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_publish_update_internals, + argc, argv); +} + +/** + * UnPublish printer in ADS via MSRPC. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_publish_unpublish(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer publish unpublish\n" + " %s\n", + _("Usage:\n"), + _("UnPublish printer in ADS via MSRPC")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_publish_unpublish_internals, + argc, argv); +} + +/** + * List published printers via MSRPC. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_publish_list(struct net_context *c, int argc, + const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc printer publish list\n" + " %s\n", + _("Usage:"), + _("List published printers via MSRPC")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_publish_list_internals, + argc, argv); +} + + +/** + * Publish printer in ADS. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + * + * @return A shell status integer (0 for success). + **/ +static int rpc_printer_publish(struct net_context *c, int argc, + const char **argv) +{ + + struct functable func[] = { + { + "publish", + rpc_printer_publish_publish, + NET_TRANSPORT_RPC, + N_("Publish printer in AD"), + N_("net rpc printer publish publish\n" + " Publish printer in AD") + }, + { + "update", + rpc_printer_publish_update, + NET_TRANSPORT_RPC, + N_("Update printer in AD"), + N_("net rpc printer publish update\n" + " Update printer in AD") + }, + { + "unpublish", + rpc_printer_publish_unpublish, + NET_TRANSPORT_RPC, + N_("Unpublish printer"), + N_("net rpc printer publish unpublish\n" + " Unpublish printer") + }, + { + "list", + rpc_printer_publish_list, + NET_TRANSPORT_RPC, + N_("List published printers"), + N_("net rpc printer publish list\n" + " List published printers") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rpc printer publish\n" + " List published printers\n" + " Alias of net rpc printer publish " + "list\n")); + net_display_usage_from_functable(func); + return 0; + } + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_publish_list_internals, + argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc printer publish",func); + +} + + +/** + * Display rpc printer help page. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ +int rpc_printer_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rpc printer LIST [printer] [misc. options] [targets]\n" + "\tlists all printers on print-server\n\n")); + d_printf(_("net rpc printer DRIVER [printer] [misc. options] [targets]\n" + "\tlists all printer-drivers on print-server\n\n")); + d_printf(_("net rpc printer PUBLISH action [printer] [misc. options] [targets]\n" + "\tpublishes printer settings in Active Directory\n" + "\taction can be one of PUBLISH, UPDATE, UNPUBLISH or LIST\n\n")); + d_printf(_("net rpc printer MIGRATE PRINTERS [printer] [misc. options] [targets]" + "\n\tmigrates printers from remote to local server\n\n")); + d_printf(_("net rpc printer MIGRATE SETTINGS [printer] [misc. options] [targets]" + "\n\tmigrates printer-settings from remote to local server\n\n")); + d_printf(_("net rpc printer MIGRATE DRIVERS [printer] [misc. options] [targets]" + "\n\tmigrates printer-drivers from remote to local server\n\n")); + d_printf(_("net rpc printer MIGRATE FORMS [printer] [misc. options] [targets]" + "\n\tmigrates printer-forms from remote to local server\n\n")); + d_printf(_("net rpc printer MIGRATE SECURITY [printer] [misc. options] [targets]" + "\n\tmigrates printer-ACLs from remote to local server\n\n")); + d_printf(_("net rpc printer MIGRATE ALL [printer] [misc. options] [targets]" + "\n\tmigrates drivers, forms, queues, settings and acls from\n" + "\tremote to local print-server\n\n")); + net_common_methods_usage(c, argc, argv); + net_common_flags_usage(c, argc, argv); + d_printf(_( + "\t-v or --verbose\t\t\tgive verbose output\n" + "\t --destination\t\tmigration target server (default: localhost)\n")); + + return -1; +} + +/** + * 'net rpc printer' entrypoint. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ +int net_rpc_printer(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + rpc_printer_list, + NET_TRANSPORT_RPC, + N_("List all printers on print server"), + N_("net rpc printer list\n" + " List all printers on print server") + }, + { + "migrate", + rpc_printer_migrate, + NET_TRANSPORT_RPC, + N_("Migrate printer to local server"), + N_("net rpc printer migrate\n" + " Migrate printer to local server") + }, + { + "driver", + rpc_printer_driver_list, + NET_TRANSPORT_RPC, + N_("List printer drivers"), + N_("net rpc printer driver\n" + " List printer drivers") + }, + { + "publish", + rpc_printer_publish, + NET_TRANSPORT_RPC, + N_("Publish printer in AD"), + N_("net rpc printer publish\n" + " Publish printer in AD") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc == 0) { + if (c->display_usage) { + d_printf(_("Usage:\n")); + d_printf(_("net rpc printer\n" + " List printers\n")); + net_display_usage_from_functable(func); + return 0; + } + return run_rpc_command(c, NULL, &ndr_table_spoolss, 0, + rpc_printer_list_internals, + argc, argv); + } + + return net_run_function(c, argc, argv, "net rpc printer", func); +} + +/** + * 'net rpc' entrypoint. + * + * @param c A net_context structure. + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +int net_rpc(struct net_context *c, int argc, const char **argv) +{ + NET_API_STATUS status; + + struct functable func[] = { + { + "audit", + net_rpc_audit, + NET_TRANSPORT_RPC, + N_("Modify global audit settings"), + N_("net rpc audit\n" + " Modify global audit settings") + }, + { + "info", + net_rpc_info, + NET_TRANSPORT_RPC, + N_("Show basic info about a domain"), + N_("net rpc info\n" + " Show basic info about a domain") + }, + { + "join", + net_rpc_join, + NET_TRANSPORT_RPC, + N_("Join a domain"), + N_("net rpc join\n" + " Join a domain") + }, + { + "oldjoin", + net_rpc_oldjoin, + NET_TRANSPORT_RPC, + N_("Join a domain created in server manager"), + N_("net rpc oldjoin\n" + " Join a domain created in server manager") + }, + { + "testjoin", + net_rpc_testjoin, + NET_TRANSPORT_RPC, + N_("Test that a join is valid"), + N_("net rpc testjoin\n" + " Test that a join is valid") + }, + { + "user", + net_rpc_user, + NET_TRANSPORT_RPC, + N_("List/modify users"), + N_("net rpc user\n" + " List/modify users") + }, + { + "password", + rpc_user_password, + NET_TRANSPORT_RPC, + N_("Change a user password"), + N_("net rpc password\n" + " Change a user password\n" + " Alias for net rpc user password") + }, + { + "group", + net_rpc_group, + NET_TRANSPORT_RPC, + N_("List/modify groups"), + N_("net rpc group\n" + " List/modify groups") + }, + { + "share", + net_rpc_share, + NET_TRANSPORT_RPC, + N_("List/modify shares"), + N_("net rpc share\n" + " List/modify shares") + }, + { + "file", + net_rpc_file, + NET_TRANSPORT_RPC, + N_("List open files"), + N_("net rpc file\n" + " List open files") + }, + { + "printer", + net_rpc_printer, + NET_TRANSPORT_RPC, + N_("List/modify printers"), + N_("net rpc printer\n" + " List/modify printers") + }, + { + "changetrustpw", + net_rpc_changetrustpw, + NET_TRANSPORT_RPC, + N_("Change trust account password"), + N_("net rpc changetrustpw\n" + " Change trust account password") + }, + { + "trustdom", + rpc_trustdom, + NET_TRANSPORT_RPC, + N_("Modify domain trusts"), + N_("net rpc trustdom\n" + " Modify domain trusts") + }, + { + "abortshutdown", + rpc_shutdown_abort, + NET_TRANSPORT_RPC, + N_("Abort a remote shutdown"), + N_("net rpc abortshutdown\n" + " Abort a remote shutdown") + }, + { + "shutdown", + rpc_shutdown, + NET_TRANSPORT_RPC, + N_("Shutdown a remote server"), + N_("net rpc shutdown\n" + " Shutdown a remote server") + }, + { + "vampire", + rpc_vampire, + NET_TRANSPORT_RPC, + N_("Sync a remote NT PDC's data into local passdb"), + N_("net rpc vampire\n" + " Sync a remote NT PDC's data into local passdb") + }, + { + "getsid", + net_rpc_getsid, + NET_TRANSPORT_RPC, + N_("Fetch the domain sid into local secrets.tdb"), + N_("net rpc getsid\n" + " Fetch the domain sid into local secrets.tdb") + }, + { + "rights", + net_rpc_rights, + NET_TRANSPORT_RPC, + N_("Manage privileges assigned to SID"), + N_("net rpc rights\n" + " Manage privileges assigned to SID") + }, + { + "service", + net_rpc_service, + NET_TRANSPORT_RPC, + N_("Start/stop/query remote services"), + N_("net rpc service\n" + " Start/stop/query remote services") + }, + { + "registry", + net_rpc_registry, + NET_TRANSPORT_RPC, + N_("Manage registry hives"), + N_("net rpc registry\n" + " Manage registry hives") + }, + { + "shell", + net_rpc_shell, + NET_TRANSPORT_RPC, + N_("Open interactive shell on remote server"), + N_("net rpc shell\n" + " Open interactive shell on remote server") + }, + { + "trust", + net_rpc_trust, + NET_TRANSPORT_RPC, + N_("Manage trusts"), + N_("net rpc trust\n" + " Manage trusts") + }, + { + "conf", + net_rpc_conf, + NET_TRANSPORT_RPC, + N_("Configure a remote samba server"), + N_("net rpc conf\n" + " Configure a remote samba server") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (status != 0) { + return -1; + } + + return net_run_function(c, argc, argv, "net rpc", func); +} diff --git a/source3/utils/net_rpc_audit.c b/source3/utils/net_rpc_audit.c new file mode 100644 index 0000000..336f6a8 --- /dev/null +++ b/source3/utils/net_rpc_audit.c @@ -0,0 +1,540 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2006,2008 Guenther Deschner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" + +/******************************************************************** +********************************************************************/ + +static int net_help_audit(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rpc audit list View configured Auditing policies\n")); + d_printf(_("net rpc audit enable Enable Auditing\n")); + d_printf(_("net rpc audit disable Disable Auditing\n")); + d_printf(_("net rpc audit get <category> View configured Auditing policy setting\n")); + d_printf(_("net rpc audit set <category> <policy> Set Auditing policies\n\n")); + d_printf(_("\tcategory can be one of: SYSTEM, LOGON, OBJECT, PRIVILEGE, PROCESS, POLICY, SAM, DIRECTORY or ACCOUNT\n")); + d_printf(_("\tpolicy can be one of: SUCCESS, FAILURE, ALL or NONE\n\n")); + + return -1; +} + +/******************************************************************** +********************************************************************/ + +static void print_auditing_category(const char *policy, const char *value) +{ + if (policy == NULL) { + policy = N_("Unknown"); + } + if (value == NULL) { + value = N_("Invalid"); + } + + d_printf(_("\t%-30s%s\n"), policy, value); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_get_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle pol; + NTSTATUS status, result; + union lsa_PolicyInformation *info = NULL; + int i; + uint32_t audit_category; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc < 1 || argc > 2) { + d_printf(_("insufficient arguments\n")); + net_help_audit(c, argc, argv); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!get_audit_category_from_param(argv[0], &audit_category)) { + d_printf(_("invalid auditing category: %s\n"), argv[0]); + return NT_STATUS_INVALID_PARAMETER; + } + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + for (i=0; i < info->audit_events.count; i++) { + + const char *val = NULL, *policy = NULL; + + if (i != audit_category) { + continue; + } + + val = audit_policy_str(mem_ctx, info->audit_events.settings[i]); + policy = audit_description_str(i); + print_auditing_category(policy, val); + } + + done: + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed to get auditing policy: %s\n"), + nt_errstr(status)); + } + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_set_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle pol; + NTSTATUS status, result; + union lsa_PolicyInformation *info = NULL; + uint32_t audit_policy, audit_category; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc < 2 || argc > 3) { + d_printf(_("insufficient arguments\n")); + net_help_audit(c, argc, argv); + return NT_STATUS_INVALID_PARAMETER; + } + + if (!get_audit_category_from_param(argv[0], &audit_category)) { + d_printf(_("invalid auditing category: %s\n"), argv[0]); + return NT_STATUS_INVALID_PARAMETER; + } + + audit_policy = LSA_AUDIT_POLICY_CLEAR; + + if (strequal(argv[1], "Success")) { + audit_policy |= LSA_AUDIT_POLICY_SUCCESS; + } else if (strequal(argv[1], "Failure")) { + audit_policy |= LSA_AUDIT_POLICY_FAILURE; + } else if (strequal(argv[1], "All")) { + audit_policy |= LSA_AUDIT_POLICY_ALL; + } else if (strequal(argv[1], "None")) { + audit_policy = LSA_AUDIT_POLICY_CLEAR; + } else { + d_printf(_("invalid auditing policy: %s\n"), argv[1]); + return NT_STATUS_INVALID_PARAMETER; + } + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + info->audit_events.settings[audit_category] = audit_policy; + + status = dcerpc_lsa_SetInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = result; + + { + const char *val = audit_policy_str(mem_ctx, info->audit_events.settings[audit_category]); + const char *policy = audit_description_str(audit_category); + print_auditing_category(policy, val); + } + + done: + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed to set audit policy: %s\n"), + nt_errstr(status)); + } + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_enable_internal_ext(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv, + bool enable) +{ + struct policy_handle pol; + NTSTATUS status, result; + union lsa_PolicyInformation *info = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + info->audit_events.auditing_mode = enable; + + status = dcerpc_lsa_SetInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + done: + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("%s: %s\n"), + enable ? _("failed to enable audit policy"): + _("failed to disable audit policy"), + nt_errstr(status)); + } + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_disable_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_audit_enable_internal_ext(pipe_hnd, mem_ctx, argc, argv, + false); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_enable_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_audit_enable_internal_ext(pipe_hnd, mem_ctx, argc, argv, + true); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_audit_list_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle pol; + NTSTATUS status, result; + union lsa_PolicyInformation *info = NULL; + int i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_AUDIT_EVENTS, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + printf(_("Auditing:\t\t")); + switch (info->audit_events.auditing_mode) { + case true: + printf(_("Enabled")); + break; + case false: + printf(_("Disabled")); + break; + default: + printf(_("unknown (%d)"), + info->audit_events.auditing_mode); + break; + } + printf("\n"); + + printf(_("Auditing categories:\t%d\n"), info->audit_events.count); + printf(_("Auditing settings:\n")); + + for (i=0; i < info->audit_events.count; i++) { + const char *val = audit_policy_str(mem_ctx, info->audit_events.settings[i]); + const char *policy = audit_description_str(i); + print_auditing_category(policy, val); + } + + done: + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("failed to list auditing policies: %s\n"), + nt_errstr(status)); + } + + return status; +} + +/******************************************************************** +********************************************************************/ + +static int rpc_audit_get(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc audit get\n" + " %s\n", + _("Usage:"), + _("View configured audit setting")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_audit_get_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_audit_set(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc audit set\n" + " %s\n", + _("Usage:"), + _("Set audit policies")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_audit_set_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_audit_enable(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc audit enable\n" + " %s\n", + _("Usage:"), + _("Enable auditing")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_audit_enable_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_audit_disable(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc audit disable\n" + " %s\n", + _("Usage:"), + _("Disable auditing")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_audit_disable_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_audit_list(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc audit list\n" + " %s\n", + _("Usage:"), + _("List auditing settings")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_audit_list_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +int net_rpc_audit(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "get", + rpc_audit_get, + NET_TRANSPORT_RPC, + N_("View configured auditing settings"), + N_("net rpc audit get\n" + " View configured auditing settings") + }, + { + "set", + rpc_audit_set, + NET_TRANSPORT_RPC, + N_("Set auditing policies"), + N_("net rpc audit set\n" + " Set auditing policies") + }, + { + "enable", + rpc_audit_enable, + NET_TRANSPORT_RPC, + N_("Enable auditing"), + N_("net rpc audit enable\n" + " Enable auditing") + }, + { + "disable", + rpc_audit_disable, + NET_TRANSPORT_RPC, + N_("Disable auditing"), + N_("net rpc audit disable\n" + " Disable auditing") + }, + { + "list", + rpc_audit_list, + NET_TRANSPORT_RPC, + N_("List configured auditing settings"), + N_("net rpc audit list\n" + " List configured auditing settings") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc audit", func); +} diff --git a/source3/utils/net_rpc_conf.c b/source3/utils/net_rpc_conf.c new file mode 100644 index 0000000..25a7b43 --- /dev/null +++ b/source3/utils/net_rpc_conf.c @@ -0,0 +1,2483 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Local configuration interface + * Copyright (C) Vicentiu Ciorbaru 2011 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * This is an interface to Samba's configuration. + * + * This tool supports local as well as remote interaction via rpc + * with the configuration stored in the registry. + */ + + +#include "includes.h" +#include "utils/net.h" +#include "utils/net_conf_util.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "rpc_client/init_samr.h" +#include "../librpc/gen_ndr/ndr_winreg_c.h" +#include "../libcli/registry/util_reg.h" +#include "rpc_client/cli_winreg.h" +#include "lib/smbconf/smbconf.h" +#include "lib/smbconf/smbconf_init.h" +#include "lib/smbconf/smbconf_reg.h" +#include "lib/param/loadparm.h" + + + +/* internal functions */ +/********************************************************** + * + * usage functions + * + **********************************************************/ +const char confpath[100] = "Software\\Samba\\smbconf"; + +static int rpc_conf_list_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s net rpc conf list\n", _("Usage:")); + return -1; +} + +static int rpc_conf_listshares_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s net rpc conf listshares\n", _("Usage:")); + return -1; +} + +static int rpc_conf_delshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net rpc conf delshare <sharename>\n")); + return -1; +} + +static int rpc_conf_addshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc conf addshare <sharename> <path> " + "[writeable={y|N} [guest_ok={y|N} [<comment>]]]\n" + "\t<sharename> the new share name.\n" + "\t<path> the path on the filesystem to export.\n" + "\twriteable={y|N} set \"writeable to \"yes\" or " + "\"no\" (default) on this share.\n" + "\tguest_ok={y|N} set \"guest ok\" to \"yes\" or " + "\"no\" (default) on this share.\n" + "\t<comment> optional comment for the new share.\n")); + return -1; + +} + +static int rpc_conf_import_usage(struct net_context *c, int argc, + const char**argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc conf import [--test|-T] <filename> " + "[<servicename>]\n" + "\t[--test|-T] testmode - do not act, just print " + "what would be done\n" + "\t<servicename> only import service <servicename>, " + "ignore the rest\n")); + return -1; +} + +static int rpc_conf_showshare_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _("net rpc conf showshare <sharename>\n")); + return -1; +} + +static int rpc_conf_drop_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf drop\n", _("Usage:")); + return -1; +} + +static int rpc_conf_getparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf getparm <sharename> <parameter>\n", + _("Usage:")); + return -1; +} + +static int rpc_conf_setparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc conf setparm <section> <param> <value>\n")); + return -1; +} + +static int rpc_conf_delparm_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf delparm <sharename> <parameter>\n", + _("Usage:")); + return -1; +} + +static int rpc_conf_getincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf getincludes <sharename>\n", + _("Usage:")); + return -1; +} + +static int rpc_conf_setincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf setincludes <sharename> [<filename>]*\n", + _("Usage:")); + return -1; +} + +static int rpc_conf_delincludes_usage(struct net_context *c, int argc, + const char **argv) +{ + d_printf("%s\nnet rpc conf delincludes <sharename>\n", + _("Usage:")); + return -1; +} + +/********************************************************** + * + * helper functions + * + **********************************************************/ + +/* + * The function deletes a registry value with the name 'value' from the share + * with the name 'share_name'. 'parent_hnd' is the handle for the smbconf key. + */ +static NTSTATUS rpc_conf_del_value(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *b, + struct policy_handle *parent_hnd, + const char *share_name, + const char *value, + WERROR *werr) +{ + + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR result = WERR_OK; + WERROR _werr; + + struct winreg_String keyname, valuename; + struct policy_handle child_hnd; + + ZERO_STRUCT(child_hnd); + ZERO_STRUCT(keyname); + ZERO_STRUCT(valuename); + + keyname.name = share_name; + valuename.name = value; + + status = dcerpc_winreg_OpenKey(b, frame, parent_hnd, keyname, 0, + REG_KEY_WRITE, &child_hnd, &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to open key '%s': %s\n"), + keyname.name, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, _("Failed to open key '%s': %s\n"), + keyname.name, win_errstr(result)); + goto error; + } + + status = dcerpc_winreg_DeleteValue(b, + frame, + &child_hnd, + valuename, + &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to delete value %s\n"), + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(result))) { + if (W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)){ + result = WERR_OK; + goto error; + } + + d_fprintf(stderr, _("Failed to delete value %s\n"), + win_errstr(result)); + goto error; + } + +error: + *werr = result; + + dcerpc_winreg_CloseKey(b, frame, &child_hnd, &_werr); + + TALLOC_FREE(frame); + return status;; + +} + +/* + * The function sets a share in the registry with the parameters + * held in the smbconf_service struct + */ +static NTSTATUS rpc_conf_set_share(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *b, + struct policy_handle *parent_hnd, + struct smbconf_service *service, + WERROR *werr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + NTSTATUS status = NT_STATUS_OK; + WERROR result = WERR_OK; + WERROR _werr; + enum winreg_CreateAction action; + uint32_t i, j; + + const char **includes; + + struct winreg_String wkey, wkeyclass; + struct policy_handle share_hnd; + + ZERO_STRUCT(share_hnd); + ZERO_STRUCT(wkey); + ZERO_STRUCT(wkeyclass); + + wkey.name = service->name; + wkeyclass.name = ""; + action = REG_ACTION_NONE; + + status = dcerpc_winreg_CreateKey(b, + frame, + parent_hnd, + wkey, + wkeyclass, + 0, + REG_KEY_ALL, + NULL, + &share_hnd, + &action, + &result); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("winreg_CreateKey: Could not create smbconf key\n"); + goto error; + } + + if (!W_ERROR_IS_OK(result)) { + d_printf("winreg_CreateKey: Could not create smbconf key\n"); + goto error; + } + + for (i = 0; i < service->num_params; i++) { + if (strequal(service->param_names[i], "include") == 0) + { + + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + service->param_names[i], + service->param_values[i], + &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, + "ERROR: Share: '%s'\n" + "Could not set parameter '%s'" + " with value %s\n %s\n", + service->name, + service->param_names[i], + service->param_values[i], + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, + "ERROR: Share: '%s'\n" + "Could not set parameter '%s'" + " with value %s\n %s\n", + service->name, + service->param_names[i], + service->param_values[i], + win_errstr(result)); + goto error; + } + } else { + + includes = talloc_zero_array(frame, + const char *, + service->num_params + 1); + if (includes == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, "ERROR: out of memory\n"); + goto error; + } + + for (j = i; j < service->num_params; j++) { + + includes[j - i] = talloc_strdup( + frame, + service->param_values[j]); + + if (includes[j-i] == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, "ERROR: out of memory\n"); + goto error; + } + } + + status = dcerpc_winreg_set_multi_sz(frame, b, &share_hnd, + "includes", + includes, + &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Share: '%s'\n" + "Could not set includes\n %s\n", + service->name, + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, "ERROR: Share: '%s'\n" + "Could not set includes\n %s\n", + service->name, + win_errstr(result)); + goto error; + } + + i = service->num_params; + } + } + +error: + /* in case of error, should it delete the created key? */ + if (!(W_ERROR_IS_OK(result))) { + status = werror_to_ntstatus(result); + + } + + dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +/* + * The function opens the registry database and retrieves + * as a smbconf_service struct the share with the name + * 'share_name' + */ +static NTSTATUS rpc_conf_get_share(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *b, + struct policy_handle *parent_hnd, + const char *share_name, + struct smbconf_service *share, + WERROR *werr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + NTSTATUS status = NT_STATUS_OK; + WERROR result = WERR_OK; + WERROR _werr; + struct policy_handle child_hnd; + int32_t includes_cnt, includes_idx = -1; + uint32_t num_vals, num_subkeys, i, param_cnt = 0; + const char **val_names; + const char **subkeys = NULL; + enum winreg_Type *types; + DATA_BLOB *data; + struct winreg_String key = { 0, }; + const char **multi_s = NULL; + const char *s = NULL; + struct smbconf_service tmp_share; + + ZERO_STRUCT(tmp_share); + + /* + * Determine correct upper/lowercase. + */ + status = dcerpc_winreg_enum_keys(frame, + b, + parent_hnd, + &num_subkeys, + &subkeys, + &result); + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to enumerate shares: %s\n"), + nt_errstr(status)); + goto error; + } + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, _("Failed to enumerate shares: %s\n"), + win_errstr(result)); + goto error; + } + + for (i = 0; i < num_subkeys; i++) { + if (!strequal(share_name, subkeys[i])) { + continue; + } + + key.name = subkeys[i]; + } + + if (key.name == NULL) { + d_fprintf(stderr, _("Could not find share.\n")); + goto error; + } + + status = dcerpc_winreg_OpenKey(b, frame, parent_hnd, key, 0, + REG_KEY_READ, &child_hnd, &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to open subkey: %s\n"), + nt_errstr(status)); + goto error; + } + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, _("Failed to open subkey: %s\n"), + win_errstr(result)); + goto error; + } + /* get all the info from the share key */ + status = dcerpc_winreg_enumvals(frame, + b, + &child_hnd, + &num_vals, + &val_names, + &types, + &data, + &result); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to enumerate values: %s\n"), + nt_errstr(status)); + goto error; + } + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, _("Failed to enumerate values: %s\n"), + win_errstr(result)); + goto error; + } + /* check for includes */ + for (i = 0; i < num_vals; i++) { + if (strcmp(val_names[i], "includes") == 0){ + if (!pull_reg_multi_sz(frame, + &data[i], + &multi_s)) + { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, + _("Failed to enumerate values: %s\n"), + win_errstr(result)); + goto error; + } + includes_idx = i; + } + } + /* count the number of includes */ + includes_cnt = 0; + if (includes_idx != -1) { + for (includes_cnt = 0; + multi_s[includes_cnt] != NULL; + includes_cnt ++); + } + /* place the name of the share in the smbconf_service struct */ + tmp_share.name = talloc_strdup(frame, key.name); + if (tmp_share.name == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + /* place the number of parameters in the smbconf_service struct */ + tmp_share.num_params = num_vals; + if (includes_idx != -1) { + tmp_share.num_params = num_vals + includes_cnt - 1; + } + /* allocate memory for the param_names and param_values lists */ + tmp_share.param_names = talloc_zero_array(frame, char *, tmp_share.num_params); + if (tmp_share.param_names == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + tmp_share.param_values = talloc_zero_array(frame, char *, tmp_share.num_params); + if (tmp_share.param_values == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + /* place all params except includes */ + for (i = 0; i < num_vals; i++) { + if (strcmp(val_names[i], "includes") != 0) { + if (!pull_reg_sz(frame, &data[i], &s)) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, + _("Failed to enumerate values: %s\n"), + win_errstr(result)); + goto error; + } + /* place param_names */ + tmp_share.param_names[param_cnt] = talloc_strdup(frame, val_names[i]); + if (tmp_share.param_names[param_cnt] == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + + /* place param_values */ + tmp_share.param_values[param_cnt++] = talloc_strdup(frame, s); + if (tmp_share.param_values[param_cnt - 1] == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + } + } + /* place the includes last */ + for (i = 0; i < includes_cnt; i++) { + tmp_share.param_names[param_cnt] = talloc_strdup(frame, "include"); + if (tmp_share.param_names[param_cnt] == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + + tmp_share.param_values[param_cnt++] = talloc_strdup(frame, multi_s[i]); + if (tmp_share.param_values[param_cnt - 1] == NULL) { + result = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(result)); + goto error; + } + } + + /* move everything to the main memory ctx */ + for (i = 0; i < param_cnt; i++) { + tmp_share.param_names[i] = talloc_move(mem_ctx, &tmp_share.param_names[i]); + tmp_share.param_values[i] = talloc_move(mem_ctx, &tmp_share.param_values[i]); + } + + tmp_share.name = talloc_move(mem_ctx, &tmp_share.name); + tmp_share.param_names = talloc_move(mem_ctx, &tmp_share.param_names); + tmp_share.param_values = talloc_move(mem_ctx, &tmp_share.param_values); + /* out parameter */ + *share = tmp_share; +error: + /* close child */ + dcerpc_winreg_CloseKey(b, frame, &child_hnd, &_werr); + *werr = result; + TALLOC_FREE(frame); + return status; +} + +/* + * The function prints the shares held as smbconf_service structs + * in a smbconf file format. + */ +static int rpc_conf_print_shares(uint32_t num_shares, + struct smbconf_service *shares) +{ + + uint32_t share_count, param_count; + const char *indent = "\t"; + + if (num_shares == 0) { + return 0; + } + + for (share_count = 0; share_count < num_shares; share_count++) { + d_printf("\n"); + if (shares[share_count].name != NULL) { + d_printf("[%s]\n", shares[share_count].name); + } + + for (param_count = 0; + param_count < shares[share_count].num_params; + param_count++) + { + d_printf("%s%s = %s\n", + indent, + shares[share_count].param_names[param_count], + shares[share_count].param_values[param_count]); + } + } + d_printf("\n"); + + return 0; + +} + +/* + * The function opens the registry key + * HKLM/Software/Samba/smbconf with the give access_mask + */ +static NTSTATUS rpc_conf_open_conf(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *b, + uint32_t access_mask, + struct policy_handle *hive_hnd, + struct policy_handle *key_hnd, + WERROR *werr) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR result = WERR_OK; + WERROR _werr; + struct policy_handle tmp_hive_hnd, tmp_key_hnd; + struct winreg_String key; + + ZERO_STRUCT(key); + + status = dcerpc_winreg_OpenHKLM(b, frame, NULL, + access_mask, &tmp_hive_hnd, &result); + + /* + * print no error messages if it is a read only open + * and key does not exist + * error still gets returned + */ + + if (access_mask == REG_KEY_READ && + W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) + { + goto error; + } + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to open hive: %s\n"), + nt_errstr(status)); + goto error; + } + if (!W_ERROR_IS_OK(result)) { + d_fprintf(stderr, _("Failed to open hive: %s\n"), + win_errstr(result)); + goto error; + } + + key.name = confpath; + status = dcerpc_winreg_OpenKey(b, frame, &tmp_hive_hnd, key, 0, + access_mask, &tmp_key_hnd, &result); + + /* + * print no error messages if it is a read only open + * and key does not exist + * error still gets returned + */ + + if (access_mask == REG_KEY_READ && + W_ERROR_EQUAL(result, WERR_FILE_NOT_FOUND)) + { + goto error; + } + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to open smbconf key: %s\n"), + nt_errstr(status)); + dcerpc_winreg_CloseKey(b, frame, &tmp_hive_hnd, &_werr); + goto error; + } + if (!(W_ERROR_IS_OK(result))) { + d_fprintf(stderr, _("Failed to open smbconf key: %s\n"), + win_errstr(result)); + dcerpc_winreg_CloseKey(b, frame, &tmp_hive_hnd, &_werr); + goto error; + } + + *hive_hnd = tmp_hive_hnd; + *key_hnd = tmp_key_hnd; + +error: + TALLOC_FREE(frame); + *werr = result; + + return status; +} + +/********************************************************** + * + * internal functions that provide the functionality + * net rpc conf + * + **********************************************************/ + +static NTSTATUS rpc_conf_listshares_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + uint32_t num_subkeys; + uint32_t i; + const char **subkeys = NULL; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 0 || c->display_usage) { + rpc_conf_listshares_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = dcerpc_winreg_enum_keys(frame, + b, + &key_hnd, + &num_subkeys, + &subkeys, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to enumerate keys: %s\n"), + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, _("Failed to enumerate keys: %s\n"), + win_errstr(werr)); + goto error; + } + + for (i = 0; i < num_subkeys; i++) { + d_printf("%s\n", subkeys[i]); + } + +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status;; +} + +static NTSTATUS rpc_conf_delshare_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 1 || c->display_usage) { + rpc_conf_delshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_ALL, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = dcerpc_winreg_delete_subkeys_recursive(frame, + b, + &key_hnd, + REG_KEY_ALL, + argv[0], + &werr); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + "winreg_delete_subkeys: Could not delete key %s: %s\n", + argv[0], nt_errstr(status)); + goto error; + } + + if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)){ + d_fprintf(stderr, _("ERROR: Key does not exist\n")); + } + + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, + "winreg_delete_subkeys: Could not delete key %s: %s\n", + argv[0], win_errstr(werr)); + goto error; + } + +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + + return status; +} + +static NTSTATUS rpc_conf_list_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + uint32_t num_subkeys; + uint32_t i; + struct smbconf_service *shares; + const char **subkeys = NULL; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 0 || c->display_usage) { + rpc_conf_list_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = dcerpc_winreg_enum_keys(frame, + b, + &key_hnd, + &num_subkeys, + &subkeys, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("Failed to enumerate keys: %s\n"), + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, _("Failed to enumerate keys: %s\n"), + win_errstr(werr)); + goto error; + } + + if (num_subkeys == 0) { + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + /* get info from each subkey */ + shares = talloc_zero_array(frame, struct smbconf_service, num_subkeys); + if (shares == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create shares: %s\n"), + win_errstr(werr)); + goto error; + + } + + for (i = 0; i < num_subkeys; i++) { + /* get each share and place it in the shares array */ + status = rpc_conf_get_share(frame, + b, + &key_hnd, + subkeys[i], + &shares[i], + &werr); + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + } + /* print the shares array */ + rpc_conf_print_shares(num_subkeys, shares); + +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +static NTSTATUS rpc_conf_drop_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + const char *keyname = confpath; + struct winreg_String wkey, wkeyclass; + enum winreg_CreateAction action = REG_ACTION_NONE; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 0 || c->display_usage) { + rpc_conf_drop_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_ALL, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = dcerpc_winreg_delete_subkeys_recursive(frame, + b, + &hive_hnd, + REG_KEY_ALL, + keyname, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("winreg_delete_subkeys: Could not delete key %s: %s\n", + keyname, nt_errstr(status)); + goto error; + } + + if (!W_ERROR_IS_OK(werr)) { + d_printf("winreg_delete_subkeys: Could not delete key %s: %s\n", + keyname, win_errstr(werr)); + goto error; + } + + ZERO_STRUCT(wkey); + wkey.name = keyname; + ZERO_STRUCT(wkeyclass); + wkeyclass.name = ""; + action = REG_ACTION_NONE; + + status = dcerpc_winreg_CreateKey(b, + frame, + &hive_hnd, + wkey, + wkeyclass, + 0, + REG_KEY_ALL, + NULL, + &key_hnd, + &action, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + d_printf("winreg_CreateKey: Could not create smbconf key\n"); + goto error; + } + + if (!W_ERROR_IS_OK(werr)) { + d_printf("winreg_CreateKey: Could not create smbconf key\n"); + goto error; + } + + +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_import_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + struct policy_handle hive_hnd, key_hnd; + + const char *filename = NULL; + const char *servicename = NULL; + char *conf_source = NULL; + TALLOC_CTX *frame; + struct smbconf_ctx *txt_ctx; + struct smbconf_service *service = NULL; + struct smbconf_service **services = NULL; + uint32_t num_shares, i; + sbcErr err = SBC_ERR_UNKNOWN_FAILURE; + + WERROR werr = WERR_OK; + NTSTATUS status = NT_STATUS_OK; + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + frame = talloc_stackframe(); + + if (c->display_usage) { + rpc_conf_import_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + switch (argc) { + case 0: + default: + rpc_conf_import_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + case 2: + servicename = talloc_strdup(frame, argv[1]); + if (servicename == NULL) { + d_printf(_("error: out of memory!\n")); + goto error; + } + + FALL_THROUGH; + case 1: + filename = argv[0]; + break; + } + + DEBUG(3,("rpc_conf_import: reading configuration from file %s.\n", + filename)); + + conf_source = talloc_asprintf(frame, "file:%s", filename); + if (conf_source == NULL) { + d_fprintf(stderr, _("error: out of memory!\n")); + goto error; + } + + err = smbconf_init(frame, &txt_ctx, conf_source); + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, _("error loading file '%s': %s\n"), filename, + sbcErrorString(err)); + goto error; + } + + if (c->opt_testmode) { + d_printf(_("\nTEST MODE - " + "would import the following configuration:\n\n")); + } + + if (servicename != NULL) { + err = smbconf_get_share(txt_ctx, frame, + servicename, + &service); + if (!SBC_ERROR_IS_OK(err)) { + goto error; + } + + num_shares = 1; + + } else { + + err = smbconf_get_config(txt_ctx, frame, + &num_shares, + &services); + if (!SBC_ERROR_IS_OK(err)) { + goto error; + } + } + + if (c->opt_testmode) { + if (servicename != NULL) { + rpc_conf_print_shares(1, service); + } + for (i = 0; i < num_shares; i++) { + rpc_conf_print_shares(1, services[i]); + } + goto error; + } + + status = rpc_conf_drop_internal(c, + domain_sid, + domain_name, + cli, + pipe_hnd, + frame, + 0, + NULL ); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + if (servicename != NULL) { + status = rpc_conf_set_share(frame, + b, + &key_hnd, + service, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + } else { + + for (i = 0; i < num_shares; i++) { + status = rpc_conf_set_share(frame, + b, + &key_hnd, + services[i], + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + } + } + +error: + if (!SBC_ERROR_IS_OK(err)) { + d_fprintf(stderr, "ERROR: %s\n", sbcErrorString(err)); + } + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_showshare_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + struct smbconf_service *service = NULL; + const char *sharename = NULL; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 1 || c->display_usage) { + rpc_conf_showshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + sharename = talloc_strdup(frame, argv[0]); + if (sharename == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(werr)); + goto error; + } + + service = talloc(frame, struct smbconf_service); + if (service == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + d_fprintf(stderr, _("Failed to create share: %s\n"), + win_errstr(werr)); + goto error; + } + + status = rpc_conf_get_share(frame, + b, + &key_hnd, + sharename, + service, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + rpc_conf_print_shares(1, service); + +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_addshare_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd, share_hnd; + char *sharename = NULL; + const char *path = NULL; + const char *comment = NULL; + const char *guest_ok = "no"; + const char *read_only = "yes"; + struct winreg_String key, keyclass; + enum winreg_CreateAction action = 0; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + ZERO_STRUCT(share_hnd); + + ZERO_STRUCT(key); + ZERO_STRUCT(keyclass); + + if (c->display_usage) { + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + switch (argc) { + case 0: + case 1: + default: + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + case 5: + comment = argv[4]; + + FALL_THROUGH; + case 4: + if (!strnequal(argv[3], "guest_ok=", 9)) { + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + switch (argv[3][9]) { + case 'y': + case 'Y': + guest_ok = "yes"; + break; + case 'n': + case 'N': + guest_ok = "no"; + break; + default: + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + FALL_THROUGH; + case 3: + if (!strnequal(argv[2], "writeable=", 10)) { + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + switch (argv[2][10]) { + case 'y': + case 'Y': + read_only = "no"; + break; + case 'n': + case 'N': + read_only = "yes"; + break; + default: + rpc_conf_addshare_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + FALL_THROUGH; + case 2: + path = argv[1]; + sharename = talloc_strdup(frame, argv[0]); + if (sharename == NULL) { + d_printf(_("error: out of memory!\n")); + goto error; + } + + break; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + key.name = argv[0]; + keyclass.name = ""; + + status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass, + 0, REG_KEY_READ, NULL, &share_hnd, + &action, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], nt_errstr(status)); + goto error; + } + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], win_errstr(werr)); + goto error; + } + + switch (action) { + case REG_ACTION_NONE: + werr = WERR_CREATE_FAILED; + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], win_errstr(werr)); + goto error; + case REG_CREATED_NEW_KEY: + DEBUG(5, ("net rpc conf setincludes:" + "createkey created %s\n", argv[0])); + break; + case REG_OPENED_EXISTING_KEY: + d_fprintf(stderr, _("ERROR: Share '%s' already exists\n"), argv[0]); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + /* set the path parameter */ + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + "path", path, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "path", path, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "path", path, win_errstr(werr)); + goto error; + } + + /* set the writeable parameter */ + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + "read only", read_only, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "read only", read_only, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "read only", read_only, win_errstr(werr)); + goto error; + } + + /* set the guest ok parameter */ + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + "guest ok", guest_ok, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "guest ok", guest_ok, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "guest ok", guest_ok, win_errstr(werr)); + goto error; + } + + if (argc == 5) { + /* set the comment parameter */ + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + "comment", comment, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "comment", comment, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + "comment", comment, win_errstr(werr)); + goto error; + } + } +error: + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr); + + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_getparm_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + struct smbconf_service *service = NULL; + + bool param_is_set = false; + uint32_t param_count; + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 2 || c->display_usage) { + rpc_conf_getparm_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + + service = talloc(frame, struct smbconf_service); + + status = rpc_conf_get_share(frame, + b, + &key_hnd, + argv[0], + service, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (W_ERROR_EQUAL(werr, WERR_FILE_NOT_FOUND)) { + d_fprintf(stderr, _("ERROR: Share %s does not exist\n"), + argv[0]); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + for (param_count = 0; + param_count < service->num_params; + param_count++) + { + /* should includes also be printed? */ + if (strcmp(service->param_names[param_count], argv[1]) == 0) { + d_printf(_("%s\n"), + service->param_values[param_count]); + param_is_set = true; + } + } + + if (!param_is_set) { + d_fprintf(stderr, _("ERROR: Given parameter '%s' has not been set\n"), + argv[1]); + werr = WERR_FILE_NOT_FOUND; + goto error; + } + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +static NTSTATUS rpc_conf_setparm_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd, share_hnd; + + struct winreg_String key, keyclass; + enum winreg_CreateAction action = 0; + + const char *service_name, *param_name, *valstr; + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + ZERO_STRUCT(share_hnd); + + ZERO_STRUCT(key); + ZERO_STRUCT(keyclass); + + if (argc != 3 || c->display_usage) { + rpc_conf_setparm_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + service_name = argv[0]; + param_name = argv[1]; + valstr = argv[2]; + + key.name = service_name; + keyclass.name = ""; + + status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass, + 0, REG_KEY_READ, NULL, &share_hnd, + &action, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + service_name, nt_errstr(status)); + goto error; + } + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + service_name, win_errstr(werr)); + goto error; + } + + switch (action) { + case REG_ACTION_NONE: + werr = WERR_CREATE_FAILED; + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + service_name, win_errstr(werr)); + goto error; + case REG_CREATED_NEW_KEY: + DEBUG(5, ("net rpc conf setparm:" + "createkey created %s\n", service_name)); + break; + case REG_OPENED_EXISTING_KEY: + DEBUG(5, ("net rpc conf setparm:" + "createkey opened existing %s\n", + service_name)); + + /* delete possibly existing value */ + status = rpc_conf_del_value(frame, + b, + &key_hnd, + service_name, + param_name, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + break; + } + + /* + * check if parameter is valid for writing + */ + + if (!net_conf_param_valid(service_name, param_name, valstr)) { + werr = WERR_INVALID_PARAMETER; + goto error; + } + + /* set the parameter */ + status = dcerpc_winreg_set_sz(frame, b, &share_hnd, + param_name, valstr, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + param_name, valstr, nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set parameter '%s'" + " with value %s\n %s\n", + param_name, valstr, win_errstr(werr)); + goto error; + } + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr); + + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_delparm_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 2 || c->display_usage) { + rpc_conf_delparm_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = rpc_conf_del_value(frame, + b, + &key_hnd, + argv[0], + argv[1], + &werr); + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +static NTSTATUS rpc_conf_getincludes_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + struct smbconf_service *service = NULL; + + uint32_t param_count; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 1 || c->display_usage) { + rpc_conf_getincludes_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + service = talloc(frame, struct smbconf_service); + + status = rpc_conf_get_share(frame, + b, + &key_hnd, + argv[0], + service, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + for (param_count = 0; + param_count < service->num_params; + param_count++) + { + if (strcmp(service->param_names[param_count], "include") == 0) { + d_printf(_("%s = %s\n"), + service->param_names[param_count], + service->param_values[param_count]); + } + } + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +static NTSTATUS rpc_conf_setincludes_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd, share_hnd; + + struct winreg_String key, keyclass; + enum winreg_CreateAction action = 0; + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + ZERO_STRUCT(share_hnd); + + ZERO_STRUCT(key); + ZERO_STRUCT(keyclass); + + if (argc < 1 || c->display_usage) { + rpc_conf_setincludes_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + key.name = argv[0]; + keyclass.name = ""; + + status = dcerpc_winreg_CreateKey(b, frame, &key_hnd, key, keyclass, + 0, REG_KEY_READ, NULL, &share_hnd, + &action, &werr); + + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], nt_errstr(status)); + goto error; + } + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], win_errstr(werr)); + goto error; + } + + switch (action) { + case REG_ACTION_NONE: + /* Is there any other way to treat this? */ + werr = WERR_CREATE_FAILED; + d_fprintf(stderr, _("ERROR: Could not create share key '%s'\n%s\n"), + argv[0], win_errstr(werr)); + goto error; + case REG_CREATED_NEW_KEY: + DEBUG(5, ("net rpc conf setincludes:" + "createkey created %s\n", argv[0])); + break; + case REG_OPENED_EXISTING_KEY: + DEBUG(5, ("net rpc conf setincludes:" + "createkey opened existing %s\n", argv[0])); + + /* delete possibly existing value */ + status = rpc_conf_del_value(frame, + b, + &key_hnd, + argv[0], + "includes", + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + break; + } + + /* set the 'includes' values */ + status = dcerpc_winreg_set_multi_sz(frame, b, &share_hnd, + "includes", argv + 1, &werr); + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, "ERROR: Could not set includes\n %s\n", + nt_errstr(status)); + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + d_fprintf(stderr, "ERROR: Could not set includes\n %s\n", + win_errstr(werr)); + goto error; + } + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &share_hnd, &_werr); + + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS rpc_conf_delincludes_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status = NT_STATUS_OK; + WERROR werr = WERR_OK; + WERROR _werr; + + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* key info */ + struct policy_handle hive_hnd, key_hnd; + + + ZERO_STRUCT(hive_hnd); + ZERO_STRUCT(key_hnd); + + + if (argc != 1 || c->display_usage) { + rpc_conf_delincludes_usage(c, argc, argv); + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + status = rpc_conf_open_conf(frame, + b, + REG_KEY_READ, + &hive_hnd, + &key_hnd, + &werr); + + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if (!(W_ERROR_IS_OK(werr))) { + goto error; + } + + status = rpc_conf_del_value(frame, + b, + &key_hnd, + argv[0], + "includes", + &werr); + +error: + + if (!(W_ERROR_IS_OK(werr))) { + status = werror_to_ntstatus(werr); + } + + dcerpc_winreg_CloseKey(b, frame, &hive_hnd, &_werr); + dcerpc_winreg_CloseKey(b, frame, &key_hnd, &_werr); + + TALLOC_FREE(frame); + return status; + +} + +/********************************************************** + * + * Functions that run the rpc commands for net rpc conf modules + * + **********************************************************/ + +static int rpc_conf_drop(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_drop_internal, argc, argv ); + +} + +static int rpc_conf_showshare(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_showshare_internal, argc, argv ); +} + +static int rpc_conf_addshare(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_addshare_internal, argc, argv ); +} + +static int rpc_conf_listshares(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_listshares_internal, argc, argv ); +} + +static int rpc_conf_list(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_list_internal, argc, argv ); +} + +static int rpc_conf_import(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_import_internal, argc, argv ); +} +static int rpc_conf_delshare(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_delshare_internal, argc, argv ); +} + +static int rpc_conf_getparm(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_getparm_internal, argc, argv ); +} + +static int rpc_conf_setparm(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_setparm_internal, argc, argv ); +} +static int rpc_conf_delparm(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_delparm_internal, argc, argv ); +} + +static int rpc_conf_getincludes(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_getincludes_internal, argc, argv ); +} + +static int rpc_conf_setincludes(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_setincludes_internal, argc, argv ); +} + +static int rpc_conf_delincludes(struct net_context *c, int argc, + const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_conf_delincludes_internal, argc, argv ); +} + +/* function calls */ +int net_rpc_conf(struct net_context *c, int argc, + const char **argv) +{ + struct functable func_table[] = { + { + "list", + rpc_conf_list, + NET_TRANSPORT_RPC, + N_("Dump the complete remote configuration in smb.conf like " + "format."), + N_("net rpc conf list\n" + " Dump the complete remote configuration in smb.conf " + "like format.") + + }, + { + "import", + rpc_conf_import, + NET_TRANSPORT_RPC, + N_("Import configuration from file in smb.conf " + "format."), + N_("net rpc conf import\n" + " Import configuration from file in smb.conf " + "format.") + }, + { + "listshares", + rpc_conf_listshares, + NET_TRANSPORT_RPC, + N_("List the remote share names."), + N_("net rpc conf list\n" + " List the remote share names.") + + }, + { + "drop", + rpc_conf_drop, + NET_TRANSPORT_RPC, + N_("Delete the complete remote configuration."), + N_("net rpc conf drop\n" + " Delete the complete remote configuration.") + + }, + { + "showshare", + rpc_conf_showshare, + NET_TRANSPORT_RPC, + N_("Show the definition of a remote share."), + N_("net rpc conf showshare\n" + " Show the definition of a remote share.") + + }, + { + "addshare", + rpc_conf_addshare, + NET_TRANSPORT_RPC, + N_("Create a new remote share."), + N_("net rpc conf addshare\n" + " Create a new remote share.") + }, + { + "delshare", + rpc_conf_delshare, + NET_TRANSPORT_RPC, + N_("Delete a remote share."), + N_("net rpc conf delshare\n" + " Delete a remote share.") + }, + { + "getparm", + rpc_conf_getparm, + NET_TRANSPORT_RPC, + N_("Retrieve the value of a parameter."), + N_("net rpc conf getparm\n" + " Retrieve the value of a parameter.") + }, + { + "setparm", + rpc_conf_setparm, + NET_TRANSPORT_RPC, + N_("Store a parameter."), + N_("net rpc conf setparm\n" + " Store a parameter.") + }, + { + "delparm", + rpc_conf_delparm, + NET_TRANSPORT_RPC, + N_("Delete a parameter."), + N_("net rpc conf delparm\n" + " Delete a parameter.") + }, + { + "getincludes", + rpc_conf_getincludes, + NET_TRANSPORT_RPC, + N_("Show the includes of a share definition."), + N_("net rpc conf getincludes\n" + " Show the includes of a share definition.") + }, + { + "setincludes", + rpc_conf_setincludes, + NET_TRANSPORT_RPC, + N_("Set includes for a share."), + N_("net rpc conf setincludes\n" + " Set includes for a share.") + }, + { + "delincludes", + rpc_conf_delincludes, + NET_TRANSPORT_RPC, + N_("Delete includes from a share definition."), + N_("net rpc conf delincludes\n" + " Delete includes from a share definition.") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc conf", func_table); + +} diff --git a/source3/utils/net_rpc_printer.c b/source3/utils/net_rpc_printer.c new file mode 100644 index 0000000..f72203f --- /dev/null +++ b/source3/utils/net_rpc_printer.c @@ -0,0 +1,2624 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2004,2009 Guenther Deschner (gd@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "system/filesys.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_spoolss_c.h" +#include "rpc_client/cli_spoolss.h" +#include "rpc_client/init_spoolss.h" +#include "nt_printing.h" +#include "registry.h" +#include "../libcli/security/security.h" +#include "../libcli/registry/util_reg.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "../libcli/smb/smbXcli_base.h" +#include "auth/gensec/gensec.h" +#include "auth/credentials/credentials.h" +#include "lib/util/string_wrappers.h" + + +/** + * This display-printdriver-functions was borrowed from rpcclient/cmd_spoolss.c. + * It is here for debugging purpose and should be removed later on. + **/ + +/**************************************************************************** + Printer info level 3 display function. +****************************************************************************/ + +static void display_print_driver3(struct spoolss_DriverInfo3 *r) +{ + int i; + + if (!r) { + return; + } + + printf(_("Printer Driver Info 3:\n")); + printf(_("\tVersion: [%x]\n"), r->version); + printf(_("\tDriver Name: [%s]\n"), r->driver_name); + printf(_("\tArchitecture: [%s]\n"), r->architecture); + printf(_("\tDriver Path: [%s]\n"), r->driver_path); + printf(_("\tDatafile: [%s]\n"), r->data_file); + printf(_("\tConfigfile: [%s]\n\n"), r->config_file); + printf(_("\tHelpfile: [%s]\n\n"), r->help_file); + + for (i=0; r->dependent_files && r->dependent_files[i] != NULL; i++) { + printf(_("\tDependentfiles: [%s]\n"), r->dependent_files[i]); + } + + printf("\n"); + + printf(_("\tMonitorname: [%s]\n"), r->monitor_name); + printf(_("\tDefaultdatatype: [%s]\n\n"), r->default_datatype); +} + +static void display_reg_value(const char *subkey, const char *name, struct registry_value *value) +{ + const char *text; + + switch(value->type) { + case REG_DWORD: + if (value->data.length == sizeof(uint32_t)) { + d_printf(_("\t[%s:%s]: REG_DWORD: 0x%08x\n"), subkey, + name, IVAL(value->data.data,0)); + } else { + d_printf(_("\t[%s:%s]: REG_DWORD: <invalid>\n"), subkey, + name); + } + break; + + case REG_SZ: + pull_reg_sz(talloc_tos(), &value->data, &text); + if (!text) { + break; + } + d_printf(_("\t[%s:%s]: REG_SZ: %s\n"), subkey, name, text); + break; + + case REG_BINARY: + d_printf(_("\t[%s:%s]: REG_BINARY: unknown length value not " + "displayed\n"), + subkey, name); + break; + + case REG_MULTI_SZ: { + uint32_t i; + const char **values; + + if (!pull_reg_multi_sz(NULL, &value->data, &values)) { + d_printf("pull_reg_multi_sz failed\n"); + break; + } + + printf("%s: REG_MULTI_SZ: \n", name); + for (i=0; values[i] != NULL; i++) { + d_printf("%s\n", values[i]); + } + TALLOC_FREE(values); + break; + } + + default: + d_printf(_("\t%s: unknown type %d\n"), name, value->type); + } + +} + +/** + * Copies ACLs, DOS-attributes and timestamps from one + * file or directory from one connected share to another connected share + * + * @param c A net_context structure + * @param mem_ctx A talloc-context + * @param cli_share_src A connected cli_state + * @param cli_share_dst A connected cli_state + * @param src_file The source file-name + * @param dst_file The destination file-name + * @param copy_acls Whether to copy acls + * @param copy_attrs Whether to copy DOS attributes + * @param copy_timestamps Whether to preserve timestamps + * @param is_file Whether this file is a file or a dir + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS net_copy_fileattr(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *src_name, const char *dst_name, + bool copy_acls, bool copy_attrs, + bool copy_timestamps, bool is_file) +{ + NTSTATUS nt_status; + uint16_t fnum_src = 0; + uint16_t fnum_dst = 0; + struct security_descriptor *sd = NULL; + uint32_t attr = (uint32_t)-1; + struct timespec f_create_time = { .tv_nsec = SAMBA_UTIME_OMIT }; + struct timespec f_access_time = { .tv_nsec = SAMBA_UTIME_OMIT }; + struct timespec f_write_time = { .tv_nsec = SAMBA_UTIME_OMIT }; + struct timespec f_change_time = { .tv_nsec = SAMBA_UTIME_OMIT }; + + if (!copy_timestamps && !copy_acls && !copy_attrs) + return NT_STATUS_OK; + + /* open file/dir on the originating server */ + + DEBUGADD(3,("opening %s %s on originating server\n", + is_file?"file":"dir", src_name)); + + nt_status = cli_ntcreate(cli_share_src, src_name, 0, + READ_CONTROL_ACCESS, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, + 0x0, 0x0, &fnum_src, NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUGADD(0,("cannot open %s %s on originating server %s\n", + is_file?"file":"dir", src_name, nt_errstr(nt_status))); + goto out; + } + + if (copy_acls) { + /* get the security descriptor */ + nt_status = cli_query_secdesc(cli_share_src, fnum_src, + mem_ctx, &sd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("failed to get security descriptor: %s\n", + nt_errstr(nt_status))); + goto out; + } + + if (c->opt_verbose && DEBUGLEVEL >= 3) + display_sec_desc(sd); + } + + if (copy_attrs || copy_timestamps) { + + /* get file attributes */ + nt_status = cli_qfileinfo_basic( + cli_share_src, + fnum_src, + copy_attrs ? &attr : NULL, + NULL, /* size */ + copy_timestamps ? &f_create_time : NULL, + copy_timestamps ? &f_access_time : NULL, + copy_timestamps ? &f_write_time : NULL, + copy_timestamps ? &f_change_time : NULL, + NULL); /* ino */ + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("failed to get file-attrs: %s\n", + nt_errstr(nt_status))); + goto out; + } + } + + /* open the file/dir on the destination server */ + nt_status = cli_ntcreate(cli_share_dst, dst_name, 0, + WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, + 0x0, 0x0, &fnum_dst, NULL); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("failed to open %s on the destination server: %s: %s\n", + is_file?"file":"dir", dst_name, nt_errstr(nt_status))); + goto out; + } + + if (copy_acls) { + /* set acls */ + nt_status = cli_set_secdesc(cli_share_dst, fnum_dst, sd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("could not set secdesc on %s: %s\n", + dst_name, nt_errstr(nt_status))); + goto out; + } + } + + if (copy_timestamps || copy_attrs) { + + nt_status = cli_setfileinfo_ext( + cli_share_dst, + fnum_dst, + f_create_time, + f_access_time, + f_write_time, + f_change_time, + attr); + if (!NT_STATUS_IS_OK(nt_status)) { + DBG_ERR("failed to set file-attrs: %s\n", + nt_errstr(nt_status)); + goto out; + } + } + + + /* closing files */ + nt_status = cli_close(cli_share_src, fnum_src); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("could not close %s on originating server: %s\n"), + is_file?"file":"dir", nt_errstr(nt_status)); + goto out; + } + + nt_status = cli_close(cli_share_dst, fnum_dst); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("could not close %s on destination server: %s\n"), + is_file?"file":"dir", nt_errstr(nt_status)); + goto out; + } + + + nt_status = NT_STATUS_OK; + +out: + + /* cleaning up */ + if (fnum_src) + cli_close(cli_share_src, fnum_src); + + if (fnum_dst) + cli_close(cli_share_dst, fnum_dst); + + return nt_status; +} + +/** + * Copy a file or directory from a connected share to another connected share + * + * @param c A net_context structure + * @param mem_ctx A talloc-context + * @param cli_share_src A connected cli_state + * @param cli_share_dst A connected cli_state + * @param src_file The source file-name + * @param dst_file The destination file-name + * @param copy_acls Whether to copy acls + * @param copy_attrs Whether to copy DOS attributes + * @param copy_timestamps Whether to preserve timestamps + * @param is_file Whether this file is a file or a dir + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS net_copy_file(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *src_name, const char *dst_name, + bool copy_acls, bool copy_attrs, + bool copy_timestamps, bool is_file) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint16_t fnum_src = 0; + uint16_t fnum_dst = 0; + static int io_bufsize = 64512; + int read_size = io_bufsize; + char *data = NULL; + off_t nread = 0; + + + if (!src_name || !dst_name) + goto out; + + if (cli_share_src == NULL || cli_share_dst == NULL) + goto out; + + /* open on the originating server */ + DEBUGADD(3,("opening %s %s on originating server\n", + is_file ? "file":"dir", src_name)); + if (is_file) + nt_status = cli_open(cli_share_src, src_name, O_RDONLY, DENY_NONE, &fnum_src); + else + nt_status = cli_ntcreate(cli_share_src, src_name, 0, READ_CONTROL_ACCESS, 0, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, 0x0, 0x0, &fnum_src, NULL); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUGADD(0,("cannot open %s %s on originating server %s\n", + is_file ? "file":"dir", + src_name, nt_errstr(nt_status))); + goto out; + } + + + if (is_file) { + + /* open file on the destination server */ + DEBUGADD(3,("opening file %s on destination server\n", dst_name)); + nt_status = cli_open(cli_share_dst, dst_name, + O_RDWR|O_CREAT|O_TRUNC, DENY_NONE, &fnum_dst); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUGADD(1,("cannot create file %s on destination server: %s\n", + dst_name, nt_errstr(nt_status))); + goto out; + } + + /* allocate memory */ + if (!(data = (char *)SMB_MALLOC(read_size))) { + d_fprintf(stderr, _("malloc fail for size %d\n"), + read_size); + nt_status = NT_STATUS_NO_MEMORY; + goto out; + } + + } + + + if (c->opt_verbose) { + + d_printf(_("copying [\\\\%s\\%s%s] => [\\\\%s\\%s%s] " + "%s ACLs and %s DOS Attributes %s\n"), + smbXcli_conn_remote_name(cli_share_src->conn), + cli_share_src->share, src_name, + smbXcli_conn_remote_name(cli_share_dst->conn), + cli_share_dst->share, dst_name, + copy_acls ? _("with") : _("without"), + copy_attrs ? _("with") : _("without"), + copy_timestamps ? _("(preserving timestamps)") : "" ); + } + + + while (is_file) { + + /* copying file */ + size_t n; + + nt_status = cli_read(cli_share_src, fnum_src, data, nread, + read_size, &n); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("Error reading file [\\\\%s\\%s%s]: %s\n"), + smbXcli_conn_remote_name(cli_share_src->conn), + cli_share_src->share, + src_name, nt_errstr(nt_status)); + goto out; + } + + if (n == 0) + break; + + nt_status = cli_writeall(cli_share_dst, fnum_dst, 0, + (uint8_t *)data, nread, n, NULL); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("Error writing file: [\\\\%s\\%s%s]: %s\n"), + smbXcli_conn_remote_name(cli_share_dst->conn), + cli_share_dst->share, + dst_name, nt_errstr(nt_status)); + goto out; + } + + nread += n; + } + + + if (!is_file && !NT_STATUS_IS_OK(cli_chkpath(cli_share_dst, dst_name))) { + + /* creating dir */ + DEBUGADD(3,("creating dir %s on the destination server\n", + dst_name)); + + nt_status = cli_mkdir(cli_share_dst, dst_name); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("cannot create directory %s: %s\n", + dst_name, nt_errstr(nt_status))); + nt_status = NT_STATUS_NO_SUCH_FILE; + } + + + nt_status = cli_chkpath(cli_share_dst, dst_name); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("cannot check for directory %s: %s\n"), + dst_name, nt_errstr(nt_status)); + goto out; + } + } + + + /* closing files */ + nt_status = cli_close(cli_share_src, fnum_src); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("could not close file on originating server: %s\n"), + nt_errstr(nt_status)); + goto out; + } + + if (is_file) { + nt_status = cli_close(cli_share_dst, fnum_dst); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, + _("could not close file on destination server: %s\n"), + nt_errstr(nt_status)); + goto out; + } + } + + /* possibly we have to copy some file-attributes / acls / sd */ + nt_status = net_copy_fileattr(c, mem_ctx, cli_share_src, cli_share_dst, + src_name, dst_name, copy_acls, + copy_attrs, copy_timestamps, is_file); + if (!NT_STATUS_IS_OK(nt_status)) + goto out; + + + nt_status = NT_STATUS_OK; + +out: + + /* cleaning up */ + if (fnum_src) + cli_close(cli_share_src, fnum_src); + + if (fnum_dst) + cli_close(cli_share_dst, fnum_dst); + + SAFE_FREE(data); + + return nt_status; +} + +/** + * Copy a driverfile from on connected share to another connected share + * This silently assumes that a driver-file is picked up from + * + * \\src_server\print$\{arch}\{version}\file + * + * and copied to + * + * \\dst_server\print$\{arch}\file + * + * to be added via setdriver-calls later. + * @param c A net_context structure + * @param mem_ctx A talloc-context + * @param cli_share_src A cli_state connected to source print$-share + * @param cli_share_dst A cli_state connected to destination print$-share + * @param file The file-name to be copied + * @param short_archi The name of the driver-architecture (short form) + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS net_copy_driverfile(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *file, const char *short_archi) { + + const char *p; + char *src_name; + char *dst_name; + char *version = NULL; + char *filename = NULL; + char *tok; + + if (!file) { + return NT_STATUS_OK; + } + + /* scroll through the file until we have the part + beyond archi_table.short_archi */ + p = file; + while (next_token_talloc(mem_ctx, &p, &tok, "\\")) { + if (strequal(tok, short_archi)) { + next_token_talloc(mem_ctx, &p, &version, "\\"); + next_token_talloc(mem_ctx, &p, &filename, "\\"); + } + } + + if (version == NULL || filename == NULL) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* build source file name */ + src_name = talloc_asprintf(mem_ctx, "\\%s\\%s\\%s", + short_archi, version, filename); + if (src_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* create destination file name */ + dst_name = talloc_asprintf(mem_ctx, "\\%s\\%s", short_archi, filename); + if (dst_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + + /* finally copy the file */ + return net_copy_file(c, mem_ctx, cli_share_src, cli_share_dst, + src_name, dst_name, false, false, false, true); +} + +/** + * Check for existing Architecture directory on a given server + * + * @param cli_share A cli_state connected to a print$-share + * @param short_archi The Architecture for the print-driver + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS check_arch_dir(struct cli_state *cli_share, const char *short_archi) +{ + + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + char *dir; + + if (asprintf(&dir, "\\%s", short_archi) < 0) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10,("creating print-driver dir for architecture: %s\n", + short_archi)); + + nt_status = cli_mkdir(cli_share, dir); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1,("cannot create directory %s: %s\n", + dir, nt_errstr(nt_status))); + } + + nt_status = cli_chkpath(cli_share, dir); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("cannot check %s: %s\n"), + dir, nt_errstr(nt_status)); + goto out; + } + + nt_status = NT_STATUS_OK; + +out: + SAFE_FREE(dir); + return nt_status; +} + +/** + * Copy a print-driver (level 3) from one connected print$-share to another + * connected print$-share + * + * @param c A net_context structure + * @param mem_ctx A talloc-context + * @param cli_share_src A cli_state connected to a print$-share + * @param cli_share_dst A cli_state connected to a print$-share + * @param short_archi The Architecture for the print-driver + * @param i1 The DRIVER_INFO_3-struct + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS copy_print_driver_3(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct cli_state *cli_share_src, + struct cli_state *cli_share_dst, + const char *short_archi, + struct spoolss_DriverInfo3 *r) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + int i; + + if (r == NULL) { + return nt_status; + } + + if (c->opt_verbose) + d_printf(_("copying driver: [%s], for architecture: [%s], " + "version: [%d]\n"), + r->driver_name, short_archi, r->version); + + nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst, + r->driver_path, short_archi); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst, + r->data_file, short_archi); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst, + r->config_file, short_archi); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst, + r->help_file, short_archi); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + for (i=0; r->dependent_files[i] != NULL; i++) { + + nt_status = net_copy_driverfile(c, mem_ctx, + cli_share_src, cli_share_dst, + r->dependent_files[i], short_archi); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + } + + return NT_STATUS_OK; +} + +/** + * net_spoolss-functions + * ===================== + * + * the net_spoolss-functions aim to simplify spoolss-client-functions + * required during the migration-process wrt buffer-sizes, returned + * error-codes, etc. + * + * this greatly reduces the complexitiy of the migrate-functions. + * + **/ + +static bool net_spoolss_enum_printers(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + char *name, + uint32_t flags, + uint32_t level, + uint32_t *num_printers, + union spoolss_PrinterInfo **info) +{ + WERROR result; + + /* enum printers */ + + result = rpccli_spoolss_enumprinters(pipe_hnd, mem_ctx, + flags, + name, + level, + 0, + num_printers, + info); + if (!W_ERROR_IS_OK(result)) { + printf(_("cannot enum printers: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + +static bool net_spoolss_open_printer_ex(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + const char *printername, + uint32_t access_required, + struct policy_handle *hnd) +{ + struct cli_credentials *creds = gensec_get_credentials(pipe_hnd->auth->auth_ctx); + const char *username = cli_credentials_get_username(creds); + WERROR result; + fstring printername2; + + fstrcpy(printername2, pipe_hnd->srv_name_slash); + fstrcat(printername2, "\\"); + fstrcat(printername2, printername); + + DEBUG(10,("connecting to: %s as %s for %s and access: %x\n", + pipe_hnd->srv_name_slash, username, printername2, access_required)); + + /* open printer */ + result = rpccli_spoolss_openprinter_ex(pipe_hnd, mem_ctx, + printername2, + access_required, + hnd); + + /* be more verbose */ + if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) { + d_fprintf(stderr, + _("no access to printer [%s] on [%s] for user [%s] " + "granted\n"), + printername2, pipe_hnd->srv_name_slash, username); + return false; + } + + if (!W_ERROR_IS_OK(result)) { + d_fprintf(stderr,_("cannot open printer %s on server %s: %s\n"), + printername2, pipe_hnd->srv_name_slash, win_errstr(result)); + return false; + } + + DEBUG(2,("got printer handle for printer: %s, server: %s\n", + printername2, pipe_hnd->srv_name_slash)); + + return true; +} + +static bool net_spoolss_getprinter(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + uint32_t level, + union spoolss_PrinterInfo *info) +{ + WERROR result; + + /* getprinter call */ + result = rpccli_spoolss_getprinter(pipe_hnd, mem_ctx, + hnd, + level, + 0, /* offered */ + info); + if (!W_ERROR_IS_OK(result)) { + printf(_("cannot get printer-info: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + +static bool net_spoolss_setprinter(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + uint32_t level, + union spoolss_PrinterInfo *info) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + WERROR result; + NTSTATUS status; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_SetPrinterInfo2 info2; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + /* setprinter call */ + + info_ctr.level = level; + switch (level) { + case 0: + info_ctr.info.info0 = (struct spoolss_SetPrinterInfo0 *) + (void *)&info->info0; + break; + case 1: + info_ctr.info.info1 = (struct spoolss_SetPrinterInfo1 *) + (void *)&info->info1; + break; + case 2: + spoolss_printerinfo2_to_setprinterinfo2(&info->info2, &info2); + info_ctr.info.info2 = &info2; + break; + case 3: + info_ctr.info.info3 = (struct spoolss_SetPrinterInfo3 *) + (void *)&info->info3; + break; + case 4: + info_ctr.info.info4 = (struct spoolss_SetPrinterInfo4 *) + (void *)&info->info4; + break; + case 5: + info_ctr.info.info5 = (struct spoolss_SetPrinterInfo5 *) + (void *)&info->info5; + break; + case 6: + info_ctr.info.info6 = (struct spoolss_SetPrinterInfo6 *) + (void *)&info->info6; + break; + case 7: + info_ctr.info.info7 = (struct spoolss_SetPrinterInfo7 *) + (void *)&info->info7; + break; +#if 0 /* FIXME GD */ + case 8: + info_ctr.info.info8 = (struct spoolss_SetPrinterInfo8 *) + (void *)&info->info8; + break; + case 9: + info_ctr.info.info9 = (struct spoolss_SetPrinterInfo9 *) + (void *)&info->info9; + break; +#endif + default: + break; /* FIXME */ + } + + status = dcerpc_spoolss_SetPrinter(b, mem_ctx, + hnd, + &info_ctr, + &devmode_ctr, + &secdesc_ctr, + 0, /* command */ + &result); + if (!NT_STATUS_IS_OK(status)) { + printf(_("cannot set printer-info: %s\n"), nt_errstr(status)); + return false; + } + if (!W_ERROR_IS_OK(result)) { + printf(_("cannot set printer-info: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + + +static bool net_spoolss_setprinterdata(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + const char *value_name, + enum winreg_Type type, + uint8_t *data, + uint32_t offered) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + WERROR result; + NTSTATUS status; + + /* setprinterdata call */ + status = dcerpc_spoolss_SetPrinterData(b, mem_ctx, + hnd, + value_name, + type, + data, + offered, + &result); + if (!NT_STATUS_IS_OK(status)) { + printf (_("unable to set printerdata: %s\n"), + nt_errstr(status)); + return false; + } + if (!W_ERROR_IS_OK(result)) { + printf (_("unable to set printerdata: %s\n"), + win_errstr(result)); + return false; + } + + return true; +} + + +static bool net_spoolss_enumprinterkey(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + const char *keyname, + const char ***keylist) +{ + WERROR result; + + /* enumprinterkey call */ + result = rpccli_spoolss_enumprinterkey(pipe_hnd, mem_ctx, hnd, keyname, keylist, 0); + + if (!W_ERROR_IS_OK(result)) { + printf(_("enumprinterkey failed: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + +static bool net_spoolss_enumprinterdataex(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + uint32_t offered, + struct policy_handle *hnd, + const char *keyname, + uint32_t *count, + struct spoolss_PrinterEnumValues **info) +{ + WERROR result; + + /* enumprinterdataex call */ + result = rpccli_spoolss_enumprinterdataex(pipe_hnd, mem_ctx, + hnd, + keyname, + 0, /* offered */ + count, + info); + + if (!W_ERROR_IS_OK(result)) { + printf(_("enumprinterdataex failed: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + + +static bool net_spoolss_setprinterdataex(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + const char *keyname, + const char *name, + struct registry_value *value) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + WERROR result; + NTSTATUS status; + + /* setprinterdataex call */ + status = dcerpc_spoolss_SetPrinterDataEx(b, mem_ctx, + hnd, + keyname, + name, + value->type, + value->data.data, + value->data.length, + &result); + if (!NT_STATUS_IS_OK(status)) { + printf(_("could not set printerdataex: %s\n"), + nt_errstr(status)); + return false; + } + if (!W_ERROR_IS_OK(result)) { + printf(_("could not set printerdataex: %s\n"), + win_errstr(result)); + return false; + } + + return true; +} + +static bool net_spoolss_enumforms(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, + int level, + uint32_t *num_forms, + union spoolss_FormInfo **forms) +{ + WERROR result; + + /* enumforms call */ + result = rpccli_spoolss_enumforms(pipe_hnd, mem_ctx, + hnd, + level, + 0, + num_forms, + forms); + if (!W_ERROR_IS_OK(result)) { + printf(_("could not enum forms: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + +static bool net_spoolss_enumprinterdrivers (struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + uint32_t level, const char *env, + uint32_t *count, + union spoolss_DriverInfo **info) +{ + WERROR result; + + /* enumprinterdrivers call */ + result = rpccli_spoolss_enumprinterdrivers(pipe_hnd, mem_ctx, + pipe_hnd->srv_name_slash, + env, + level, + 0, + count, + info); + if (!W_ERROR_IS_OK(result)) { + if (W_ERROR_V(result) != W_ERROR_V(WERR_INVALID_ENVIRONMENT)) { + printf(_("cannot enum drivers for environment %s: %s\n"), env, + win_errstr(result)); + return false; + } else { + printf(_("Server does not support environment [%s]\n"), + env); + } + } + + return true; +} + +static bool net_spoolss_getprinterdriver(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hnd, uint32_t level, + const char *env, int version, + union spoolss_DriverInfo *info) +{ + WERROR result; + uint32_t server_major_version; + uint32_t server_minor_version; + + /* getprinterdriver call */ + result = rpccli_spoolss_getprinterdriver2(pipe_hnd, mem_ctx, + hnd, + env, + level, + 0, + version, + 2, + info, + &server_major_version, + &server_minor_version); + if (!W_ERROR_IS_OK(result)) { + DEBUG(1,("cannot get driver (for architecture: %s): %s\n", + env, win_errstr(result))); + if (W_ERROR_V(result) != W_ERROR_V(WERR_UNKNOWN_PRINTER_DRIVER) && + W_ERROR_V(result) != W_ERROR_V(WERR_INVALID_ENVIRONMENT)) { + printf(_("cannot get driver: %s\n"), + win_errstr(result)); + } + return false; + } + + return true; +} + + +static bool net_spoolss_addprinterdriver(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, uint32_t level, + union spoolss_DriverInfo *info) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + WERROR result; + NTSTATUS status; + struct spoolss_AddDriverInfoCtr info_ctr; + + info_ctr.level = level; + + switch (level) { + case 2: + info_ctr.info.info2 = (struct spoolss_AddDriverInfo2 *) + (void *)&info->info2; + break; + case 3: + info_ctr.info.info3 = (struct spoolss_AddDriverInfo3 *) + (void *)&info->info3; + break; + default: + printf(_("unsupported info level: %d\n"), level); + return false; + } + + /* addprinterdriver call */ + status = dcerpc_spoolss_AddPrinterDriver(b, mem_ctx, + pipe_hnd->srv_name_slash, + &info_ctr, + &result); + if (!NT_STATUS_IS_OK(status)) { + printf(_("cannot add driver: %s\n"), nt_errstr(status)); + return false; + } + /* be more verbose */ + if (W_ERROR_V(result) == W_ERROR_V(WERR_ACCESS_DENIED)) { + printf(_("You are not allowed to add drivers\n")); + return false; + } + if (!W_ERROR_IS_OK(result)) { + printf(_("cannot add driver: %s\n"), win_errstr(result)); + return false; + } + + return true; +} + +/** + * abstraction function to get uint32_t num_printers and PRINTER_INFO_CTR ctr + * for a single printer or for all printers depending on argc/argv + **/ + +static bool get_printer_info(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int level, + int argc, + const char **argv, + uint32_t *num_printers, + union spoolss_PrinterInfo **info_p) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + struct policy_handle hnd; + WERROR werr; + + /* no arguments given, enumerate all printers */ + if (argc == 0) { + + if (!net_spoolss_enum_printers(pipe_hnd, mem_ctx, NULL, + PRINTER_ENUM_LOCAL|PRINTER_ENUM_SHARED, + level, num_printers, info_p)) + return false; + + goto out; + } + + /* argument given, get a single printer by name */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, argv[0], + MAXIMUM_ALLOWED_ACCESS, + &hnd)) + return false; + + *info_p = talloc_zero(mem_ctx, union spoolss_PrinterInfo); + if (*info_p == NULL) { + return false; + } + + if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, *info_p)) { + dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr); + return false; + } + + dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr); + + *num_printers = 1; + +out: + DEBUG(3,("got %d printers\n", *num_printers)); + + return true; + +} + +/** + * List print-queues (including local printers that are not shared) + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i, num_printers; + uint32_t level = 2; + const char *printername, *sharename; + union spoolss_PrinterInfo *info; + + printf("listing printers\n"); + + if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info)) + return nt_status; + + for (i = 0; i < num_printers; i++) { + + /* do some initialization */ + printername = info[i].info2.printername; + sharename = info[i].info2.sharename; + + if (printername && sharename) { + d_printf(_("printer %d: %s, shared as: %s\n"), + i+1, printername, sharename); + } + } + + return NT_STATUS_OK; +} + +/** + * List printer-drivers from a server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_driver_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i; + uint32_t level = 3; + union spoolss_DriverInfo *info; + + printf(_("listing printer-drivers\n")); + + for (i=0; archi_table[i].long_archi!=NULL; i++) { + + uint32_t d, num_drivers; + + /* enum remote drivers */ + if (!net_spoolss_enumprinterdrivers(pipe_hnd, mem_ctx, level, + archi_table[i].long_archi, + &num_drivers, &info)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (num_drivers == 0) { + d_printf(_("no drivers found on server for " + "architecture: [%s].\n"), + archi_table[i].long_archi); + continue; + } + + d_printf(_("got %d printer-drivers for architecture: [%s]\n"), + num_drivers, archi_table[i].long_archi); + + + /* do something for all drivers for architecture */ + for (d = 0; d < num_drivers; d++) { + display_print_driver3(&info[d].info3); + } + } + + nt_status = NT_STATUS_OK; + +done: + return nt_status; + +} + +/** + * Publish print-queues with args-wrapper + * + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * @param action + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_printer_publish_internals_args(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv, + uint32_t action) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i, num_printers; + uint32_t level = 7; + const char *printername, *sharename; + union spoolss_PrinterInfo *info_enum; + union spoolss_PrinterInfo info; + struct spoolss_SetPrinterInfoCtr info_ctr; + struct spoolss_DevmodeContainer devmode_ctr; + struct sec_desc_buf secdesc_ctr; + struct policy_handle hnd = { 0, }; + WERROR result; + const char *action_str; + + if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) + return nt_status; + + for (i = 0; i < num_printers; i++) { + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + if (!printername || !sharename) { + goto done; + } + + /* open printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &info)) + goto done; + + /* check action and set string */ + switch (action) { + case DSPRINT_PUBLISH: + action_str = N_("published"); + break; + case DSPRINT_UPDATE: + action_str = N_("updated"); + break; + case DSPRINT_UNPUBLISH: + action_str = N_("unpublished"); + break; + default: + action_str = N_("unknown action"); + printf(_("unknown action: %d\n"), action); + break; + } + + info.info7.action = action; + info_ctr.level = 7; + info_ctr.info.info7 = (struct spoolss_SetPrinterInfo7 *) + (void *)&info.info7; + + ZERO_STRUCT(devmode_ctr); + ZERO_STRUCT(secdesc_ctr); + + nt_status = dcerpc_spoolss_SetPrinter(b, mem_ctx, + &hnd, + &info_ctr, + &devmode_ctr, + &secdesc_ctr, + 0, /* command */ + &result); + if (!NT_STATUS_IS_OK(nt_status)) { + printf(_("cannot set printer-info: %s\n"), + nt_errstr(nt_status)); + goto done; + } + if (!W_ERROR_IS_OK(result) && !W_ERROR_EQUAL(result, WERR_IO_PENDING)) { + if ((action == DSPRINT_UPDATE) && W_ERROR_EQUAL(result, W_ERROR(0x80070002))) { + printf(_("printer not published yet\n")); + } else { + printf(_("cannot set printer-info: %s\n"), + win_errstr(result)); + } + nt_status = werror_to_ntstatus(result); + goto done; + } + + printf(_("successfully %s printer %s in Active Directory\n"), + action_str, sharename); + } + + nt_status = NT_STATUS_OK; + +done: + if (is_valid_policy_hnd(&hnd)) { + dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &result); + } + + return nt_status; +} + +NTSTATUS rpc_printer_publish_publish_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_PUBLISH); +} + +NTSTATUS rpc_printer_publish_unpublish_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_UNPUBLISH); +} + +NTSTATUS rpc_printer_publish_update_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_printer_publish_internals_args(pipe_hnd, mem_ctx, argc, argv, DSPRINT_UPDATE); +} + +/** + * List print-queues w.r.t. their publishing state + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_publish_list_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i, num_printers; + uint32_t level = 7; + const char *printername, *sharename; + union spoolss_PrinterInfo *info_enum; + union spoolss_PrinterInfo info; + struct policy_handle hnd = { 0, }; + int state; + WERROR werr; + + if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) + return nt_status; + + for (i = 0; i < num_printers; i++) { + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + + if (!printername || !sharename) { + goto done; + } + + /* open printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &info)) + goto done; + + if (!info.info7.guid) { + goto done; + } + state = info.info7.action; + switch (state) { + case DSPRINT_PUBLISH: + printf(_("printer [%s] is published"), + sharename); + if (c->opt_verbose) + printf(_(", guid: %s"),info.info7.guid); + printf("\n"); + break; + case DSPRINT_UNPUBLISH: + printf(_("printer [%s] is unpublished\n"), + sharename); + break; + case DSPRINT_UPDATE: + printf(_("printer [%s] is currently updating\n"), + sharename); + break; + default: + printf(_("unknown state: %d\n"), state); + break; + } + } + + nt_status = NT_STATUS_OK; + +done: + if (is_valid_policy_hnd(&hnd)) { + dcerpc_spoolss_ClosePrinter(b, mem_ctx, &hnd, &werr); + } + + return nt_status; +} + +/** + * Migrate Printer-ACLs from a source server to the destination server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_migrate_security_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle; + /* TODO: what now, info2 or info3 ? + convince jerry that we should add clientside setacls level 3 at least + */ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i = 0; + uint32_t num_printers; + uint32_t level = 2; + const char *printername, *sharename; + struct rpc_pipe_client *pipe_hnd_dst = NULL; + struct dcerpc_binding_handle *b_dst = NULL; + struct policy_handle hnd_src = { 0, }; + struct policy_handle hnd_dst = { 0, }; + union spoolss_PrinterInfo *info_enum; + struct cli_state *cli_dst = NULL; + union spoolss_PrinterInfo info_src, info_dst; + WERROR werr; + + DEBUG(3,("copying printer ACLs\n")); + + /* connect destination PI_SPOOLSS */ + nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst, + &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + b_dst = pipe_hnd_dst->binding_handle; + + /* enum source printers */ + if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!num_printers) { + printf (_("no printers found on server.\n")); + nt_status = NT_STATUS_OK; + goto done; + } + + /* do something for all printers */ + for (i = 0; i < num_printers; i++) { + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + + if (!printername || !sharename) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* we can reset NT_STATUS here because we do not + get any real NT_STATUS-codes anymore from now on */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + d_printf(_("migrating printer ACLs for: [%s] / [%s]\n"), + printername, sharename); + + /* according to msdn you have specify these access-rights + to see the security descriptor + - READ_CONTROL (DACL) + - ACCESS_SYSTEM_SECURITY (SACL) + */ + + /* open src printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + MAXIMUM_ALLOWED_ACCESS, &hnd_src)) + goto done; + + /* open dst printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd_dst)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst)) + goto done; + + /* check for existing src printer */ + if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, 3, &info_src)) + goto done; + + /* Copy Security Descriptor */ + + /* copy secdesc (info level 2) */ + info_dst.info2.devmode = NULL; + if (info_src.info3.secdesc == NULL) { + info_dst.info2.secdesc = NULL; + } else { + info_dst.info2.secdesc + = security_descriptor_copy(mem_ctx, + info_src.info3.secdesc); + if (info_dst.info2.secdesc == NULL) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + } + + if (c->opt_verbose) + display_sec_desc(info_dst.info2.secdesc); + + if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst)) + goto done; + + DEBUGADD(1,("\tSetPrinter of SECDESC succeeded\n")); + + + /* close printer handles here */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr); + } + + } + + nt_status = NT_STATUS_OK; + +done: + + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr); + } + + if (cli_dst) { + cli_shutdown(cli_dst); + } + return nt_status; +} + +/** + * Migrate printer-forms from a src server to the dst server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_migrate_forms_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + WERROR result; + uint32_t i, f; + uint32_t num_printers; + uint32_t level = 1; + const char *printername, *sharename; + struct rpc_pipe_client *pipe_hnd_dst = NULL; + struct dcerpc_binding_handle *b_dst = NULL; + struct policy_handle hnd_src = { 0, }; + struct policy_handle hnd_dst = { 0, }; + union spoolss_PrinterInfo *info_enum; + union spoolss_PrinterInfo info_dst; + uint32_t num_forms; + union spoolss_FormInfo *forms; + struct cli_state *cli_dst = NULL; + + DEBUG(3,("copying forms\n")); + + /* connect destination PI_SPOOLSS */ + nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst, + &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + b_dst = pipe_hnd_dst->binding_handle; + + /* enum src printers */ + if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!num_printers) { + printf (_("no printers found on server.\n")); + nt_status = NT_STATUS_OK; + goto done; + } + + /* do something for all printers */ + for (i = 0; i < num_printers; i++) { + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + + if (!printername || !sharename) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + /* we can reset NT_STATUS here because we do not + get any real NT_STATUS-codes anymore from now on */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + d_printf(_("migrating printer forms for: [%s] / [%s]\n"), + printername, sharename); + + + /* open src printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + MAXIMUM_ALLOWED_ACCESS, &hnd_src)) + goto done; + + /* open dst printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd_dst)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst)) + goto done; + + /* finally migrate forms */ + if (!net_spoolss_enumforms(pipe_hnd, mem_ctx, &hnd_src, level, &num_forms, &forms)) + goto done; + + DEBUG(1,("got %d forms for printer\n", num_forms)); + + + for (f = 0; f < num_forms; f++) { + + struct spoolss_AddFormInfoCtr info_ctr; + NTSTATUS status; + + /* only migrate FORM_PRINTER types, according to jerry + FORM_BUILTIN-types are hard-coded in samba */ + if (forms[f].info1.flags != SPOOLSS_FORM_PRINTER) + continue; + + if (c->opt_verbose) + d_printf(_("\tmigrating form # %d [%s] of type " + "[%d]\n"), + f, forms[f].info1.form_name, + forms[f].info1.flags); + info_ctr.level = 1; + info_ctr.info.info1 = (struct spoolss_AddFormInfo1 *) + (void *)&forms[f].info1; + + /* FIXME: there might be something wrong with samba's + builtin-forms */ + status = dcerpc_spoolss_AddForm(b_dst, mem_ctx, + &hnd_dst, + &info_ctr, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf(_("\tdcerpc_spoolss_AddForm form %d: [%s] - %s\n"), + f, forms[f].info1.form_name, nt_errstr(status)); + continue; + } + if (!W_ERROR_IS_OK(result)) { + d_printf(_("\tAddForm form %d: [%s] refused.\n"), + f, forms[f].info1.form_name); + continue; + } + + DEBUGADD(1,("\tAddForm of [%s] succeeded\n", + forms[f].info1.form_name)); + } + + + /* close printer handles here */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + } + + nt_status = NT_STATUS_OK; + +done: + + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + + if (cli_dst) { + cli_shutdown(cli_dst); + } + return nt_status; +} + +/** + * Migrate printer-drivers from a src server to the dst server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_migrate_drivers_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i, p; + uint32_t num_printers; + uint32_t level = 3; + const char *printername, *sharename; + bool got_src_driver_share = false; + bool got_dst_driver_share = false; + struct rpc_pipe_client *pipe_hnd_dst = NULL; + struct dcerpc_binding_handle *b_dst = NULL; + struct policy_handle hnd_src = { 0, }; + struct policy_handle hnd_dst = { 0, }; + union spoolss_DriverInfo drv_info_src; + union spoolss_PrinterInfo *info_enum; + union spoolss_PrinterInfo info_dst; + struct cli_state *cli_dst = NULL; + struct cli_state *cli_share_src = NULL; + struct cli_state *cli_share_dst = NULL; + const char *drivername = NULL; + WERROR werr; + + DEBUG(3,("copying printer-drivers\n")); + + nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst, + &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + b_dst = pipe_hnd_dst->binding_handle; + + /* open print$-share on the src server */ + nt_status = connect_to_service(c, &cli_share_src, + smbXcli_conn_remote_sockaddr(cli->conn), + smbXcli_conn_remote_name(cli->conn), + "print$", "A:"); + if (!NT_STATUS_IS_OK(nt_status)) + goto done; + + got_src_driver_share = true; + + + /* open print$-share on the dst server */ + nt_status = connect_to_service(c, &cli_share_dst, + smbXcli_conn_remote_sockaddr(cli_dst->conn), + smbXcli_conn_remote_name(cli_dst->conn), + "print$", "A:"); + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + got_dst_driver_share = true; + + + /* enum src printers */ + if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &info_enum)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (num_printers == 0) { + printf (_("no printers found on server.\n")); + nt_status = NT_STATUS_OK; + goto done; + } + + + /* do something for all printers */ + for (p = 0; p < num_printers; p++) { + + /* do some initialization */ + printername = info_enum[p].info2.printername; + sharename = info_enum[p].info2.sharename; + + if (!printername || !sharename) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* we can reset NT_STATUS here because we do not + get any real NT_STATUS-codes anymore from now on */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + d_printf(_("migrating printer driver for: [%s] / [%s]\n"), + printername, sharename); + + /* open dst printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd_dst)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst)) + goto done; + + + /* open src printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + MAXIMUM_ALLOWED_ACCESS, + &hnd_src)) + goto done; + + /* in a first step call getdriver for each shared printer (per arch) + to get a list of all files that have to be copied */ + + for (i=0; archi_table[i].long_archi!=NULL; i++) { + + /* getdriver src */ + if (!net_spoolss_getprinterdriver(pipe_hnd, mem_ctx, &hnd_src, + level, archi_table[i].long_archi, + archi_table[i].version, &drv_info_src)) + continue; + + drivername = drv_info_src.info3.driver_name; + + if (c->opt_verbose) + display_print_driver3(&drv_info_src.info3); + + /* check arch dir */ + nt_status = check_arch_dir(cli_share_dst, archi_table[i].short_archi); + if (!NT_STATUS_IS_OK(nt_status)) + goto done; + + + /* copy driver-files */ + nt_status = copy_print_driver_3(c, mem_ctx, cli_share_src, cli_share_dst, + archi_table[i].short_archi, + &drv_info_src.info3); + if (!NT_STATUS_IS_OK(nt_status)) + goto done; + + + /* adddriver dst */ + if (!net_spoolss_addprinterdriver(pipe_hnd_dst, mem_ctx, level, &drv_info_src)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + DEBUGADD(1,("Successfully added driver [%s] for printer [%s]\n", + drivername, printername)); + + } + + if (!drivername || strlen(drivername) == 0) { + DEBUGADD(1,("Did not get driver for printer %s\n", + printername)); + goto done; + } + + /* setdriver dst */ + info_dst.info2.drivername = drivername; + + if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_dst)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + DEBUGADD(1,("Successfully set driver %s for printer %s\n", + drivername, printername)); + + /* close dst */ + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr); + } + + /* close src */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr); + } + } + + nt_status = NT_STATUS_OK; + +done: + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &werr); + } + + /* close src */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &werr); + } + + if (cli_dst) { + cli_shutdown(cli_dst); + } + + if (got_src_driver_share) + cli_shutdown(cli_share_src); + + if (got_dst_driver_share) + cli_shutdown(cli_share_dst); + + return nt_status; + +} + +/** + * Migrate printer-queues from a src to the dst server + * (requires a working "addprinter command" to be installed for the local smbd) + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_migrate_printers_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle; + WERROR result; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i = 0, num_printers; + uint32_t level = 2; + union spoolss_PrinterInfo info_dst, info_src; + union spoolss_PrinterInfo *info_enum; + struct cli_state *cli_dst = NULL; + struct policy_handle hnd_src = { 0, }; + struct policy_handle hnd_dst = { 0, }; + const char *printername, *sharename; + struct rpc_pipe_client *pipe_hnd_dst = NULL; + struct dcerpc_binding_handle *b_dst = NULL; + struct spoolss_SetPrinterInfoCtr info_ctr; + + DEBUG(3,("copying printers\n")); + + /* connect destination PI_SPOOLSS */ + nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst, + &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + b_dst = pipe_hnd_dst->binding_handle; + + /* enum printers */ + if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!num_printers) { + printf (_("no printers found on server.\n")); + nt_status = NT_STATUS_OK; + goto done; + } + + /* do something for all printers */ + for (i = 0; i < num_printers; i++) { + + struct spoolss_SetPrinterInfo2 info2; + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + + if (!printername || !sharename) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + /* we can reset NT_STATUS here because we do not + get any real NT_STATUS-codes anymore from now on */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + d_printf(_("migrating printer queue for: [%s] / [%s]\n"), + printername, sharename); + + /* open dst printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd_dst)) { + + DEBUG(1,("could not open printer: %s\n", sharename)); + } + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &info_dst)) { + printf (_("could not get printer, creating printer.\n")); + } else { + DEBUG(1,("printer already exists: %s\n", sharename)); + /* close printer handle here - dst only, not got src yet. */ + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + continue; + } + + /* now get again src printer ctr via getprinter, + we first need a handle for that */ + + /* open src printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + MAXIMUM_ALLOWED_ACCESS, &hnd_src)) + goto done; + + /* getprinter on the src server */ + if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, level, &info_src)) + goto done; + + /* copy each src printer to a dst printer 1:1, + maybe some values have to be changed though */ + d_printf(_("creating printer: %s\n"), printername); + + info_ctr.level = level; + spoolss_printerinfo2_to_setprinterinfo2(&info_src.info2, &info2); + info_ctr.info.info2 = &info2; + + result = rpccli_spoolss_addprinterex(pipe_hnd_dst, + mem_ctx, + &info_ctr); + + if (W_ERROR_IS_OK(result)) + d_printf (_("printer [%s] successfully added.\n"), + printername); + else if (W_ERROR_V(result) == W_ERROR_V(WERR_PRINTER_ALREADY_EXISTS)) + d_fprintf (stderr, _("printer [%s] already exists.\n"), + printername); + else { + d_fprintf (stderr, _("could not create printer [%s]\n"), + printername); + goto done; + } + + /* close printer handles here */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + } + + nt_status = NT_STATUS_OK; + +done: + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + + if (cli_dst) { + cli_shutdown(cli_dst); + } + return nt_status; +} + +/** + * Migrate Printer-Settings from a src server to the dst server + * (for this to work, printers and drivers already have to be migrated earlier) + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param c A net_context structure + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destroyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +NTSTATUS rpc_printer_migrate_settings_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct dcerpc_binding_handle *b_src = pipe_hnd->binding_handle; + + /* FIXME: Here the nightmare begins */ + + WERROR result; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uint32_t i = 0, j = 0; + uint32_t num_printers; + uint32_t level = 2; + const char *printername, *sharename; + struct rpc_pipe_client *pipe_hnd_dst = NULL; + struct dcerpc_binding_handle *b_dst = NULL; + struct policy_handle hnd_src = { 0, }; + struct policy_handle hnd_dst = { 0, }; + union spoolss_PrinterInfo *info_enum; + union spoolss_PrinterInfo info_dst_publish; + union spoolss_PrinterInfo info_dst; + struct cli_state *cli_dst = NULL; + const char *longname; + const char **keylist = NULL; + + /* FIXME GD */ + ZERO_STRUCT(info_dst_publish); + + DEBUG(3,("copying printer settings\n")); + + /* connect destination PI_SPOOLSS */ + nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst, + &ndr_table_spoolss); + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + b_dst = pipe_hnd_dst->binding_handle; + + /* enum src printers */ + if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &info_enum)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!num_printers) { + printf (_("no printers found on server.\n")); + nt_status = NT_STATUS_OK; + goto done; + } + + + /* needed for dns-strings in regkeys */ + longname = get_mydnsfullname(); + if (!longname) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + /* do something for all printers */ + for (i = 0; i < num_printers; i++) { + + uint32_t value_needed; + uint32_t data_needed; + enum winreg_Type type; + struct spoolss_EnumPrinterData r; + + /* do some initialization */ + printername = info_enum[i].info2.printername; + sharename = info_enum[i].info2.sharename; + + if (!printername || !sharename) { + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + /* we can reset NT_STATUS here because we do not + get any real NT_STATUS-codes anymore from now on */ + nt_status = NT_STATUS_UNSUCCESSFUL; + + d_printf(_("migrating printer settings for: [%s] / [%s]\n"), + printername, sharename); + + + /* open src printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename, + MAXIMUM_ALLOWED_ACCESS, &hnd_src)) + goto done; + + /* open dst printer handle */ + if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename, + PRINTER_ALL_ACCESS, &hnd_dst)) + goto done; + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, + level, &info_dst)) + goto done; + + + /* STEP 1: COPY DEVICE-MODE and other + PRINTER_INFO_2-attributes + */ + + info_dst.info2 = info_enum[i].info2; + + /* why is the port always disconnected when the printer + is correctly installed (incl. driver ???) */ + info_dst.info2.portname = SAMBA_PRINTER_PORT_NAME; + + /* check if printer is published */ + if (info_enum[i].info2.attributes & PRINTER_ATTRIBUTE_PUBLISHED) { + + /* check for existing dst printer */ + if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &info_dst_publish)) + goto done; + + info_dst_publish.info7.action = DSPRINT_PUBLISH; + + /* ignore false from setprinter due to WERR_IO_PENDING */ + net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &info_dst_publish); + + DEBUG(3,("republished printer\n")); + } + + if (info_enum[i].info2.devmode != NULL) { + + /* copy devmode (info level 2) */ + info_dst.info2.devmode = info_enum[i].info2.devmode; + + /* do not copy security descriptor (we have another + * command for that) */ + info_dst.info2.secdesc = NULL; + +#if 0 + info_dst.info2.devmode.devicename = + talloc_asprintf(mem_ctx, "\\\\%s\\%s", + longname, printername); + if (!info_dst.info2.devmode.devicename) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } +#endif + if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, + level, &info_dst)) + goto done; + + DEBUGADD(1,("\tSetPrinter of DEVICEMODE succeeded\n")); + } + + /* STEP 2: COPY REGISTRY VALUES */ + + /* please keep in mind that samba parse_spools gives horribly + crippled results when used to rpccli_spoolss_enumprinterdataex + a win2k3-server. (Bugzilla #1851) + FIXME: IIRC I've seen it too on a win2k-server + */ + + r.in.handle = &hnd_src; + r.in.enum_index = 0; + r.in.value_offered = 0; + r.in.data_offered = 0; + r.out.value_name = NULL; + r.out.value_needed = &value_needed; + r.out.type = &type; + r.out.data = NULL; + r.out.data_needed = &data_needed; + + /* enumerate data on src handle */ + nt_status = dcerpc_spoolss_EnumPrinterData_r(b_src, mem_ctx, &r); + + r.in.data_offered = *r.out.data_needed; + r.in.value_offered = *r.out.value_needed; + r.out.data = talloc_zero_array(mem_ctx, uint8_t, r.in.data_offered); + r.out.value_name = talloc_zero_array(mem_ctx, char, r.in.value_offered); + + /* loop for all printerdata of "PrinterDriverData" */ + while (NT_STATUS_IS_OK(nt_status) && W_ERROR_IS_OK(r.out.result)) { + + r.in.enum_index++; + + nt_status = dcerpc_spoolss_EnumPrinterData_r(b_src, mem_ctx, &r); + + /* loop for all reg_keys */ + if (NT_STATUS_IS_OK(nt_status) && W_ERROR_IS_OK(r.out.result)) { + + /* display_value */ + if (c->opt_verbose) { + struct registry_value v; + v.type = *r.out.type; + v.data = data_blob_const( + r.out.data, r.in.data_offered); + + display_reg_value(SPOOL_PRINTERDATA_KEY, + r.out.value_name, &v); + } + + /* set_value */ + if (!net_spoolss_setprinterdata(pipe_hnd_dst, mem_ctx, + &hnd_dst, r.out.value_name, + *r.out.type, r.out.data, r.in.data_offered)) + goto done; + + DEBUGADD(1,("\tSetPrinterData of [%s] succeeded\n", + r.out.value_name)); + } + } + + /* STEP 3: COPY SUBKEY VALUES */ + + /* here we need to enum all printer_keys and then work + on the result with enum_printer_key_ex. nt4 does not + respond to enumprinterkey, win2k does, so continue + in case of an error */ + + if (!net_spoolss_enumprinterkey(pipe_hnd, mem_ctx, &hnd_src, "", &keylist)) { + printf(_("got no key-data\n")); + continue; + } + + + /* work on a list of printer keys + each key has to be enumerated to get all required + information. information is then set via setprinterdataex-calls */ + + if (keylist == NULL) + continue; + + for (i=0; keylist && keylist[i] != NULL; i++) { + + const char *subkey = keylist[i]; + uint32_t count; + struct spoolss_PrinterEnumValues *info; + + /* enumerate all src subkeys */ + if (!net_spoolss_enumprinterdataex(pipe_hnd, mem_ctx, 0, + &hnd_src, subkey, + &count, &info)) { + goto done; + } + + for (j=0; j < count; j++) { + + struct registry_value value; + const char *value_name = info[j].value_name; + bool ok; + + value.type = REG_SZ; + + /* although samba replies with sane data in most cases we + should try to avoid writing wrong registry data */ + + if (strequal(value_name, SPOOL_REG_PORTNAME)) { + /* although windows uses a multi-sz, we use a sz */ + ok = push_reg_sz(mem_ctx, &value.data, SAMBA_PRINTER_PORT_NAME); + if (!ok) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + } + else if (strequal(value_name, SPOOL_REG_UNCNAME)) { + char *unc_name; + if (asprintf(&unc_name, "\\\\%s\\%s", longname, sharename) < 0) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + ok = push_reg_sz(mem_ctx, &value.data, unc_name); + if (!ok) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + free(unc_name); + } + else if (strequal(value_name, SPOOL_REG_URL)) { + continue; +#if 0 + /* FIXME: should we really do that ??? */ + if (asprintf(&url, "http://%s:631/printers/%s", longname, sharename) < 0) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + push_reg_sz(mem_ctx, NULL, &value.data, url); + free(url); +#endif + } + else if (strequal(value_name, SPOOL_REG_SERVERNAME)) { + ok = push_reg_sz(mem_ctx, &value.data, longname); + if (!ok) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + } + else if (strequal(value_name, SPOOL_REG_SHORTSERVERNAME)) { + ok = push_reg_sz(mem_ctx, &value.data, lp_netbios_name()); + if (!ok) { + nt_status = NT_STATUS_NO_MEMORY; + goto done; + } + } + else { + value.type = info[j].type; + value.data = *info[j].data; + } + + if (c->opt_verbose) { + display_reg_value(subkey, value_name, &value); + } + + /* here we have to set all subkeys on the dst server */ + if (!net_spoolss_setprinterdataex(pipe_hnd_dst, mem_ctx, &hnd_dst, + subkey, value_name, &value)) + { + goto done; + } + + DEBUGADD(1,("\tSetPrinterDataEx of key [%s\\%s] succeeded\n", + subkey, info[j].value_name)); + + } + } + + TALLOC_FREE(keylist); + + /* close printer handles here */ + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + } + + nt_status = NT_STATUS_OK; + +done: + if (is_valid_policy_hnd(&hnd_src)) { + dcerpc_spoolss_ClosePrinter(b_src, mem_ctx, &hnd_src, &result); + } + + if (is_valid_policy_hnd(&hnd_dst)) { + dcerpc_spoolss_ClosePrinter(b_dst, mem_ctx, &hnd_dst, &result); + } + + if (cli_dst) { + cli_shutdown(cli_dst); + } + return nt_status; +} diff --git a/source3/utils/net_rpc_registry.c b/source3/utils/net_rpc_registry.c new file mode 100644 index 0000000..cec5459 --- /dev/null +++ b/source3/utils/net_rpc_registry.c @@ -0,0 +1,2126 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + + Copyright (C) Gerald (Jerry) Carter 2005-2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "system/filesys.h" +#include "rpc_client/rpc_client.h" +#include "registry.h" +#include "utils/net.h" +#include "utils/net_registry_util.h" +#include "registry/regfio.h" +#include "../librpc/gen_ndr/ndr_winreg_c.h" +#include "../librpc/gen_ndr/ndr_security.h" +#include "registry/reg_format.h" +#include "registry/reg_import.h" +#include <assert.h> +#include "../libcli/security/display_sec.h" +#include "../libcli/registry/util_reg.h" +#include "client.h" +#include "lib/util/smb_strtox.h" + + +/******************************************************************* + connect to a registry hive root (open a registry policy) +*******************************************************************/ + +static NTSTATUS dcerpc_winreg_Connect(struct dcerpc_binding_handle *b, TALLOC_CTX *mem_ctx, + uint32_t reg_type, uint32_t access_mask, + struct policy_handle *reg_hnd, WERROR *werr) +{ + ZERO_STRUCTP(reg_hnd); + + switch (reg_type) + { + case HKEY_CLASSES_ROOT: + return dcerpc_winreg_OpenHKCR(b, mem_ctx, NULL, + access_mask, reg_hnd, werr); + + case HKEY_LOCAL_MACHINE: + return dcerpc_winreg_OpenHKLM(b, mem_ctx, NULL, + access_mask, reg_hnd, werr); + + case HKEY_USERS: + return dcerpc_winreg_OpenHKU(b, mem_ctx, NULL, + access_mask, reg_hnd, werr); + + case HKEY_CURRENT_USER: + return dcerpc_winreg_OpenHKCU(b, mem_ctx, NULL, + access_mask, reg_hnd, werr); + + case HKEY_PERFORMANCE_DATA: + return dcerpc_winreg_OpenHKPD(b, mem_ctx, NULL, + access_mask, reg_hnd, werr); + + default: + /* fall through to end of function */ + break; + } + + return NT_STATUS_INVALID_PARAMETER; +} + +static bool reg_hive_key(TALLOC_CTX *ctx, const char *fullname, + uint32_t *reg_type, const char **key_name) +{ + WERROR werr; + char *hivename = NULL; + char *tmp_keyname = NULL; + bool ret = false; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + werr = split_hive_key(tmp_ctx, fullname, &hivename, &tmp_keyname); + if (!W_ERROR_IS_OK(werr)) { + goto done; + } + + *key_name = talloc_strdup(ctx, tmp_keyname); + if (*key_name == NULL) { + goto done; + } + + if (strequal(hivename, "HKLM") || + strequal(hivename, "HKEY_LOCAL_MACHINE")) + { + (*reg_type) = HKEY_LOCAL_MACHINE; + } else if (strequal(hivename, "HKCR") || + strequal(hivename, "HKEY_CLASSES_ROOT")) + { + (*reg_type) = HKEY_CLASSES_ROOT; + } else if (strequal(hivename, "HKU") || + strequal(hivename, "HKEY_USERS")) + { + (*reg_type) = HKEY_USERS; + } else if (strequal(hivename, "HKCU") || + strequal(hivename, "HKEY_CURRENT_USER")) + { + (*reg_type) = HKEY_CURRENT_USER; + } else if (strequal(hivename, "HKPD") || + strequal(hivename, "HKEY_PERFORMANCE_DATA")) + { + (*reg_type) = HKEY_PERFORMANCE_DATA; + } else { + DEBUG(10,("reg_hive_key: unrecognised hive key %s\n", + fullname)); + goto done; + } + + ret = true; + +done: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static NTSTATUS registry_openkey(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_hnd, + const char *name, uint32_t access_mask, + struct policy_handle *hive_hnd, + struct policy_handle *key_hnd) +{ + uint32_t hive; + NTSTATUS status; + WERROR werr; + struct winreg_String key; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(key); + + if (!reg_hive_key(mem_ctx, name, &hive, &key.name)) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_winreg_Connect(b, mem_ctx, hive, access_mask, + hive_hnd, &werr); + if (!(NT_STATUS_IS_OK(status))) { + return status; + } + if (!W_ERROR_IS_OK(werr)) { + return werror_to_ntstatus(werr); + } + + status = dcerpc_winreg_OpenKey(b, mem_ctx, hive_hnd, key, 0, + access_mask, key_hnd, &werr); + if (!(NT_STATUS_IS_OK(status))) { + dcerpc_winreg_CloseKey(b, mem_ctx, hive_hnd, &werr); + return status; + } + if (!(W_ERROR_IS_OK(werr))) { + WERROR _werr; + dcerpc_winreg_CloseKey(b, mem_ctx, hive_hnd, &_werr); + return werror_to_ntstatus(werr); + } + + return NT_STATUS_OK; +} + +static NTSTATUS registry_enumkeys(TALLOC_CTX *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *key_hnd, + uint32_t *pnum_keys, char ***pnames, + char ***pclasses, NTTIME ***pmodtimes) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + WERROR werr; + uint32_t num_subkeys, max_subkeylen, max_classlen; + uint32_t num_values, max_valnamelen, max_valbufsize; + uint32_t i; + NTTIME last_changed_time; + uint32_t secdescsize; + struct winreg_String classname; + char **names, **classes; + NTTIME **modtimes; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (!(mem_ctx = talloc_new(ctx))) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(classname); + status = dcerpc_winreg_QueryInfoKey( + b, mem_ctx, key_hnd, &classname, &num_subkeys, + &max_subkeylen, &max_classlen, &num_values, &max_valnamelen, + &max_valbufsize, &secdescsize, &last_changed_time, &werr); + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto error; + } + + if (num_subkeys == 0) { + *pnum_keys = 0; + TALLOC_FREE(mem_ctx); + return NT_STATUS_OK; + } + + if ((!(names = talloc_zero_array(mem_ctx, char *, num_subkeys))) || + (!(classes = talloc_zero_array(mem_ctx, char *, num_subkeys))) || + (!(modtimes = talloc_zero_array(mem_ctx, NTTIME *, + num_subkeys)))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + for (i=0; i<num_subkeys; i++) { + char c, n; + struct winreg_StringBuf class_buf; + struct winreg_StringBuf name_buf; + NTTIME modtime; + + c = '\0'; + class_buf.name = &c; + class_buf.size = max_classlen+2; + + n = '\0'; + name_buf.name = &n; + name_buf.size = max_subkeylen+2; + + ZERO_STRUCT(modtime); + + status = dcerpc_winreg_EnumKey(b, mem_ctx, key_hnd, + i, &name_buf, &class_buf, + &modtime, &werr); + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (W_ERROR_EQUAL(werr, + WERR_NO_MORE_ITEMS) ) { + status = NT_STATUS_OK; + break; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto error; + } + + classes[i] = NULL; + + if (class_buf.name && + (!(classes[i] = talloc_strdup(classes, class_buf.name)))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + if (!(names[i] = talloc_strdup(names, name_buf.name))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + if ((!(modtimes[i] = (NTTIME *)talloc_memdup( + modtimes, &modtime, sizeof(modtime))))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + } + + *pnum_keys = num_subkeys; + + if (pnames) { + *pnames = talloc_move(ctx, &names); + } + if (pclasses) { + *pclasses = talloc_move(ctx, &classes); + } + if (pmodtimes) { + *pmodtimes = talloc_move(ctx, &modtimes); + } + + status = NT_STATUS_OK; + + error: + TALLOC_FREE(mem_ctx); + return status; +} + +static NTSTATUS registry_enumvalues(TALLOC_CTX *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *key_hnd, + uint32_t *pnum_values, char ***pvalnames, + struct registry_value ***pvalues) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + WERROR werr; + uint32_t num_subkeys, max_subkeylen, max_classlen; + uint32_t num_values, max_valnamelen, max_valbufsize; + uint32_t i; + NTTIME last_changed_time; + uint32_t secdescsize; + struct winreg_String classname; + struct registry_value **values; + char **names; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (!(mem_ctx = talloc_new(ctx))) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(classname); + status = dcerpc_winreg_QueryInfoKey( + b, mem_ctx, key_hnd, &classname, &num_subkeys, + &max_subkeylen, &max_classlen, &num_values, &max_valnamelen, + &max_valbufsize, &secdescsize, &last_changed_time, &werr); + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto error; + } + + if (num_values == 0) { + *pnum_values = 0; + TALLOC_FREE(mem_ctx); + return NT_STATUS_OK; + } + + if ((!(names = talloc_array(mem_ctx, char *, num_values))) || + (!(values = talloc_array(mem_ctx, struct registry_value *, + num_values)))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + for (i=0; i<num_values; i++) { + enum winreg_Type type = REG_NONE; + uint8_t *data = NULL; + uint32_t data_size; + uint32_t value_length; + + char n; + struct winreg_ValNameBuf name_buf; + WERROR err; + + n = '\0'; + name_buf.name = &n; + name_buf.size = max_valnamelen + 2; + + data_size = max_valbufsize; + data = (uint8_t *)TALLOC(mem_ctx, data_size); + value_length = 0; + + status = dcerpc_winreg_EnumValue(b, mem_ctx, key_hnd, + i, &name_buf, &type, + data, &data_size, + &value_length, &err); + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if ( W_ERROR_EQUAL(err, + WERR_NO_MORE_ITEMS) ) { + status = NT_STATUS_OK; + break; + } + + if (!W_ERROR_IS_OK(err)) { + status = werror_to_ntstatus(err); + goto error; + } + + if (name_buf.name == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + if (!(names[i] = talloc_strdup(names, name_buf.name))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + values[i] = talloc_zero(values, struct registry_value); + if (values[i] == NULL) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + values[i]->type = type; + values[i]->data = data_blob_talloc(values[i], data, data_size); + } + + *pnum_values = num_values; + + if (pvalnames) { + *pvalnames = talloc_move(ctx, &names); + } + if (pvalues) { + *pvalues = talloc_move(ctx, &values); + } + + status = NT_STATUS_OK; + + error: + TALLOC_FREE(mem_ctx); + return status; +} + +static NTSTATUS registry_enumvalues2(TALLOC_CTX *ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *key_hnd, + uint32_t *pnum_values, char ***pvalnames, + struct regval_blob ***pvalues) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + WERROR werr; + uint32_t num_subkeys, max_subkeylen, max_classlen; + uint32_t num_values, max_valnamelen, max_valbufsize; + uint32_t i; + NTTIME last_changed_time; + uint32_t secdescsize; + struct winreg_String classname; + struct regval_blob **values; + char **names; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (!(mem_ctx = talloc_new(ctx))) { + return NT_STATUS_NO_MEMORY; + } + + ZERO_STRUCT(classname); + status = dcerpc_winreg_QueryInfoKey( + b, mem_ctx, key_hnd, &classname, &num_subkeys, + &max_subkeylen, &max_classlen, &num_values, &max_valnamelen, + &max_valbufsize, &secdescsize, &last_changed_time, &werr); + + if (!NT_STATUS_IS_OK(status)) { + goto error; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + goto error; + } + + if (num_values == 0) { + *pnum_values = 0; + TALLOC_FREE(mem_ctx); + return NT_STATUS_OK; + } + + if ((!(names = talloc_array(mem_ctx, char *, num_values))) || + (!(values = talloc_array(mem_ctx, struct regval_blob *, + num_values)))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + for (i=0; i<num_values; i++) { + enum winreg_Type type = REG_NONE; + uint8_t *data = NULL; + uint32_t data_size; + uint32_t value_length; + + char n; + struct winreg_ValNameBuf name_buf; + WERROR err; + + n = '\0'; + name_buf.name = &n; + name_buf.size = max_valnamelen + 2; + + data_size = max_valbufsize; + data = (uint8_t *)TALLOC(mem_ctx, data_size); + value_length = 0; + + status = dcerpc_winreg_EnumValue(b, mem_ctx, key_hnd, + i, &name_buf, &type, + data, &data_size, + &value_length, &err); + if (!(NT_STATUS_IS_OK(status))) { + goto error; + } + + if ( W_ERROR_EQUAL(err, WERR_NO_MORE_ITEMS) ) { + status = NT_STATUS_OK; + break; + } + + if (!W_ERROR_IS_OK(err)) { + status = werror_to_ntstatus(err); + goto error; + } + + if (name_buf.name == NULL) { + status = NT_STATUS_INVALID_PARAMETER; + goto error; + } + + if (!(names[i] = talloc_strdup(names, name_buf.name))) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + + assert(value_length<=data_size); /*??? */ + + values[i] = regval_compose(values, + name_buf.name, + type, + data, value_length); + if (!values[i]) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + } + + *pnum_values = num_values; + + if (pvalnames) { + *pvalnames = talloc_move(ctx, &names); + } + if (pvalues) { + *pvalues = talloc_move(ctx, &values); + } + + status = NT_STATUS_OK; + + error: + TALLOC_FREE(mem_ctx); + return status; +} + +static NTSTATUS registry_getsd(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *b, + struct policy_handle *key_hnd, + uint32_t sec_info, + struct KeySecurityData *sd, + WERROR *werr) +{ + return dcerpc_winreg_GetKeySecurity(b, mem_ctx, key_hnd, + sec_info, sd, werr); +} + + +static NTSTATUS registry_setvalue(TALLOC_CTX *mem_ctx, + struct rpc_pipe_client *pipe_hnd, + struct policy_handle *key_hnd, + const char *name, + const struct registry_value *value) +{ + struct winreg_String name_string; + NTSTATUS result; + WERROR werr; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(name_string); + + name_string.name = name; + result = dcerpc_winreg_SetValue(b, mem_ctx, key_hnd, + name_string, value->type, + value->data.data, value->data.length, &werr); + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + return werror_to_ntstatus(werr); +} + +static NTSTATUS rpc_registry_setvalue_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hive_hnd, key_hnd; + NTSTATUS status; + WERROR werr; + struct registry_value value; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], + SEC_FLAG_MAXIMUM_ALLOWED, + &hive_hnd, &key_hnd); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + if (!strequal(argv[2], "multi_sz") && (argc != 4)) { + d_fprintf(stderr, _("Too many args for type %s\n"), argv[2]); + return NT_STATUS_NOT_IMPLEMENTED; + } + + if (strequal(argv[2], "dword")) { + int error = 0; + uint32_t v; + + v = smb_strtoul(argv[3], NULL, 10, &error, SMB_STR_STANDARD); + if (error != 0) { + goto error; + } + + value.type = REG_DWORD; + value.data = data_blob_talloc(mem_ctx, NULL, 4); + SIVAL(value.data.data, 0, v); + } + else if (strequal(argv[2], "sz")) { + value.type = REG_SZ; + if (!push_reg_sz(mem_ctx, &value.data, argv[3])) { + status = NT_STATUS_NO_MEMORY; + goto error; + } + } + else { + d_fprintf(stderr, _("type \"%s\" not implemented\n"), argv[2]); + status = NT_STATUS_NOT_IMPLEMENTED; + goto error; + } + + status = registry_setvalue(mem_ctx, pipe_hnd, &key_hnd, + argv[1], &value); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_setvalue failed: %s\n"), + nt_errstr(status)); + } + + error: + dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr); + + return NT_STATUS_OK; +} + +static int rpc_registry_setvalue(struct net_context *c, int argc, + const char **argv ) +{ + if (argc < 4 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry setvalue <key> <valuename> " + "<type> [<val>]+\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_setvalue_internal, argc, argv ); +} + +static NTSTATUS rpc_registry_deletevalue_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hive_hnd, key_hnd; + NTSTATUS status; + WERROR werr; + struct winreg_String valuename; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(valuename); + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], + SEC_FLAG_MAXIMUM_ALLOWED, + &hive_hnd, &key_hnd); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + valuename.name = argv[1]; + + status = dcerpc_winreg_DeleteValue(b, mem_ctx, &key_hnd, + valuename, &werr); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_deletevalue failed: %s\n"), + nt_errstr(status)); + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + d_fprintf(stderr, _("registry_deletevalue failed: %s\n"), + win_errstr(werr)); + } + + dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr); + + return status; +} + +static int rpc_registry_deletevalue(struct net_context *c, int argc, + const char **argv ) +{ + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry deletevalue <key> <valuename>\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_deletevalue_internal, argc, argv ); +} + +static NTSTATUS rpc_registry_getvalue_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + bool raw, + int argc, + const char **argv) +{ + struct policy_handle hive_hnd, key_hnd; + NTSTATUS status; + WERROR werr; + struct winreg_String valuename; + struct registry_value *value = NULL; + enum winreg_Type type = REG_NONE; + uint32_t data_size = 0; + uint32_t value_length = 0; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(valuename); + + status = registry_openkey(tmp_ctx, pipe_hnd, argv[0], + SEC_FLAG_MAXIMUM_ALLOWED, + &hive_hnd, &key_hnd); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + valuename.name = argv[1]; + + value = talloc_zero(tmp_ctx, struct registry_value); + if (value == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* + * call QueryValue once with data == NULL to get the + * needed memory size to be allocated, then allocate + * data buffer and call again. + */ + status = dcerpc_winreg_QueryValue(b, tmp_ctx, &key_hnd, + &valuename, + &type, + NULL, + &data_size, + &value_length, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_queryvalue failed: %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + d_fprintf(stderr, _("registry_queryvalue failed: %s\n"), + nt_errstr(status)); + goto done; + } + + value->data = data_blob_talloc(tmp_ctx, NULL, data_size); + + status = dcerpc_winreg_QueryValue(b, tmp_ctx, &key_hnd, + &valuename, + &type, + value->data.data, + &data_size, + &value_length, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_queryvalue failed: %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + d_fprintf(stderr, _("registry_queryvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + + + value->type = type; + + print_registry_value(value, raw); + +done: + dcerpc_winreg_CloseKey(b, tmp_ctx, &key_hnd, &werr); + dcerpc_winreg_CloseKey(b, tmp_ctx, &hive_hnd, &werr); + + TALLOC_FREE(tmp_ctx); + + return status; +} + +static NTSTATUS rpc_registry_getvalue_full(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_registry_getvalue_internal(c, domain_sid, domain_name, + cli, pipe_hnd, mem_ctx, false, + argc, argv); +} + +static int rpc_registry_getvalue(struct net_context *c, int argc, + const char **argv) +{ + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry getvalue <key> <valuename>\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_getvalue_full, argc, argv); +} + +static NTSTATUS rpc_registry_getvalue_raw(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + return rpc_registry_getvalue_internal(c, domain_sid, domain_name, + cli, pipe_hnd, mem_ctx, true, + argc, argv); +} + +static int rpc_registry_getvalueraw(struct net_context *c, int argc, + const char **argv) +{ + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry getvalue <key> <valuename>\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_getvalue_raw, argc, argv); +} + +static NTSTATUS rpc_registry_createkey_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + uint32_t hive; + struct policy_handle hive_hnd, key_hnd; + struct winreg_String key, keyclass; + enum winreg_CreateAction action; + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(key); + ZERO_STRUCT(keyclass); + + if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_winreg_Connect(b, mem_ctx, hive, + SEC_FLAG_MAXIMUM_ALLOWED, + &hive_hnd, &werr); + if (!(NT_STATUS_IS_OK(status))) { + return status; + } + if (!W_ERROR_IS_OK(werr)) { + return werror_to_ntstatus(werr); + } + + action = REG_ACTION_NONE; + keyclass.name = ""; + + status = dcerpc_winreg_CreateKey(b, mem_ctx, &hive_hnd, key, + keyclass, 0, REG_KEY_READ, NULL, + &key_hnd, &action, &werr); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("createkey returned %s\n"), + nt_errstr(status)); + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr); + return status; + } + if (!W_ERROR_IS_OK(werr)) { + WERROR _werr; + d_fprintf(stderr, _("createkey returned %s\n"), + win_errstr(werr)); + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &_werr); + return werror_to_ntstatus(werr); + } + + switch (action) { + case REG_ACTION_NONE: + d_printf(_("createkey did nothing -- huh?\n")); + break; + case REG_CREATED_NEW_KEY: + d_printf(_("createkey created %s\n"), argv[0]); + break; + case REG_OPENED_EXISTING_KEY: + d_printf(_("createkey opened existing %s\n"), argv[0]); + break; + } + + dcerpc_winreg_CloseKey(b, mem_ctx, &key_hnd, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &werr); + + return status; +} + +static int rpc_registry_createkey(struct net_context *c, int argc, + const char **argv ) +{ + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry createkey <key>\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_createkey_internal, argc, argv ); +} + +static NTSTATUS rpc_registry_deletekey_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + uint32_t hive; + struct policy_handle hive_hnd; + struct winreg_String key; + NTSTATUS status; + WERROR werr; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(key); + + if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) { + return NT_STATUS_INVALID_PARAMETER; + } + + status = dcerpc_winreg_Connect(b, mem_ctx, hive, + SEC_FLAG_MAXIMUM_ALLOWED, + &hive_hnd, &werr); + if (!(NT_STATUS_IS_OK(status))) { + return status; + } + if (!W_ERROR_IS_OK(werr)) { + return werror_to_ntstatus(werr); + } + + status = dcerpc_winreg_DeleteKey(b, mem_ctx, &hive_hnd, key, &werr); + if (is_valid_policy_hnd(&hive_hnd)) { + WERROR _werr; + dcerpc_winreg_CloseKey(b, mem_ctx, &hive_hnd, &_werr); + } + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("deletekey returned %s\n"), + nt_errstr(status)); + return status; + } + + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("deletekey returned %s\n"), + win_errstr(werr)); + return werror_to_ntstatus(werr); + } + + return status; +} + +static int rpc_registry_deletekey(struct net_context *c, int argc, const char **argv ) +{ + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net rpc registry deletekey <key>\n")); + return -1; + } + + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_deletekey_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_registry_enumerate_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle pol_hive, pol_key; + NTSTATUS status; + WERROR werr; + uint32_t num_subkeys = 0; + uint32_t num_values = 0; + char **names = NULL, **classes = NULL; + NTTIME **modtimes = NULL; + uint32_t i; + struct registry_value **values = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry enumerate <path>\n")); + d_printf("%s net rpc registry enumerate " + "'HKLM\\Software\\Samba'\n", _("Example:")); + return NT_STATUS_INVALID_PARAMETER; + } + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_READ, + &pol_hive, &pol_key); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + status = registry_enumkeys(mem_ctx, pipe_hnd, &pol_key, &num_subkeys, + &names, &classes, &modtimes); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("enumerating keys failed: %s\n"), + nt_errstr(status)); + return status; + } + + for (i=0; i<num_subkeys; i++) { + print_registry_key(names[i], modtimes[i]); + } + + status = registry_enumvalues(mem_ctx, pipe_hnd, &pol_key, &num_values, + &names, &values); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("enumerating values failed: %s\n"), + nt_errstr(status)); + return status; + } + + for (i=0; i<num_values; i++) { + print_registry_value_with_name(names[i], values[i]); + } + + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr); + + return status; +} + +/******************************************************************** +********************************************************************/ + +static int rpc_registry_enumerate(struct net_context *c, int argc, + const char **argv ) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_enumerate_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_registry_save_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + WERROR result = WERR_GEN_FAILURE; + struct policy_handle pol_hive, pol_key; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + struct winreg_String filename; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry backup <path> <file> \n")); + return NT_STATUS_INVALID_PARAMETER; + } + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_ALL, + &pol_hive, &pol_key); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + filename.name = argv[1]; + status = dcerpc_winreg_SaveKey(b, mem_ctx, &pol_key, &filename, NULL, &result); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Unable to save [%s] to %s:%s\n"), argv[0], + pipe_hnd->desthost, argv[1]); + } + if (!W_ERROR_IS_OK(result)) { + status = werror_to_ntstatus(result); + d_fprintf(stderr, _("Unable to save [%s] to %s:%s\n"), argv[0], + pipe_hnd->desthost, argv[1]); + } + + /* cleanup */ + + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &result); + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &result); + + return status; +} + +/******************************************************************** +********************************************************************/ + +static int rpc_registry_save(struct net_context *c, int argc, const char **argv ) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_save_internal, argc, argv ); +} + + +/******************************************************************** +********************************************************************/ + +static void dump_values( REGF_NK_REC *nk ) +{ + int i, j; + const char *data_str = NULL; + uint32_t data_size, data; + DATA_BLOB blob; + + if ( !nk->values ) + return; + + for ( i=0; i<nk->num_values; i++ ) { + d_printf( "\"%s\" = ", nk->values[i].valuename ? nk->values[i].valuename : "(default)" ); + d_printf( "(%s) ", str_regtype( nk->values[i].type ) ); + + data_size = nk->values[i].data_size & ~VK_DATA_IN_OFFSET; + switch ( nk->values[i].type ) { + case REG_SZ: + blob = data_blob_const(nk->values[i].data, data_size); + if (!pull_reg_sz(talloc_tos(), &blob, + &data_str)) { + data_str = NULL; + } + if (!data_str) { + break; + } + d_printf( "%s", data_str ); + break; + case REG_MULTI_SZ: + case REG_EXPAND_SZ: + for ( j=0; j<data_size; j++ ) { + d_printf( "%c", nk->values[i].data[j] ); + } + break; + case REG_DWORD: + data = IVAL( nk->values[i].data, 0 ); + d_printf("0x%x", data ); + break; + case REG_BINARY: + for ( j=0; j<data_size; j++ ) { + d_printf( "%x", nk->values[i].data[j] ); + } + break; + default: + d_printf(_("unknown")); + break; + } + + d_printf( "\n" ); + } + +} + +/******************************************************************** +********************************************************************/ + +static bool dump_registry_tree( REGF_FILE *file, REGF_NK_REC *nk, const char *parent ) +{ + REGF_NK_REC *key; + + /* depth first dump of the registry tree */ + + while ( (key = regfio_fetch_subkey( file, nk )) ) { + char *regpath; + if (asprintf(®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; i<nk->num_values; i++ ) { + regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type, + nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) ); + } + + /* copy subkeys into the struct regsubkey_ctr */ + + while ( (subkey = regfio_fetch_subkey( infile, nk )) ) { + regsubkey_ctr_addkey( subkeys, subkey->keyname ); + } + + key = regfio_write_key( outfile, nk->keyname, values, subkeys, nk->sec_desc->sec_desc, parent ); + + /* write each one of the subkeys out */ + + path = talloc_asprintf(subkeys, + "%s%s%s", + parentpath, + parent ? "\\" : "", + nk->keyname); + if (!path) { + TALLOC_FREE(subkeys); + return false; + } + + nk->subkey_index = 0; + while ( (subkey = regfio_fetch_subkey( infile, nk )) ) { + write_registry_tree( infile, subkey, key, outfile, path ); + } + + d_printf("[%s]\n", path ); + TALLOC_FREE(subkeys); + + return true; +} + +/******************************************************************** +********************************************************************/ + +static int rpc_registry_dump(struct net_context *c, int argc, const char **argv) +{ + REGF_FILE *registry; + REGF_NK_REC *nk; + + if (argc != 1 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry dump <file> \n")); + return -1; + } + + d_printf(_("Opening %s...."), argv[0]); + if ( !(registry = regfio_open( argv[0], O_RDONLY, 0)) ) { + d_fprintf(stderr, _("Failed to open %s for reading\n"),argv[0]); + return 1; + } + d_printf(_("ok\n")); + + /* get the root of the registry file */ + + if ((nk = regfio_rootkey( registry )) == NULL) { + d_fprintf(stderr, _("Could not get rootkey\n")); + regfio_close( registry ); + return 1; + } + d_printf("[%s]\n", nk->keyname); + dump_values( nk ); + d_printf("\n"); + + dump_registry_tree( registry, nk, nk->keyname ); + +#if 0 + talloc_report_full( registry->mem_ctx, stderr ); +#endif + d_printf(_("Closing registry...")); + regfio_close( registry ); + d_printf(_("ok\n")); + + return 0; +} + +/******************************************************************** +********************************************************************/ + +static int rpc_registry_copy(struct net_context *c, int argc, const char **argv ) +{ + REGF_FILE *infile = NULL, *outfile = NULL; + REGF_NK_REC *nk; + int result = 1; + + if (argc != 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry copy <srcfile> <newfile>\n")); + return -1; + } + + d_printf(_("Opening %s...."), argv[0]); + if ( !(infile = regfio_open( argv[0], O_RDONLY, 0 )) ) { + d_fprintf(stderr, _("Failed to open %s for reading\n"),argv[0]); + return 1; + } + d_printf(_("ok\n")); + + d_printf(_("Opening %s...."), argv[1]); + if ( !(outfile = regfio_open( argv[1], (O_RDWR|O_CREAT|O_TRUNC), + (S_IRUSR|S_IWUSR) )) ) { + d_fprintf(stderr, _("Failed to open %s for writing\n"),argv[1]); + goto out; + } + d_printf(_("ok\n")); + + /* get the root of the registry file */ + + if ((nk = regfio_rootkey( infile )) == NULL) { + d_fprintf(stderr, _("Could not get rootkey\n")); + goto out; + } + d_printf(_("RootKey: [%s]\n"), nk->keyname); + + write_registry_tree( infile, nk, NULL, outfile, "" ); + + result = 0; + +out: + + d_printf(_("Closing %s..."), argv[1]); + if (outfile) { + regfio_close( outfile ); + } + d_printf(_("ok\n")); + + d_printf(_("Closing %s..."), argv[0]); + if (infile) { + regfio_close( infile ); + } + d_printf(_("ok\n")); + + return( result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_registry_getsd_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle pol_hive, pol_key; + NTSTATUS status; + WERROR werr; + enum ndr_err_code ndr_err; + struct KeySecurityData *sd = NULL; + uint32_t sec_info; + DATA_BLOB blob; + struct security_descriptor sec_desc; + uint32_t access_mask = SEC_FLAG_MAXIMUM_ALLOWED | + SEC_FLAG_SYSTEM_SECURITY; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc <1 || argc > 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry getsd <path> <secinfo>\n")); + d_printf("%s net rpc registry getsd " + "'HKLM\\Software\\Samba'\n", _("Example:")); + return NT_STATUS_INVALID_PARAMETER; + } + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], + access_mask, + &pol_hive, &pol_key); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + sd = talloc_zero(mem_ctx, struct KeySecurityData); + if (!sd) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + sd->size = 0x1000; + + if (argc >= 2) { + sscanf(argv[1], "%x", &sec_info); + } else { + sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; + } + + status = registry_getsd(mem_ctx, b, &pol_key, sec_info, sd, &werr); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("getting sd failed: %s\n"), + nt_errstr(status)); + goto out; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + d_fprintf(stderr, _("getting sd failed: %s\n"), + win_errstr(werr)); + goto out; + } + + blob.data = sd->data; + blob.length = sd->size; + + ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, &sec_desc, + (ndr_pull_flags_fn_t)ndr_pull_security_descriptor); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + goto out; + } + status = NT_STATUS_OK; + + display_sec_desc(&sec_desc); + + out: + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr); + + return status; +} + + +static int rpc_registry_getsd(struct net_context *c, int argc, const char **argv) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_getsd_internal, argc, argv); +} + +/******************************************************************** + ********************************************************************/ +/** + * @defgroup net_rpc_registry net rpc registry + */ + +/** + * @defgroup net_rpc_registry_export Export + * @ingroup net_rpc_registry + * @{ + */ + +static NTSTATUS registry_export(struct rpc_pipe_client* pipe_hnd, + TALLOC_CTX* ctx, + struct policy_handle* key_hnd, + struct reg_format* f, + const char* parentfullname, + const char* name) +{ + NTSTATUS status; + uint32_t num_subkeys = 0; + uint32_t num_values = 0; + char **names = NULL, **classes = NULL; + NTTIME **modtimes = NULL; + struct regval_blob **values = NULL; + uint32_t i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + TALLOC_CTX* mem_ctx = talloc_new(ctx); + + + const char* fullname = name + ? talloc_asprintf(mem_ctx, "%s\\%s", parentfullname, name) + : parentfullname; + reg_format_key(f, &fullname, 1, false); + + status = registry_enumvalues2(mem_ctx, pipe_hnd, key_hnd, &num_values, + &names, &values); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("enumerating values failed: %s\n"), + nt_errstr(status)); + goto done; + } + + for (i=0; i<num_values; i++) { + reg_format_regval_blob(f, names[i], values[i]); + } + + + status = registry_enumkeys(mem_ctx, pipe_hnd, key_hnd, &num_subkeys, + &names, &classes, &modtimes); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("enumerating keys failed: %s\n"), + nt_errstr(status)); + goto done; + } + + for (i=0; i<num_subkeys; i++) { + struct policy_handle subkey_hnd; + struct winreg_String key; + WERROR werr; + ZERO_STRUCT(key); + /* key.name = talloc_strdup(mem_ctx, names[i]); ??? */ + key.name = names[i]; + + status = dcerpc_winreg_OpenKey(b, mem_ctx, key_hnd, key, + 0, REG_KEY_READ, + &subkey_hnd, &werr); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("dcerpc_winreg_OpenKey failed: %s %s\n"), + names[i], nt_errstr(status)); + continue; + } + if (!W_ERROR_IS_OK(werr)) { + status = werror_to_ntstatus(werr); + d_fprintf(stderr, + _("dcerpc_winreg_OpenKey failed: %s %s\n"), + names[i], win_errstr(werr)); + continue; + } + + status = registry_export(pipe_hnd, mem_ctx, &subkey_hnd, + f, fullname, names[i]); + if (!(NT_STATUS_IS_OK(status))) { + d_fprintf(stderr, + _("export key failed: %s %s\n"), + names[i], nt_errstr(status)); + } + dcerpc_winreg_CloseKey(b, mem_ctx, + &subkey_hnd, &werr); + } +done: + talloc_free(mem_ctx); + return status; +} + +static NTSTATUS rpc_registry_export_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle pol_hive, pol_key; + NTSTATUS status; + WERROR werr; + struct reg_format* f; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc < 2 || argc > 3 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry export <path> <file> [opt]\n")); + d_printf("%s net rpc registry export " + "'HKLM\\Software\\Samba' samba.reg\n", _("Example:")); + return NT_STATUS_INVALID_PARAMETER; + } + + status = registry_openkey(mem_ctx, pipe_hnd, argv[0], REG_KEY_READ, + &pol_hive, &pol_key); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_openkey failed: %s\n"), + nt_errstr(status)); + return status; + } + + f = reg_format_file(mem_ctx, argv[1], (argc > 2) ? argv[2] : NULL); + if (f == NULL) { + d_fprintf(stderr, _("open file failed: %s\n"), strerror(errno)); + return map_nt_error_from_unix(errno); + } + + status = registry_export(pipe_hnd, mem_ctx, &pol_key, + f, argv[0], NULL ); + if (!NT_STATUS_IS_OK(status)) + return status; + + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_key, &werr); + dcerpc_winreg_CloseKey(b, mem_ctx, &pol_hive, &werr); + + return status; +} +/******************************************************************** + ********************************************************************/ + +static int rpc_registry_export(struct net_context *c, int argc, + const char **argv ) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_export_internal, argc, argv ); +} + +/**@}*/ + +/******************************************************************** + ********************************************************************/ + +/** + * @defgroup net_rpc_registry_import Import + * @ingroup net_rpc_registry + * @{ + */ + +struct import_ctx { + struct rpc_pipe_client *pipe_hnd; + TALLOC_CTX *mem_ctx; +}; + +static WERROR import_create_key(struct import_ctx* ctx, + struct policy_handle* parent, const char* name, + void** pkey, bool* existing) +{ + WERROR werr; + NTSTATUS status; + void* mem_ctx = talloc_new(ctx->mem_ctx); + + struct policy_handle* key = NULL; + struct policy_handle hive; + struct winreg_String keyclass, keyname; + enum winreg_CreateAction action = REG_ACTION_NONE; + struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle; + + ZERO_STRUCT(keyname); + keyname.name = name; + + if (parent == NULL) { + uint32_t hive_idx = 0; + if (!reg_hive_key(mem_ctx, name, &hive_idx, &keyname.name)) { + werr = WERR_FOOBAR; + goto done; + } + + status = dcerpc_winreg_Connect(b, mem_ctx, + hive_idx, SEC_FLAG_MAXIMUM_ALLOWED, + &hive, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"), + win_errstr(werr)); + goto done; + } + + parent = &hive; + } + + key = talloc_zero(mem_ctx, struct policy_handle); + if (key == NULL) { + werr = WERR_NOT_ENOUGH_MEMORY; + goto done; + } + + ZERO_STRUCT(keyclass); + keyclass.name = ""; + + status = dcerpc_winreg_CreateKey(b, mem_ctx, + parent, keyname, + keyclass, 0, REG_KEY_READ, NULL, + key, &action, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("dcerpc_winreg_CreateKey returned %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("dcerpc_winreg_CreateKey returned %s\n"), + win_errstr(werr)); + goto done; + } + + switch (action) { + case REG_CREATED_NEW_KEY: + d_printf(_("createkey created %s\n"), name); + if (existing != NULL) + *existing = false; + break; + + case REG_OPENED_EXISTING_KEY: + d_printf(_("createkey opened existing %s\n"), name); + if (existing != NULL) + *existing = true; + break; + + case REG_ACTION_NONE: + d_printf(_("createkey did nothing -- huh?\n")); + werr = WERR_CREATE_FAILED; + break; + default: + assert(false); + } + +done: + if ( parent == &hive ) { + WERROR _result; + dcerpc_winreg_CloseKey(b, mem_ctx, + parent, &_result); + } + + if (pkey!=NULL) { + *pkey = talloc_steal(ctx->mem_ctx, key); + } + + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_delete_key(struct import_ctx* ctx, + struct policy_handle* parent, const char* name) +{ + WERROR werr; + NTSTATUS status; + void* mem_ctx = talloc_new(ctx->mem_ctx); + struct winreg_String keyname = { 0, }; + struct policy_handle hive; + struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle; + + keyname.name = name; + + if (parent == NULL) { + uint32_t hive_idx; + if (!reg_hive_key(mem_ctx, name, &hive_idx, &keyname.name)) { + werr = WERR_FOOBAR; + goto done; + } + + status = dcerpc_winreg_Connect(b, mem_ctx, hive_idx, + SEC_FLAG_MAXIMUM_ALLOWED, &hive, + &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("dcerpc_winreg_Connect returned %s\n"), + win_errstr(werr)); + goto done; + } + + parent = &hive; + } + + status = dcerpc_winreg_DeleteKey(b, mem_ctx, parent, + keyname, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("dcerpc_winreg_DeleteKey returned %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("dcerpc_winreg_DeleteKey returned %s\n"), + win_errstr(werr)); + goto done; + } + +done: + if ( parent == &hive ) { + WERROR _result; + dcerpc_winreg_CloseKey(b, mem_ctx, parent, &_result); + } + + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_close_key(struct import_ctx* ctx, + struct policy_handle* key) +{ + WERROR werr; + NTSTATUS status; + void* mem_ctx = talloc_new(ctx->mem_ctx); + struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle; + + status = dcerpc_winreg_CloseKey(b, mem_ctx, key, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("dcerpc_winreg_CloseKey returned %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("dcerpc_winreg_CloseKey returned %s\n"), + win_errstr(werr)); + goto done; + } + + werr = (talloc_free(key) == 0) ? WERR_OK : WERR_GEN_FAILURE; +done: + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_create_val(struct import_ctx* ctx, + struct policy_handle* parent, const char* name, + uint32_t type, const uint8_t* val, uint32_t len) +{ + WERROR werr; + NTSTATUS status; + void* mem_ctx = talloc_new(ctx->mem_ctx); + struct winreg_String valuename; + struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle; + + if (parent == NULL) { + return WERR_INVALID_PARAMETER; + } + + ZERO_STRUCT(valuename); + valuename.name = name; + + status = dcerpc_winreg_SetValue(b, mem_ctx, parent, + valuename, type, + (uint8_t *)discard_const(val), len, &werr); + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("registry_setvalue failed: %s\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + d_fprintf(stderr, _("registry_setvalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + +done: + talloc_free(mem_ctx); + return werr; +} + +static WERROR import_delete_val(struct import_ctx* ctx, + struct policy_handle* parent, const char* name) +{ + WERROR werr; + NTSTATUS status; + void* mem_ctx = talloc_new(ctx->mem_ctx); + struct winreg_String valuename; + struct dcerpc_binding_handle *b = ctx->pipe_hnd->binding_handle; + + if (parent == NULL) { + return WERR_INVALID_PARAMETER; + } + + ZERO_STRUCT(valuename); + valuename.name = name; + + status = dcerpc_winreg_DeleteValue(b, mem_ctx, + parent, valuename, &werr); + + if (!NT_STATUS_IS_OK(status)) { + werr = ntstatus_to_werror(status); + d_fprintf(stderr, _("registry_deletevalue failed: %s\n"), + nt_errstr(status)); + goto done; + } + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("registry_deletevalue failed: %s\n"), + win_errstr(werr)); + goto done; + } + +done: + talloc_free(mem_ctx); + return werr; +} + + + +static NTSTATUS rpc_registry_import_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct import_ctx import_ctx; + + struct reg_import_callback import_callback = { + .openkey = NULL, + .closekey = (reg_import_callback_closekey_t)&import_close_key, + .createkey = (reg_import_callback_createkey_t)&import_create_key, + .deletekey = (reg_import_callback_deletekey_t)&import_delete_key, + .deleteval = (reg_import_callback_deleteval_t)&import_delete_val, + .setval = { + .blob = (reg_import_callback_setval_blob_t)&import_create_val, + }, + .setval_type = BLOB, + .data = &import_ctx + }; + + int ret; + if (argc < 1 || argc > 2 || c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc registry import <file> [options]\n")); + d_printf("%s net rpc registry export " + "samba.reg enc=CP1252,flags=0\n", _("Example:")); + return NT_STATUS_INVALID_PARAMETER; + } + ZERO_STRUCT(import_ctx); + import_ctx.pipe_hnd = pipe_hnd; + import_ctx.mem_ctx = mem_ctx; + ret = reg_parse_file(argv[0], + reg_import_adapter(import_ctx.mem_ctx, + import_callback + ), + (argc > 1) ? argv[1] : NULL + ); + + return ret==0 ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/******************************************************************** + ********************************************************************/ + +static int rpc_registry_import(struct net_context *c, int argc, + const char **argv ) +{ + return run_rpc_command(c, NULL, &ndr_table_winreg, 0, + rpc_registry_import_internal, argc, argv ); +} + +/**@}*/ +/******************************************************************** + ********************************************************************/ + +int net_rpc_registry(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "enumerate", + rpc_registry_enumerate, + NET_TRANSPORT_RPC, + N_("Enumerate registry keys and values"), + N_("net rpc registry enumerate\n" + " Enumerate registry keys and values") + }, + { + "createkey", + rpc_registry_createkey, + NET_TRANSPORT_RPC, + N_("Create a new registry key"), + N_("net rpc registry createkey\n" + " Create a new registry key") + }, + { + "deletekey", + rpc_registry_deletekey, + NET_TRANSPORT_RPC, + N_("Delete a registry key"), + N_("net rpc registry deletekey\n" + " Delete a registry key") + }, + { + "getvalue", + rpc_registry_getvalue, + NET_TRANSPORT_RPC, + N_("Print a registry value"), + N_("net rpc registry getvalue\n" + " Print a registry value") + }, + { + "getvalueraw", + rpc_registry_getvalueraw, + NET_TRANSPORT_RPC, + N_("Print a registry value"), + N_("net rpc registry getvalueraw\n" + " Print a registry value (raw version)") + }, + { + "setvalue", + rpc_registry_setvalue, + NET_TRANSPORT_RPC, + N_("Set a new registry value"), + N_("net rpc registry setvalue\n" + " Set a new registry value") + }, + { + "deletevalue", + rpc_registry_deletevalue, + NET_TRANSPORT_RPC, + N_("Delete a registry value"), + N_("net rpc registry deletevalue\n" + " Delete a registry value") + }, + { + "save", + rpc_registry_save, + NET_TRANSPORT_RPC, + N_("Save a registry file"), + N_("net rpc registry save\n" + " Save a registry file") + }, + { + "dump", + rpc_registry_dump, + NET_TRANSPORT_RPC, + N_("Dump a registry file"), + N_("net rpc registry dump\n" + " Dump a registry file") + }, + { + "copy", + rpc_registry_copy, + NET_TRANSPORT_RPC, + N_("Copy a registry file"), + N_("net rpc registry copy\n" + " Copy a registry file") + }, + { + "getsd", + rpc_registry_getsd, + NET_TRANSPORT_RPC, + N_("Get security descriptor"), + N_("net rpc registry getsd\n" + " Get security descriptor") + }, + { + "import", + rpc_registry_import, + NET_TRANSPORT_RPC, + N_("Import .reg file"), + N_("net rpc registry import\n" + " Import .reg file") + }, + { + "export", + rpc_registry_export, + NET_TRANSPORT_RPC, + N_("Export .reg file"), + N_("net rpc registry export\n" + " Export .reg file") + }, + {NULL, NULL, 0, NULL, NULL} + }; + return net_run_function(c, argc, argv, "net rpc registry", func); +} diff --git a/source3/utils/net_rpc_rights.c b/source3/utils/net_rpc_rights.c new file mode 100644 index 0000000..267ce65 --- /dev/null +++ b/source3/utils/net_rpc_rights.c @@ -0,0 +1,798 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) Gerald (Jerry) Carter 2004 + Copyright (C) Guenther Deschner 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" +#include "rpc_client/init_lsa.h" +#include "../libcli/security/security.h" +#include "lib/util/string_wrappers.h" + +/******************************************************************** +********************************************************************/ + +static NTSTATUS sid_to_name(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct dom_sid *sid, + fstring name) +{ + struct policy_handle pol; + enum lsa_SidType *sid_types = NULL; + NTSTATUS status, result; + char **domains = NULL, **names = NULL; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, &pol); + + if ( !NT_STATUS_IS_OK(status) ) + return status; + + status = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &pol, 1, sid, &domains, &names, &sid_types); + + if ( NT_STATUS_IS_OK(status) ) { + if ( *domains[0] ) + fstr_sprintf( name, "%s\\%s", domains[0], names[0] ); + else + fstrcpy( name, names[0] ); + } + + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS name_to_sid(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct dom_sid *sid, const char *name) +{ + struct policy_handle pol; + enum lsa_SidType *sid_types; + NTSTATUS status, result; + struct dom_sid *sids; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* maybe its a raw SID */ + if (dom_sid_parse(name, sid)) { + return NT_STATUS_OK; + } + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, &pol); + + if ( !NT_STATUS_IS_OK(status) ) + return status; + + status = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &pol, 1, &name, + NULL, 1, &sids, &sid_types); + + if ( NT_STATUS_IS_OK(status) ) + sid_copy( sid, &sids[0] ); + + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS enum_privileges(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *ctx, + struct policy_handle *pol ) +{ + NTSTATUS status, result; + uint32_t enum_context = 0; + uint32_t pref_max_length=0x1000; + uint32_t i; + uint16_t lang_id=0; + uint16_t lang_id_sys=0; + uint16_t lang_id_desc; + struct lsa_StringLarge *description = NULL; + struct lsa_PrivArray priv_array; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_EnumPrivs(b, ctx, + pol, + &enum_context, + &priv_array, + pref_max_length, + &result); + + if ( !NT_STATUS_IS_OK(status) ) + return status; + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + /* Print results */ + + for (i = 0; i < priv_array.count; i++) { + + struct lsa_String lsa_name; + + d_printf("%30s ", + priv_array.privs[i].name.string ? priv_array.privs[i].name.string : "*unknown*" ); + + /* try to get the description */ + + init_lsa_String(&lsa_name, priv_array.privs[i].name.string); + + status = dcerpc_lsa_LookupPrivDisplayName(b, ctx, + pol, + &lsa_name, + lang_id, + lang_id_sys, + &description, + &lang_id_desc, + &result); + if (!NT_STATUS_IS_OK(status)) { + d_printf("??????\n"); + continue; + } + if (!NT_STATUS_IS_OK(result)) { + d_printf("??????\n"); + continue; + } + + d_printf("%s\n", description ? description->string : "??????"); + } + + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS check_privilege_for_user(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *ctx, + struct policy_handle *pol, + struct dom_sid *sid, + const char *right) +{ + NTSTATUS status, result; + struct lsa_RightSet rights; + uint32_t i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_EnumAccountRights(b, ctx, + pol, + sid, + &rights, + &result); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + if (!NT_STATUS_IS_OK(result)) { + return result; + } + + if (rights.count == 0) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + for (i = 0; i < rights.count; i++) { + if (strcasecmp_m(rights.names[i].string, right) == 0) { + return NT_STATUS_OK; + } + } + + return NT_STATUS_OBJECT_NAME_NOT_FOUND; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS enum_privileges_for_user(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *ctx, + struct policy_handle *pol, + struct dom_sid *sid ) +{ + NTSTATUS status, result; + struct lsa_RightSet rights; + uint32_t i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_EnumAccountRights(b, ctx, + pol, + sid, + &rights, + &result); + if (!NT_STATUS_IS_OK(status)) + return status; + if (!NT_STATUS_IS_OK(result)) + return result; + + if (rights.count == 0) { + d_printf(_("No privileges assigned\n")); + } + + for (i = 0; i < rights.count; i++) { + printf("%s\n", rights.names[i].string); + } + + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS enum_accounts_for_privilege(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *ctx, + struct policy_handle *pol, + const char *privilege) +{ + NTSTATUS status, result; + uint32_t enum_context=0; + uint32_t pref_max_length=0x1000; + struct lsa_SidArray sid_array; + uint32_t i; + fstring name; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_EnumAccounts(b, ctx, + pol, + &enum_context, + &sid_array, + pref_max_length, + &result); + if (!NT_STATUS_IS_OK(status)) + return status; + if (!NT_STATUS_IS_OK(result)) + return result; + + d_printf("%s:\n", privilege); + + for ( i=0; i<sid_array.num_sids; i++ ) { + + status = check_privilege_for_user(pipe_hnd, ctx, pol, + sid_array.sids[i].sid, + privilege); + + if ( ! NT_STATUS_IS_OK(status)) { + if ( ! NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return status; + } + continue; + } + + /* try to convert the SID to a name. Fall back to + printing the raw SID if necessary */ + status = sid_to_name( pipe_hnd, ctx, sid_array.sids[i].sid, name ); + if ( !NT_STATUS_IS_OK (status) ) + sid_to_fstring(name, sid_array.sids[i].sid); + + d_printf(" %s\n", name); + } + + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS enum_privileges_for_accounts(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *ctx, + struct policy_handle *pol) +{ + NTSTATUS status, result; + uint32_t enum_context=0; + uint32_t pref_max_length=0x1000; + struct lsa_SidArray sid_array; + uint32_t i; + fstring name; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_EnumAccounts(b, ctx, + pol, + &enum_context, + &sid_array, + pref_max_length, + &result); + if (!NT_STATUS_IS_OK(status)) + return status; + if (!NT_STATUS_IS_OK(result)) + return result; + + for ( i=0; i<sid_array.num_sids; i++ ) { + + /* try to convert the SID to a name. Fall back to + printing the raw SID if necessary */ + + status = sid_to_name(pipe_hnd, ctx, sid_array.sids[i].sid, name); + if ( !NT_STATUS_IS_OK (status) ) + sid_to_fstring(name, sid_array.sids[i].sid); + + d_printf("%s\n", name); + + status = enum_privileges_for_user(pipe_hnd, ctx, pol, + sid_array.sids[i].sid); + if ( !NT_STATUS_IS_OK(status) ) + return status; + + d_printf("\n"); + } + + return NT_STATUS_OK; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_rights_list_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle pol; + NTSTATUS status, result; + struct dom_sid sid; + fstring privname; + struct lsa_String lsa_name; + struct lsa_StringLarge *description = NULL; + uint16_t lang_id = 0; + uint16_t lang_id_sys = 0; + uint16_t lang_id_desc; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + status = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, &pol); + + if ( !NT_STATUS_IS_OK(status) ) + return status; + + /* backwards compatibility; just list available privileges if no argument */ + + if (argc == 0) { + status = enum_privileges(pipe_hnd, mem_ctx, &pol ); + goto done; + } + + if (strequal(argv[0], "privileges")) { + int i = 1; + + if (argv[1] == NULL) { + status = enum_privileges(pipe_hnd, mem_ctx, &pol ); + goto done; + } + + while ( argv[i] != NULL ) { + fstrcpy(privname, argv[i]); + init_lsa_String(&lsa_name, argv[i]); + i++; + + /* verify that this is a valid privilege for error reporting */ + status = dcerpc_lsa_LookupPrivDisplayName(b, mem_ctx, + &pol, + &lsa_name, + lang_id, + lang_id_sys, + &description, + &lang_id_desc, + &result); + if (!NT_STATUS_IS_OK(status)) { + continue; + } + status = result; + if ( !NT_STATUS_IS_OK(result) ) { + if ( NT_STATUS_EQUAL(result, NT_STATUS_NO_SUCH_PRIVILEGE)) + d_fprintf(stderr, _("No such privilege " + "exists: %s.\n"), privname); + else + d_fprintf(stderr, _("Error resolving " + "privilege display name " + "[%s].\n"), + nt_errstr(result)); + continue; + } + + status = enum_accounts_for_privilege(pipe_hnd, mem_ctx, &pol, privname); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Error enumerating " + "accounts for privilege %s [%s].\n"), + privname, nt_errstr(status)); + continue; + } + } + goto done; + } + + /* special case to enumerate all privileged SIDs with associated rights */ + + if (strequal( argv[0], "accounts")) { + int i = 1; + + if (argv[1] == NULL) { + status = enum_privileges_for_accounts(pipe_hnd, mem_ctx, &pol); + goto done; + } + + while (argv[i] != NULL) { + status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[i]); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + status = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + i++; + } + goto done; + } + + /* backward compatibility: if no keyword provided, treat the key + as an account name */ + if (argc > 1) { + d_printf("%s net rpc rights list [[accounts|privileges] " + "[name|SID]]\n", _("Usage:")); + status = NT_STATUS_OK; + goto done; + } + + status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + status = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid ); + +done: + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_rights_grant_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle dom_pol = { + .handle_type = 0, + }; + NTSTATUS status, result; + struct lsa_RightSet rights; + int i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + struct dom_sid sid; + + if (argc < 2 ) { + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc rights grant <name|SID> <rights...>\n")); + return NT_STATUS_OK; + } + + status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]); + if (NT_STATUS_EQUAL(status, NT_STATUS_NONE_MAPPED)) + status = NT_STATUS_NO_SUCH_USER; + + if (!NT_STATUS_IS_OK(status)) + goto done; + + status = dcerpc_lsa_open_policy_fallback(b, + mem_ctx, + pipe_hnd->srv_name_slash, + true, + SEC_FLAG_MAXIMUM_ALLOWED, + &out_version, + &out_revision_info, + &dom_pol, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + DBG_DEBUG("Couldn't open policy handle: %s\n", + nt_errstr(status)); + goto done; + } + + rights.count = argc-1; + rights.names = talloc_array(mem_ctx, struct lsa_StringLarge, + rights.count); + if (rights.names == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + for (i=0; i<argc-1; i++) { + init_lsa_StringLarge(&rights.names[i], argv[i+1]); + } + + status = dcerpc_lsa_AddAccountRights(b, mem_ctx, + &dom_pol, + &sid, + &rights, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + goto done; + } + + d_printf(_("Successfully granted rights.\n")); + + done: + if ( !NT_STATUS_IS_OK(status) ) { + d_fprintf(stderr, _("Failed to grant privileges for %s (%s)\n"), + argv[0], nt_errstr(status)); + } + + dcerpc_lsa_Close(b, mem_ctx, &dom_pol, &result); + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_rights_revoke_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle dom_pol; + NTSTATUS status, result; + struct lsa_RightSet rights; + struct dom_sid sid; + int i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + union lsa_revision_info out_revision_info = { + .info1 = + { + .revision = 0, + }, + }; + uint32_t out_version = 0; + + if (argc < 2 ) { + d_printf("%s\n%s", + _("Usage:"), + _(" net rpc rights revoke <name|SID> <rights...>\n")); + return NT_STATUS_OK; + } + + status = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]); + if (!NT_STATUS_IS_OK(status)) + return status; + + status = dcerpc_lsa_open_policy_fallback(b, + mem_ctx, + pipe_hnd->srv_name_slash, + true, + SEC_FLAG_MAXIMUM_ALLOWED, + &out_version, + &out_revision_info, + &dom_pol, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + DBG_DEBUG("Couldn't open policy handle: %s\n", + nt_errstr(status)); + return status; + } + + rights.count = argc-1; + rights.names = talloc_array(mem_ctx, struct lsa_StringLarge, + rights.count); + if (!rights.names) { + return NT_STATUS_NO_MEMORY; + } + + for (i=0; i<argc-1; i++) { + init_lsa_StringLarge(&rights.names[i], argv[i+1]); + } + + status = dcerpc_lsa_RemoveAccountRights(b, mem_ctx, + &dom_pol, + &sid, + false, + &rights, + &result); + if (!NT_STATUS_IS_OK(status)) + goto done; + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + d_printf(_("Successfully revoked rights.\n")); + +done: + if ( !NT_STATUS_IS_OK(status) ) { + d_fprintf(stderr,_("Failed to revoke privileges for %s (%s)\n"), + argv[0], nt_errstr(status)); + } + + dcerpc_lsa_Close(b, mem_ctx, &dom_pol, &result); + + return status; +} + + +/******************************************************************** +********************************************************************/ + +static int rpc_rights_list(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc rights list [{accounts|privileges} " + "[name|SID]]\n" + " View available/assigned privileges\n")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_rights_list_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_rights_grant(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc rights grant <name|SID> <right>\n" + " Assign privilege[s]\n")); + d_printf(_("For example:\n" + " net rpc rights grant 'VALE\\biddle' " + "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n" + " would grant the printer admin and disk manager " + "rights to the user 'VALE\\biddle'\n")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_rights_grant_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_rights_revoke(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc rights revoke <name|SID> <right>\n" + " Revoke privilege[s]\n")); + d_printf(_("For example:\n" + " net rpc rights revoke 'VALE\\biddle' " + "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n" + " would revoke the printer admin and disk manager" + " rights from the user 'VALE\\biddle'\n")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_lsarpc, 0, + rpc_rights_revoke_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +int net_rpc_rights(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + rpc_rights_list, + NET_TRANSPORT_RPC, + N_("View available/assigned privileges"), + N_("net rpc rights list\n" + " View available/assigned privileges") + }, + { + "grant", + rpc_rights_grant, + NET_TRANSPORT_RPC, + N_("Assign privilege[s]"), + N_("net rpc rights grant\n" + " Assign privilege[s]") + }, + { + "revoke", + rpc_rights_revoke, + NET_TRANSPORT_RPC, + N_("Revoke privilege[s]"), + N_("net rpc rights revoke\n" + " Revoke privilege[s]") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc rights", func); +} + +static NTSTATUS rpc_sh_rights_list(struct net_context *c, + TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_rights_list_internal(c, ctx->domain_sid, ctx->domain_name, + ctx->cli, pipe_hnd, mem_ctx, + argc, argv); +} + +static NTSTATUS rpc_sh_rights_grant(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_rights_grant_internal(c, ctx->domain_sid, ctx->domain_name, + ctx->cli, pipe_hnd, mem_ctx, + argc, argv); +} + +static NTSTATUS rpc_sh_rights_revoke(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_rights_revoke_internal(c, ctx->domain_sid, ctx->domain_name, + ctx->cli, pipe_hnd, mem_ctx, + argc, argv); +} + +struct rpc_sh_cmd *net_rpc_rights_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx) +{ + static struct rpc_sh_cmd cmds[] = { + + { "list", NULL, &ndr_table_lsarpc, rpc_sh_rights_list, + N_("View available or assigned privileges") }, + + { "grant", NULL, &ndr_table_lsarpc, rpc_sh_rights_grant, + N_("Assign privilege[s]") }, + + { "revoke", NULL, &ndr_table_lsarpc, rpc_sh_rights_revoke, + N_("Revoke privilege[s]") }, + + { NULL, NULL, 0, NULL, NULL } + }; + + return cmds; +} + diff --git a/source3/utils/net_rpc_samsync.c b/source3/utils/net_rpc_samsync.c new file mode 100644 index 0000000..e295d6a --- /dev/null +++ b/source3/utils/net_rpc_samsync.c @@ -0,0 +1,257 @@ +/* + Unix SMB/CIFS implementation. + dump the remote SAM using rpc samsync operations + + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Tim Potter 2001,2002 + Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2005 + Modified by Volker Lendecke 2002 + Copyright (C) Jeremy Allison 2005. + Copyright (C) Guenther Deschner 2008. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "../librpc/gen_ndr/ndr_netlogon.h" +#include "../librpc/gen_ndr/ndr_drsuapi.h" +#include "libnet/libnet_dssync.h" +#include "../libcli/security/security.h" +#include "passdb/machine_sid.h" + +/** + * Basic usage function for 'net rpc vampire' + * + * @param c A net_context structure + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +int rpc_vampire_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net rpc vampire ([ldif [<ldif-filename>] | [keytab] " + "[<keytab-filename]) [options]\n" + "\t to pull accounts from a remote PDC where we are a BDC\n" + "\t\t no args puts accounts in local passdb from smb.conf\n" + "\t\t ldif - put accounts in ldif format (file defaults to " + "/tmp/tmp.ldif)\n" + "\t\t keytab - put account passwords in krb5 keytab " + "(defaults to system keytab)\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +static NTSTATUS rpc_vampire_ds_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status; + struct dssync_context *ctx = NULL; + + if (!dom_sid_equal(domain_sid, get_global_sam_sid())) { + struct dom_sid_buf buf1, buf2; + d_printf(_("Cannot import users from %s at this time, " + "as the current domain:\n\t%s: %s\nconflicts " + "with the remote domain\n\t%s: %s\n" + "Perhaps you need to set: \n\n\tsecurity=user\n\t" + "workgroup=%s\n\n in your smb.conf?\n"), + domain_name, + get_global_sam_name(), + dom_sid_str_buf(get_global_sam_sid(), &buf1), + domain_name, + dom_sid_str_buf(domain_sid, &buf2), + domain_name); + return NT_STATUS_UNSUCCESSFUL; + } + + status = libnet_dssync_init_context(mem_ctx, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ctx->cli = pipe_hnd; + ctx->domain_name = domain_name; + ctx->ops = &libnet_dssync_passdb_ops; + + status = libnet_dssync(mem_ctx, ctx); + if (!NT_STATUS_IS_OK(status) && ctx->error_message) { + d_fprintf(stderr, "%s\n", ctx->error_message); + goto out; + } + + if (ctx->result_message) { + d_fprintf(stdout, "%s\n", ctx->result_message); + } + + out: + TALLOC_FREE(ctx); + + return status; +} + +int rpc_vampire_passdb(struct net_context *c, int argc, const char **argv) +{ + int ret = 0; + NTSTATUS status; + struct cli_state *cli = NULL; + struct net_dc_info dc_info; + + if (c->display_usage) { + d_printf( "%s\n" + "net rpc vampire passdb\n" + " %s\n", + _("Usage:"), + _("Dump remote SAM database to passdb")); + return 0; + } + + status = net_make_ipc_connection(c, 0, &cli); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + status = net_scan_dc(c, cli, &dc_info); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + if (!dc_info.is_ad) { + printf(_("DC is not running Active Directory, exiting\n")); + return -1; + } + + if (!c->opt_force) { + d_printf( "%s\n" + "net rpc vampire passdb\n" + " %s\n", + _("Usage:"), + _("Should not be used against Active Directory, maybe use --force")); + return -1; + } + + ret = run_rpc_command(c, cli, &ndr_table_drsuapi, + NET_FLAGS_SEAL | NET_FLAGS_TCP, + rpc_vampire_ds_internals, argc, argv); + return ret; +} + +static NTSTATUS rpc_vampire_keytab_ds_internals(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + NTSTATUS status; + struct dssync_context *ctx = NULL; + + status = libnet_dssync_init_context(mem_ctx, + &ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ctx->force_full_replication = c->opt_force_full_repl ? true : false; + ctx->clean_old_entries = c->opt_clean_old_entries ? true : false; + + if (argc < 1) { + /* the caller should ensure that a filename is provided */ + return NT_STATUS_INVALID_PARAMETER; + } else { + ctx->output_filename = argv[0]; + } + + if (argc >= 2) { + ctx->object_dns = &argv[1]; + ctx->object_count = argc - 1; + ctx->single_object_replication = c->opt_single_obj_repl ? true + : false; + } + + ctx->cli = pipe_hnd; + ctx->domain_name = domain_name; + ctx->ops = &libnet_dssync_keytab_ops; + + status = libnet_dssync(mem_ctx, ctx); + if (!NT_STATUS_IS_OK(status) && ctx->error_message) { + d_fprintf(stderr, "%s\n", ctx->error_message); + goto out; + } + + if (ctx->result_message) { + d_fprintf(stdout, "%s\n", ctx->result_message); + } + + out: + TALLOC_FREE(ctx); + + return status; +} + +/** + * Basic function for 'net rpc vampire keytab' + * + * @param c A net_context structure + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +int rpc_vampire_keytab(struct net_context *c, int argc, const char **argv) +{ + int ret = 0; + NTSTATUS status; + struct cli_state *cli = NULL; + struct net_dc_info dc_info; + + if (c->display_usage || (argc < 1)) { + d_printf("%s\n%s", + _("Usage:"), + _("net rpc vampire keytab <keytabfile>\n" + " Dump remote SAM database to Kerberos keytab " + "file\n")); + return 0; + } + + status = net_make_ipc_connection(c, 0, &cli); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + status = net_scan_dc(c, cli, &dc_info); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + if (!dc_info.is_ad) { + printf(_("DC is not running Active Directory, exiting\n")); + return -1; + } + + ret = run_rpc_command(c, cli, &ndr_table_drsuapi, + NET_FLAGS_SEAL | NET_FLAGS_TCP, + rpc_vampire_keytab_ds_internals, argc, argv); + return ret; +} diff --git a/source3/utils/net_rpc_service.c b/source3/utils/net_rpc_service.c new file mode 100644 index 0000000..a0fbc51 --- /dev/null +++ b/source3/utils/net_rpc_service.c @@ -0,0 +1,1138 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) Gerald (Jerry) Carter 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_svcctl.h" +#include "../librpc/gen_ndr/ndr_svcctl_c.h" +#include "lib/util/string_wrappers.h" + +struct svc_state_msg { + uint32_t flag; + const char *message; +}; + +static struct svc_state_msg state_msg_table[] = { + { SVCCTL_STOPPED, N_("stopped") }, + { SVCCTL_START_PENDING, N_("start pending") }, + { SVCCTL_STOP_PENDING, N_("stop pending") }, + { SVCCTL_RUNNING, N_("running") }, + { SVCCTL_CONTINUE_PENDING, N_("resume pending") }, + { SVCCTL_PAUSE_PENDING, N_("pause pending") }, + { SVCCTL_PAUSED, N_("paused") }, + { 0, NULL } +}; + + +/******************************************************************** +********************************************************************/ +const char *svc_status_string( uint32_t state ) +{ + fstring msg; + int i; + + fstr_sprintf( msg, _("Unknown State [%d]"), state ); + + for ( i=0; state_msg_table[i].message; i++ ) { + if ( state_msg_table[i].flag == state ) { + fstrcpy( msg, state_msg_table[i].message ); + break; + } + } + + return talloc_strdup(talloc_tos(), msg); +} + +/******************************************************************** +********************************************************************/ + +static WERROR open_service(struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + struct policy_handle *hSCM, + const char *service, + uint32_t access_mask, + struct policy_handle *hService) +{ + NTSTATUS status; + WERROR result; + + status = dcerpc_svcctl_OpenServiceW(b, mem_ctx, + hSCM, + service, + access_mask, + hService, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Failed to open service. [%s]\n"), + nt_errstr(status)); + return result; + } + if (!W_ERROR_IS_OK(result) ) { + d_fprintf(stderr, _("Failed to open service. [%s]\n"), + win_errstr(result)); + return result; + } + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +static WERROR open_scm(struct dcerpc_binding_handle *b, + TALLOC_CTX *mem_ctx, + const char *server_name, + uint32_t access_mask, + struct policy_handle *hSCM) +{ + NTSTATUS status; + WERROR result; + + status = dcerpc_svcctl_OpenSCManagerW(b, mem_ctx, + server_name, + NULL, + access_mask, + hSCM, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, + _("Failed to open Service Control Manager. [%s]\n"), + nt_errstr(status)); + return result; + } + if (!W_ERROR_IS_OK(result)) { + d_fprintf(stderr, + _("Failed to open Service Control Manager. [%s]\n"), + win_errstr(result)); + return result; + } + + return WERR_OK; +} + +/******************************************************************** +********************************************************************/ + +static WERROR query_service_state(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hSCM, + const char *service, + uint32_t *state ) +{ + struct policy_handle hService; + struct SERVICE_STATUS service_status; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* now cycle until the status is actually 'watch_state' */ + + result = open_service(b, mem_ctx, hSCM, service, + SC_RIGHT_SVC_QUERY_STATUS, + &hService); + if (!W_ERROR_IS_OK(result) ) { + return result; + } + + status = dcerpc_svcctl_QueryServiceStatus(b, mem_ctx, + &hService, + &service_status, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + goto done; + } + if (!W_ERROR_IS_OK(result)) { + goto done; + } + + *state = service_status.state; + + done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + + return result; +} + +/******************************************************************** +********************************************************************/ + +static WERROR watch_service_state(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hSCM, + const char *service, + uint32_t watch_state, + uint32_t *final_state ) +{ + uint32_t i; + uint32_t state = 0; + WERROR result = WERR_GEN_FAILURE; + + + i = 0; + while ( (state != watch_state ) && i<30 ) { + /* get the status */ + + result = query_service_state(pipe_hnd, mem_ctx, hSCM, service, &state ); + if ( !W_ERROR_IS_OK(result) ) { + break; + } + + d_printf("."); + i++; + usleep( 100 ); + } + d_printf("\n"); + + *final_state = state; + + return result; +} + +/******************************************************************** +********************************************************************/ + +static WERROR control_service(struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + struct policy_handle *hSCM, + const char *service, + uint32_t control, + uint32_t watch_state ) +{ + struct policy_handle hService; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + struct SERVICE_STATUS service_status; + uint32_t state = 0; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + /* Open the Service */ + + result = open_service(b, mem_ctx, hSCM, service, + (SC_RIGHT_SVC_STOP|SC_RIGHT_SVC_PAUSE_CONTINUE), + &hService); + if (!W_ERROR_IS_OK(result) ) { + return result; + } + + /* get the status */ + + status = dcerpc_svcctl_ControlService(b, mem_ctx, + &hService, + control, + &service_status, + &result); + + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Control service request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(result) ) { + d_fprintf(stderr, _("Control service request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + /* loop -- checking the state until we are where we want to be */ + + result = watch_service_state(pipe_hnd, mem_ctx, hSCM, service, watch_state, &state ); + + d_printf(_("%s service is %s.\n"), service, svc_status_string(state)); + +done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + + return result; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_list_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM; + struct ENUM_SERVICE_STATUSW *services = NULL; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + int i; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + uint8_t *buffer; + uint32_t buf_size = 0; + uint32_t bytes_needed = 0; + uint32_t num_services = 0; + uint32_t resume_handle = 0; + + if (argc != 0 ) { + d_printf("%s net rpc service list\n", _("Usage:")); + return NT_STATUS_OK; + } + + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + buffer = talloc_array(mem_ctx, uint8_t, buf_size); + if (buffer == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + do { + status = dcerpc_svcctl_EnumServicesStatusW(b, mem_ctx, + &hSCM, + SERVICE_TYPE_WIN32, + SERVICE_STATE_ALL, + buffer, + buf_size, + &bytes_needed, + &num_services, + &resume_handle, + &result); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + _("Failed to enumerate services. [%s]\n"), + nt_errstr(status)); + break; + } + + if (W_ERROR_EQUAL(result, WERR_MORE_DATA) && bytes_needed > 0) { + buf_size = bytes_needed; + buffer = talloc_realloc(mem_ctx, buffer, uint8_t, bytes_needed); + if (buffer == NULL) { + status = NT_STATUS_NO_MEMORY; + break; + } + continue; + } + + if (!W_ERROR_IS_OK(result)) { + status = werror_to_ntstatus(result); + d_fprintf(stderr, + _("Failed to enumerate services. [%s]\n"), + win_errstr(result)); + break; + } + + if ( num_services == 0 ) { + d_printf(_("No services returned\n")); + break; + } + + { + enum ndr_err_code ndr_err; + DATA_BLOB blob; + struct ndr_pull *ndr; + + blob.length = buf_size; + blob.data = talloc_steal(mem_ctx, buffer); + + services = talloc_array(mem_ctx, struct ENUM_SERVICE_STATUSW, num_services); + if (!services) { + status = NT_STATUS_NO_MEMORY; + break; + } + + ndr = ndr_pull_init_blob(&blob, mem_ctx); + if (ndr == NULL) { + status = NT_STATUS_NO_MEMORY; + break; + } + + ndr_err = ndr_pull_ENUM_SERVICE_STATUSW_array( + ndr, num_services, services); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + break; + } + + for ( i=0; i<num_services; i++ ) { + d_printf("%-20s \"%s\"\n", + services[i].service_name, + services[i].display_name); + } + } + + } while (W_ERROR_EQUAL(result, WERR_MORE_DATA)); + +done: + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return status; +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_status_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM, hService; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + struct SERVICE_STATUS service_status; + struct QUERY_SERVICE_CONFIG config; + uint32_t buf_size = sizeof(config); + uint32_t ret_size = 0; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + /* Open the Service */ + + result = open_service(b, mem_ctx, &hSCM, argv[0], + (SC_RIGHT_SVC_QUERY_STATUS|SC_RIGHT_SVC_QUERY_CONFIG), + &hService); + if (!W_ERROR_IS_OK(result) ) { + goto done; + } + + /* get the status */ + + status = dcerpc_svcctl_QueryServiceStatus(b, mem_ctx, + &hService, + &service_status, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Query status request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + + if (!W_ERROR_IS_OK(result) ) { + d_fprintf(stderr, _("Query status request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + d_printf(_("%s service is %s.\n"), argv[0], + svc_status_string(service_status.state)); + + /* get the config */ + + status = dcerpc_svcctl_QueryServiceConfigW(b, mem_ctx, + &hService, + &config, + buf_size, + &ret_size, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Query config request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + + if (W_ERROR_EQUAL(result, WERR_INSUFFICIENT_BUFFER)) { + buf_size = ret_size; + status = dcerpc_svcctl_QueryServiceConfigW(b, mem_ctx, + &hService, + &config, + buf_size, + &ret_size, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Query config request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + } + + if (!W_ERROR_IS_OK(result) ) { + d_fprintf(stderr, _("Query config request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + /* print out the configuration information for the service */ + + d_printf(_("Configuration details:\n")); + d_printf(_("\tControls Accepted = 0x%x\n"), + service_status.controls_accepted); + d_printf(_("\tService Type = 0x%x\n"), config.service_type); + d_printf(_("\tStart Type = 0x%x\n"), config.start_type); + d_printf(_("\tError Control = 0x%x\n"), config.error_control); + d_printf(_("\tTag ID = 0x%x\n"), config.tag_id); + + if (config.executablepath) { + d_printf(_("\tExecutable Path = %s\n"), + config.executablepath); + } + + if (config.loadordergroup) { + d_printf(_("\tLoad Order Group = %s\n"), + config.loadordergroup); + } + + if (config.dependencies) { + d_printf(_("\tDependencies = %s\n"), + config.dependencies); + } + + if (config.startname) { + d_printf(_("\tStart Name = %s\n"), config.startname); + } + + if (config.displayname) { + d_printf(_("\tDisplay Name = %s\n"), + config.displayname); + } + +done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_stop_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM; + WERROR result = WERR_GEN_FAILURE; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0], + SVCCTL_CONTROL_STOP, SVCCTL_STOPPED ); + + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_pause_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM; + WERROR result = WERR_GEN_FAILURE; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0], + SVCCTL_CONTROL_PAUSE, SVCCTL_PAUSED ); + + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_resume_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM; + WERROR result = WERR_GEN_FAILURE; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + result = control_service(pipe_hnd, mem_ctx, &hSCM, argv[0], + SVCCTL_CONTROL_CONTINUE, SVCCTL_RUNNING ); + + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_start_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv ) +{ + struct policy_handle hSCM, hService; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + uint32_t state = 0; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service status <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + + /* Open the Service */ + + result = open_service(b, mem_ctx, &hSCM, argv[0], + SC_RIGHT_SVC_START, + &hService); + if (!W_ERROR_IS_OK(result) ) { + goto done; + } + + /* get the status */ + + status = dcerpc_svcctl_StartServiceW(b, mem_ctx, + &hService, + 0, + NULL, + &result); + + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Query status request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(result) ) { + d_fprintf(stderr, _("Query status request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + result = watch_service_state(pipe_hnd, mem_ctx, &hSCM, argv[0], SVCCTL_RUNNING, &state ); + + if ( W_ERROR_IS_OK(result) && (state == SVCCTL_RUNNING) ) + d_printf(_("Successfully started service: %s\n"), + argv[0] ); + else + d_fprintf(stderr,_("Failed to start service: %s [%s]\n"), + argv[0], win_errstr(result) ); + +done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_delete_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle hSCM, hService; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 1 ) { + d_printf("%s net rpc service delete <service>\n", _("Usage:")); + return NT_STATUS_OK; + } + + ZERO_STRUCT(hSCM); + ZERO_STRUCT(hService); + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_ENUMERATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + /* Open the Service */ + + result = open_service(b, mem_ctx, &hSCM, argv[0], + SERVICE_ALL_ACCESS, + &hService); + if (!W_ERROR_IS_OK(result) ) { + goto done; + } + + /* Delete the Service */ + + status = dcerpc_svcctl_DeleteService(b, mem_ctx, + &hService, + &result); + + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Delete service request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(result)) { + d_fprintf(stderr, _("Delete service request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + d_printf(_("Successfully deleted Service: %s\n"), argv[0]); + + done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static NTSTATUS rpc_service_create_internal(struct net_context *c, + const struct dom_sid *domain_sid, + const char *domain_name, + struct cli_state *cli, + struct rpc_pipe_client *pipe_hnd, + TALLOC_CTX *mem_ctx, + int argc, + const char **argv) +{ + struct policy_handle hSCM, hService; + WERROR result = WERR_GEN_FAILURE; + NTSTATUS status; + const char *ServiceName; + const char *DisplayName; + const char *binary_path; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + if (argc != 3) { + d_printf("%s net rpc service create <service> " + "<displayname> <binarypath>\n", _("Usage:")); + return NT_STATUS_OK; + } + + ZERO_STRUCT(hSCM); + ZERO_STRUCT(hService); + + /* Open the Service Control Manager */ + result = open_scm(b, mem_ctx, pipe_hnd->srv_name_slash, + SC_RIGHT_MGR_CREATE_SERVICE, + &hSCM); + if (!W_ERROR_IS_OK(result)) { + return werror_to_ntstatus(result); + } + + /* Create the service */ + + ServiceName = argv[0]; + DisplayName = argv[1]; + binary_path = argv[2]; + + status = dcerpc_svcctl_CreateServiceW(b, mem_ctx, + &hSCM, + ServiceName, + DisplayName, + SERVICE_ALL_ACCESS, + SERVICE_TYPE_WIN32_OWN_PROCESS, + SVCCTL_DEMAND_START, + SVCCTL_SVC_ERROR_NORMAL, + binary_path, + NULL, /* LoadOrderGroupKey */ + NULL, /* TagId */ + NULL, /* dependencies */ + 0, /* dependencies_size */ + NULL, /* service_start_name */ + NULL, /* password */ + 0, /* password_size */ + &hService, + &result); + if (!NT_STATUS_IS_OK(status)) { + result = ntstatus_to_werror(status); + d_fprintf(stderr, _("Create service request failed. [%s]\n"), + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(result)) { + d_fprintf(stderr, _("Create service request failed. [%s]\n"), + win_errstr(result)); + goto done; + } + + d_printf(_("Successfully created Service: %s\n"), argv[0]); + + done: + if (is_valid_policy_hnd(&hService)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hService, &_result); + } + if (is_valid_policy_hnd(&hSCM)) { + WERROR _result; + dcerpc_svcctl_CloseServiceHandle(b, mem_ctx, &hSCM, &_result); + } + + return werror_to_ntstatus(result); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_list(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service list\n" + " %s\n", + _("Usage:"), + _("View configured Win32 services")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_list_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_start(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service start <service>\n" + " %s\n", + _("Usage:"), + _("Start a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_start_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_stop(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service stop <service>\n" + " %s\n", + _("Usage:"), + _("Stop a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_stop_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_resume(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service resume <service>\n" + " %s\n", + _("Usage:"), + _("Resume a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_resume_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_pause(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service pause <service>\n" + " %s\n", + _("Usage:"), + _("Pause a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_pause_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_status(struct net_context *c, int argc, const char **argv ) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service status <service>\n" + " %s\n", + _("Usage:"), + _("Show the current status of a service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_status_internal, argc, argv ); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_delete(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service delete <service>\n" + " %s\n", + _("Usage:"), + _("Delete a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_delete_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +static int rpc_service_create(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net rpc service create <service>\n" + " %s\n", + _("Usage:"), + _("Create a Win32 service")); + return 0; + } + + return run_rpc_command(c, NULL, &ndr_table_svcctl, 0, + rpc_service_create_internal, argc, argv); +} + +/******************************************************************** +********************************************************************/ + +int net_rpc_service(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + rpc_service_list, + NET_TRANSPORT_RPC, + N_("View configured Win32 services"), + N_("net rpc service list\n" + " View configured Win32 services") + }, + { + "start", + rpc_service_start, + NET_TRANSPORT_RPC, + N_("Start a service"), + N_("net rpc service start\n" + " Start a service") + }, + { + "stop", + rpc_service_stop, + NET_TRANSPORT_RPC, + N_("Stop a service"), + N_("net rpc service stop\n" + " Stop a service") + }, + { + "pause", + rpc_service_pause, + NET_TRANSPORT_RPC, + N_("Pause a service"), + N_("net rpc service pause\n" + " Pause a service") + }, + { + "resume", + rpc_service_resume, + NET_TRANSPORT_RPC, + N_("Resume a paused service"), + N_("net rpc service resume\n" + " Resume a service") + }, + { + "status", + rpc_service_status, + NET_TRANSPORT_RPC, + N_("View current status of a service"), + N_("net rpc service status\n" + " View current status of a service") + }, + { + "delete", + rpc_service_delete, + NET_TRANSPORT_RPC, + N_("Delete a service"), + N_("net rpc service delete\n" + " Deletes a service") + }, + { + "create", + rpc_service_create, + NET_TRANSPORT_RPC, + N_("Create a service"), + N_("net rpc service create\n" + " Creates a service") + }, + + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc service",func); +} diff --git a/source3/utils/net_rpc_sh_acct.c b/source3/utils/net_rpc_sh_acct.c new file mode 100644 index 0000000..3fc6568 --- /dev/null +++ b/source3/utils/net_rpc_sh_acct.c @@ -0,0 +1,489 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2006 Volker Lendecke (vl@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/rpc_client.h" +#include "../librpc/gen_ndr/ndr_samr_c.h" +#include "../libcli/security/security.h" + +/* + * Do something with the account policies. Read them all, run a function on + * them and possibly write them back. "fn" has to return the container index + * it has modified, it can return 0 for no change. + */ + +static NTSTATUS rpc_sh_acct_do(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv, + int (*fn)(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv)) +{ + struct policy_handle connect_pol, domain_pol; + NTSTATUS status, result; + union samr_DomainInfo *info1 = NULL; + union samr_DomainInfo *info3 = NULL; + union samr_DomainInfo *info12 = NULL; + int store; + struct dcerpc_binding_handle *b = pipe_hnd->binding_handle; + + ZERO_STRUCT(connect_pol); + ZERO_STRUCT(domain_pol); + + /* Get sam policy handle */ + + status = dcerpc_samr_Connect2(b, mem_ctx, + pipe_hnd->desthost, + MAXIMUM_ALLOWED_ACCESS, + &connect_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + /* Get domain policy handle */ + + status = dcerpc_samr_OpenDomain(b, mem_ctx, + &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + ctx->domain_sid, + &domain_pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + &domain_pol, + 1, + &info1, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("query_domain_info level 1 failed: %s\n"), + nt_errstr(result)); + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + &domain_pol, + 3, + &info3, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("query_domain_info level 3 failed: %s\n"), + nt_errstr(result)); + goto done; + } + + status = dcerpc_samr_QueryDomainInfo(b, mem_ctx, + &domain_pol, + 12, + &info12, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + d_fprintf(stderr, _("query_domain_info level 12 failed: %s\n"), + nt_errstr(result)); + goto done; + } + + store = fn(c, mem_ctx, ctx, &info1->info1, &info3->info3, + &info12->info12, argc, argv); + + if (store <= 0) { + /* Don't save anything */ + goto done; + } + + switch (store) { + case 1: + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + &domain_pol, + 1, + info1, + &result); + break; + case 3: + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + &domain_pol, + 3, + info3, + &result); + break; + case 12: + status = dcerpc_samr_SetDomainInfo(b, mem_ctx, + &domain_pol, + 12, + info12, + &result); + break; + default: + d_fprintf(stderr, _("Got unexpected info level %d\n"), store); + status = NT_STATUS_INTERNAL_ERROR; + goto done; + } + + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = result; + + done: + if (is_valid_policy_hnd(&domain_pol)) { + dcerpc_samr_Close(b, mem_ctx, &domain_pol, &result); + } + if (is_valid_policy_hnd(&connect_pol)) { + dcerpc_samr_Close(b, mem_ctx, &connect_pol, &result); + } + + return status; +} + +static int account_show(struct net_context *c, + TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 0) { + d_fprintf(stderr, "%s %s\n", _("Usage:"), ctx->whoami); + return -1; + } + + d_printf(_("Minimum password length: %d\n"), i1->min_password_length); + d_printf(_("Password history length: %d\n"), + i1->password_history_length); + + d_printf(_("Minimum password age: ")); + if (!nt_time_is_zero((NTTIME *)&i1->min_password_age)) { + time_t t = nt_time_to_unix_abs((NTTIME *)&i1->min_password_age); + d_printf(_("%d seconds\n"), (int)t); + } else { + d_printf(_("not set\n")); + } + + d_printf(_("Maximum password age: ")); + if (nt_time_is_set((NTTIME *)&i1->max_password_age)) { + time_t t = nt_time_to_unix_abs((NTTIME *)&i1->max_password_age); + d_printf(_("%d seconds\n"), (int)t); + } else { + d_printf(_("not set\n")); + } + + d_printf(_("Bad logon attempts: %d\n"), i12->lockout_threshold); + + if (i12->lockout_threshold != 0) { + + d_printf(_("Account lockout duration: ")); + if (nt_time_is_set(&i12->lockout_duration)) { + time_t t = nt_time_to_unix_abs(&i12->lockout_duration); + d_printf(_("%d seconds\n"), (int)t); + } else { + d_printf(_("not set\n")); + } + + d_printf(_("Bad password count reset after: ")); + if (nt_time_is_set(&i12->lockout_window)) { + time_t t = nt_time_to_unix_abs(&i12->lockout_window); + d_printf(_("%d seconds\n"), (int)t); + } else { + d_printf(_("not set\n")); + } + } + + d_printf(_("Disconnect users when logon hours expire: %s\n"), + nt_time_is_zero(&i3->force_logoff_time) ? _("yes") : _("no")); + + d_printf(_("User must logon to change password: %s\n"), + (i1->password_properties & 0x2) ? _("yes") : _("no")); + + return 0; /* Don't save */ +} + +static NTSTATUS rpc_sh_acct_pol_show(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) { + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_show); +} + +static int account_set_badpw(struct net_context *c, + TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, "%s %s <count>\n", _("Usage:"), ctx->whoami); + return -1; + } + + i12->lockout_threshold = atoi(argv[0]); + d_printf(_("Setting bad password count to %d\n"), + i12->lockout_threshold); + + return 12; +} + +static NTSTATUS rpc_sh_acct_set_badpw(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_badpw); +} + +static int account_set_lockduration(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + unix_to_nt_time_abs(&i12->lockout_duration, atoi(argv[0])); + d_printf(_("Setting lockout duration to %d seconds\n"), + (int)nt_time_to_unix_abs(&i12->lockout_duration)); + + return 12; +} + +static NTSTATUS rpc_sh_acct_set_lockduration(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_lockduration); +} + +static int account_set_resetduration(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + unix_to_nt_time_abs(&i12->lockout_window, atoi(argv[0])); + d_printf(_("Setting bad password reset duration to %d seconds\n"), + (int)nt_time_to_unix_abs(&i12->lockout_window)); + + return 12; +} + +static NTSTATUS rpc_sh_acct_set_resetduration(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_resetduration); +} + +static int account_set_minpwage(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + unix_to_nt_time_abs((NTTIME *)&i1->min_password_age, atoi(argv[0])); + d_printf(_("Setting minimum password age to %d seconds\n"), + (int)nt_time_to_unix_abs((NTTIME *)&i1->min_password_age)); + + return 1; +} + +static NTSTATUS rpc_sh_acct_set_minpwage(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_minpwage); +} + +static int account_set_maxpwage(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + unix_to_nt_time_abs((NTTIME *)&i1->max_password_age, atoi(argv[0])); + d_printf(_("Setting maximum password age to %d seconds\n"), + (int)nt_time_to_unix_abs((NTTIME *)&i1->max_password_age)); + + return 1; +} + +static NTSTATUS rpc_sh_acct_set_maxpwage(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_maxpwage); +} + +static int account_set_minpwlen(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + i1->min_password_length = atoi(argv[0]); + d_printf(_("Setting minimum password length to %d\n"), + i1->min_password_length); + + return 1; +} + +static NTSTATUS rpc_sh_acct_set_minpwlen(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_minpwlen); +} + +static int account_set_pwhistlen(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct samr_DomInfo1 *i1, + struct samr_DomInfo3 *i3, + struct samr_DomInfo12 *i12, + int argc, const char **argv) +{ + if (argc != 1) { + d_fprintf(stderr, _("Usage: %s <count>\n"), ctx->whoami); + return -1; + } + + i1->password_history_length = atoi(argv[0]); + d_printf(_("Setting password history length to %d\n"), + i1->password_history_length); + + return 1; +} + +static NTSTATUS rpc_sh_acct_set_pwhistlen(struct net_context *c, + TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_sh_acct_do(c, mem_ctx, ctx, pipe_hnd, argc, argv, + account_set_pwhistlen); +} + +struct rpc_sh_cmd *net_rpc_acct_cmds(struct net_context *c, TALLOC_CTX *mem_ctx, + struct rpc_sh_ctx *ctx) +{ + static struct rpc_sh_cmd cmds[9] = { + { "show", NULL, &ndr_table_samr, rpc_sh_acct_pol_show, + N_("Show current account policy settings") }, + { "badpw", NULL, &ndr_table_samr, rpc_sh_acct_set_badpw, + N_("Set bad password count before lockout") }, + { "lockduration", NULL, &ndr_table_samr, rpc_sh_acct_set_lockduration, + N_("Set account lockout duration") }, + { "resetduration", NULL, &ndr_table_samr, + rpc_sh_acct_set_resetduration, + N_("Set bad password count reset duration") }, + { "minpwage", NULL, &ndr_table_samr, rpc_sh_acct_set_minpwage, + N_("Set minimum password age") }, + { "maxpwage", NULL, &ndr_table_samr, rpc_sh_acct_set_maxpwage, + N_("Set maximum password age") }, + { "minpwlen", NULL, &ndr_table_samr, rpc_sh_acct_set_minpwlen, + N_("Set minimum password length") }, + { "pwhistlen", NULL, &ndr_table_samr, rpc_sh_acct_set_pwhistlen, + N_("Set the password history length") }, + { NULL, NULL, 0, NULL, NULL } + }; + + return cmds; +} diff --git a/source3/utils/net_rpc_shell.c b/source3/utils/net_rpc_shell.c new file mode 100644 index 0000000..1ea7080 --- /dev/null +++ b/source3/utils/net_rpc_shell.c @@ -0,0 +1,306 @@ +/* + * Unix SMB/CIFS implementation. + * Shell around net rpc subcommands + * Copyright (C) Volker Lendecke 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_samr.h" +#include "lib/netapi/netapi.h" +#include "lib/netapi/netapi_net.h" +#include "../libcli/smbreadline/smbreadline.h" +#include "libsmb/libsmb.h" +#include "libcli/security/dom_sid.h" + +#include <popt.h> + +static NTSTATUS rpc_sh_info(struct net_context *c, + TALLOC_CTX *mem_ctx, struct rpc_sh_ctx *ctx, + struct rpc_pipe_client *pipe_hnd, + int argc, const char **argv) +{ + return rpc_info_internals(c, ctx->domain_sid, ctx->domain_name, + ctx->cli, pipe_hnd, mem_ctx, + argc, argv); +} + +static struct rpc_sh_ctx *this_ctx; + +static char **completion_fn(const char *text, int start, int end) +{ + char **cmds = NULL; + int n_cmds = 0; + struct rpc_sh_cmd *c; + + if (start != 0) { + return NULL; + } + + ADD_TO_ARRAY(NULL, char *, SMB_STRDUP(text), &cmds, &n_cmds); + + for (c = this_ctx->cmds; c->name != NULL; c++) { + bool match = (strncmp(text, c->name, strlen(text)) == 0); + + if (match) { + ADD_TO_ARRAY(NULL, char *, SMB_STRDUP(c->name), + &cmds, &n_cmds); + } + } + + if (n_cmds == 2) { + SAFE_FREE(cmds[0]); + cmds[0] = cmds[1]; + n_cmds -= 1; + } + + ADD_TO_ARRAY(NULL, char *, NULL, &cmds, &n_cmds); + return cmds; +} + +static NTSTATUS net_sh_run(struct net_context *c, + struct rpc_sh_ctx *ctx, struct rpc_sh_cmd *cmd, + int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx; + struct rpc_pipe_client *pipe_hnd = NULL; + NTSTATUS status; + + mem_ctx = talloc_new(ctx); + if (mem_ctx == NULL) { + d_fprintf(stderr, _("talloc_new failed\n")); + return NT_STATUS_NO_MEMORY; + } + + status = cli_rpc_pipe_open_noauth(ctx->cli, cmd->table, + &pipe_hnd); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not open pipe: %s\n"), + nt_errstr(status)); + return status; + } + + status = cmd->fn(c, mem_ctx, ctx, pipe_hnd, argc, argv); + + TALLOC_FREE(pipe_hnd); + + talloc_destroy(mem_ctx); + + return status; +} + +static bool net_sh_process(struct net_context *c, + struct rpc_sh_ctx *ctx, + int argc, const char **argv) +{ + struct rpc_sh_cmd *cmd; + struct rpc_sh_ctx *new_ctx; + NTSTATUS status; + + if (argc == 0) { + return true; + } + + if (ctx == this_ctx) { + + /* We've been called from the cmd line */ + if (strequal(argv[0], "..") && + (this_ctx->parent != NULL)) { + new_ctx = this_ctx->parent; + TALLOC_FREE(this_ctx); + this_ctx = new_ctx; + return true; + } + } + + if (strequal(argv[0], "exit") || + strequal(argv[0], "quit") || + strequal(argv[0], "q")) { + return false; + } + + if (strequal(argv[0], "help") || strequal(argv[0], "?")) { + for (cmd = ctx->cmds; cmd->name != NULL; cmd++) { + if (ctx != this_ctx) { + d_printf("%s ", ctx->whoami); + } + d_printf("%-15s %s\n", cmd->name, cmd->help); + } + return true; + } + + for (cmd = ctx->cmds; cmd->name != NULL; cmd++) { + if (strequal(cmd->name, argv[0])) { + break; + } + } + + if (cmd->name == NULL) { + /* None found */ + d_fprintf(stderr,_( "%s: unknown cmd\n"), argv[0]); + return true; + } + + new_ctx = talloc(ctx, struct rpc_sh_ctx); + if (new_ctx == NULL) { + d_fprintf(stderr, _("talloc failed\n")); + return false; + } + new_ctx->cli = ctx->cli; + new_ctx->whoami = talloc_asprintf(new_ctx, "%s %s", + ctx->whoami, cmd->name); + new_ctx->thiscmd = talloc_strdup(new_ctx, cmd->name); + + if (cmd->sub != NULL) { + new_ctx->cmds = cmd->sub(c, new_ctx, ctx); + } else { + new_ctx->cmds = NULL; + } + + new_ctx->parent = ctx; + new_ctx->domain_name = ctx->domain_name; + new_ctx->domain_sid = ctx->domain_sid; + + argc -= 1; + argv += 1; + + if (cmd->sub != NULL) { + if (argc == 0) { + this_ctx = new_ctx; + return true; + } + return net_sh_process(c, new_ctx, argc, argv); + } + + status = net_sh_run(c, new_ctx, cmd, argc, argv); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("%s failed: %s\n"), new_ctx->whoami, + nt_errstr(status)); + } + + return true; +} + +static struct rpc_sh_cmd sh_cmds[6] = { + + { "info", NULL, &ndr_table_samr, rpc_sh_info, + N_("Print information about the domain connected to") }, + + { "rights", net_rpc_rights_cmds, 0, NULL, + N_("List/Grant/Revoke user rights") }, + + { "share", net_rpc_share_cmds, 0, NULL, + N_("List/Add/Remove etc shares") }, + + { "user", net_rpc_user_cmds, 0, NULL, + N_("List/Add/Remove user info") }, + + { "account", net_rpc_acct_cmds, 0, NULL, + N_("Show/Change account policy settings") }, + + { NULL, NULL, 0, NULL, NULL } +}; + +int net_rpc_shell(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + struct rpc_sh_ctx *ctx; + struct dom_sid_buf buf; + NET_API_STATUS net_api_status; + + if (argc != 0 || c->display_usage) { + d_printf("%s\nnet rpc shell\n", _("Usage:")); + return -1; + } + + net_api_status = libnetapi_net_init(&c->netapi_ctx, c->lp_ctx, c->creds); + if (net_api_status != 0) { + return -1; + } + + ctx = talloc(NULL, struct rpc_sh_ctx); + if (ctx == NULL) { + d_fprintf(stderr, _("talloc failed\n")); + return -1; + } + + status = net_make_ipc_connection(c, 0, &(ctx->cli)); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not open connection: %s\n"), + nt_errstr(status)); + return -1; + } + + ctx->cmds = sh_cmds; + ctx->whoami = "net rpc"; + ctx->parent = NULL; + + status = net_get_remote_domain_sid(ctx->cli, ctx, &ctx->domain_sid, + &ctx->domain_name); + if (!NT_STATUS_IS_OK(status)) { + return -1; + } + + d_printf(_("Talking to domain %s (%s)\n"), ctx->domain_name, + dom_sid_str_buf(ctx->domain_sid, &buf)); + + this_ctx = ctx; + + while(1) { + char *prompt = NULL; + char *line = NULL; + int ret; + + if (asprintf(&prompt, "%s> ", this_ctx->whoami) < 0) { + break; + } + + line = smb_readline(prompt, NULL, completion_fn); + SAFE_FREE(prompt); + + if (line == NULL) { + break; + } + + ret = poptParseArgvString(line, &argc, &argv); + if (ret == POPT_ERROR_NOARG) { + SAFE_FREE(line); + continue; + } + if (ret != 0) { + d_fprintf(stderr, _("cmdline invalid: %s\n"), + poptStrerror(ret)); + SAFE_FREE(line); + return false; + } + + if ((line[0] != '\n') && + (!net_sh_process(c, this_ctx, argc, argv))) { + SAFE_FREE(line); + break; + } + SAFE_FREE(line); + } + + cli_shutdown(ctx->cli); + + TALLOC_FREE(ctx); + + return 0; +} diff --git a/source3/utils/net_rpc_trust.c b/source3/utils/net_rpc_trust.c new file mode 100644 index 0000000..a3354ad --- /dev/null +++ b/source3/utils/net_rpc_trust.c @@ -0,0 +1,735 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2011 Sumit Bose (sbose@redhat.com) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + + +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/cli_lsarpc.h" +#include "librpc/gen_ndr/ndr_drsblobs.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "../libcli/security/dom_sid.h" +#include "libsmb/libsmb.h" + +#include "lib/crypto/gnutls_helpers.h" +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#define ARG_OTHERSERVER "otherserver=" +#define ARG_OTHERUSER "otheruser=" +#define ARG_OTHERDOMAINSID "otherdomainsid=" +#define ARG_OTHERDOMAIN "otherdomain=" +#define ARG_OTHERNETBIOSDOMAIN "other_netbios_domain=" +#define ARG_TRUSTPW "trustpw=" + +enum trust_op { + TRUST_CREATE, + TRUST_DELETE +}; + +struct other_dom_data { + char *host; + char *user_name; + char *domain_sid_str; + char *dns_domain_name; + char *domain_name; +}; + +struct dom_data { + struct dom_sid *domsid; + char *dns_domain_name; + char *domain_name; +}; + +static NTSTATUS close_handle(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *bind_hnd, + struct policy_handle *pol_hnd) +{ + NTSTATUS status; + NTSTATUS result; + + status = dcerpc_lsa_Close(bind_hnd, mem_ctx, pol_hnd, &result); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_lsa_Close failed with error [%s].\n", + nt_errstr(status))); + return status; + } + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("lsa close failed with error [%s].\n", + nt_errstr(result))); + return result; + } + + return NT_STATUS_OK; +} + +static NTSTATUS delete_trust(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *bind_hnd, + struct policy_handle *pol_hnd, + struct dom_sid *domsid) +{ + NTSTATUS status; + struct lsa_DeleteTrustedDomain dr; + + dr.in.handle = pol_hnd; + dr.in.dom_sid = domsid; + + status = dcerpc_lsa_DeleteTrustedDomain_r(bind_hnd, mem_ctx, &dr); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_lsa_DeleteTrustedDomain_r failed with [%s]\n", + nt_errstr(status))); + return status; + } + if (!NT_STATUS_IS_OK(dr.out.result)) { + DEBUG(0, ("DeleteTrustedDomain returned [%s]\n", + nt_errstr(dr.out.result))); + return dr.out.result; + } + + return NT_STATUS_OK; +} + +static NTSTATUS create_trust(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *bind_hnd, + struct policy_handle *pol_hnd, + const char *trust_name, + const char *trust_name_dns, + struct dom_sid *domsid, + struct lsa_TrustDomainInfoAuthInfoInternal *authinfo) +{ + NTSTATUS status; + struct lsa_CreateTrustedDomainEx2 r; + struct lsa_TrustDomainInfoInfoEx trustinfo; + struct policy_handle trustdom_handle; + bool is_nt4 = trust_name_dns == NULL; + + if (!is_nt4) { + fprintf(stdout, "Creating AD trust\n"); + trustinfo.trust_type = LSA_TRUST_TYPE_UPLEVEL; + trustinfo.trust_attributes = LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE; + } else { + fprintf(stdout, "Creating NT4 trust\n"); + trustinfo.trust_type = LSA_TRUST_TYPE_DOWNLEVEL; + trustinfo.trust_attributes = 0; + trust_name_dns = trust_name; + } + + trustinfo.sid = domsid; + trustinfo.netbios_name.string = trust_name; + trustinfo.domain_name.string = trust_name_dns; + + trustinfo.trust_direction = LSA_TRUST_DIRECTION_INBOUND | + LSA_TRUST_DIRECTION_OUTBOUND; + + r.in.policy_handle = pol_hnd; + r.in.info = &trustinfo; + r.in.auth_info_internal = authinfo; + r.in.access_mask = LSA_TRUSTED_SET_POSIX | LSA_TRUSTED_SET_AUTH | + LSA_TRUSTED_QUERY_DOMAIN_NAME; + r.out.trustdom_handle = &trustdom_handle; + + status = dcerpc_lsa_CreateTrustedDomainEx2_r(bind_hnd, mem_ctx, &r); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_lsa_CreateTrustedDomainEx2_r failed " + "with error [%s].\n", nt_errstr(status))); + return status; + } + if (!NT_STATUS_IS_OK(r.out.result)) { + DEBUG(0, ("CreateTrustedDomainEx2_r returned [%s].\n", + nt_errstr(r.out.result))); + return r.out.result; + } + + return NT_STATUS_OK; +} + +static NTSTATUS get_domain_info(TALLOC_CTX *mem_ctx, + struct dcerpc_binding_handle *bind_hdn, + struct policy_handle *pol_hnd, + struct dom_data *dom_data) +{ + NTSTATUS status; + struct lsa_QueryInfoPolicy2 qr; + struct dom_sid_buf buf; + + qr.in.handle = pol_hnd; + qr.in.level = LSA_POLICY_INFO_DNS; + + status = dcerpc_lsa_QueryInfoPolicy2_r(bind_hdn, mem_ctx, &qr); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("dcerpc_lsa_QueryInfoPolicy2_r failed " + "with error [%s].\n", nt_errstr(status))); + return status; + } + + if (!NT_STATUS_IS_OK(qr.out.result)) { + DEBUG(0, ("QueryInfoPolicy2 returned [%s].\n", + nt_errstr(qr.out.result))); + return qr.out.result; + } + + dom_data->domain_name = talloc_strdup(mem_ctx, + (*qr.out.info)->dns.name.string); + dom_data->dns_domain_name = talloc_strdup(mem_ctx, + (*qr.out.info)->dns.dns_domain.string); + dom_data->domsid = dom_sid_dup(mem_ctx, (*qr.out.info)->dns.sid); + if (dom_data->domain_name == NULL || + dom_data->dns_domain_name == NULL || + dom_data->domsid == NULL) { + DEBUG(0, ("Copying domain data failed.\n")); + return NT_STATUS_NO_MEMORY; + } + + DEBUG(0, ("Got the following domain info [%s][%s][%s].\n", + dom_data->domain_name, dom_data->dns_domain_name, + dom_sid_str_buf(dom_data->domsid, &buf))); + + return NT_STATUS_OK; +} + +static NTSTATUS connect_and_get_info(TALLOC_CTX *mem_ctx, + struct net_context *net_ctx, + struct cli_state **cli, + struct rpc_pipe_client **pipe_hnd, + struct policy_handle *pol_hnd, + struct dom_data *dom_data, + DATA_BLOB *session_key) +{ + NTSTATUS status; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32_t out_version = 0; + union lsa_revision_info out_revision_info = { + .info1 = { + .revision = 0, + }, + }; + + status = net_make_ipc_connection_ex(net_ctx, NULL, NULL, NULL, + NET_FLAGS_PDC, cli); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to connect to [%s] with error [%s]\n", + net_ctx->opt_host, nt_errstr(status))); + return status; + } + + status = cli_rpc_pipe_open_noauth(*cli, &ndr_table_lsarpc, pipe_hnd); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to initialise lsa pipe with error [%s]\n", + nt_errstr(status))); + return status; + } + + status = dcerpc_lsa_open_policy_fallback( + (*pipe_hnd)->binding_handle, + mem_ctx, + (*pipe_hnd)->srv_name_slash, + false, + LSA_POLICY_VIEW_LOCAL_INFORMATION | + LSA_POLICY_TRUST_ADMIN | + LSA_POLICY_CREATE_SECRET, + &out_version, + &out_revision_info, + pol_hnd, + &result); + if (any_nt_status_not_ok(status, result, &status)) { + DBG_ERR("Failed to open policy handle: %s\n", + nt_errstr(result)); + return status; + } + + status = get_domain_info(mem_ctx, (*pipe_hnd)->binding_handle, + pol_hnd, dom_data); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("get_domain_info failed with error [%s].\n", + nt_errstr(status))); + return status; + } + + status = cli_get_session_key(mem_ctx, *pipe_hnd, session_key); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Error getting session_key of LSA pipe. Error was %s\n", + nt_errstr(status))); + return status; + } + + return NT_STATUS_OK; +} + +static bool get_trust_domain_passwords_auth_blob(TALLOC_CTX *mem_ctx, + const char *password, + DATA_BLOB *auth_blob) +{ + struct trustDomainPasswords auth_struct; + struct AuthenticationInformation *auth_info_array; + enum ndr_err_code ndr_err; + size_t converted_size; + + generate_random_buffer(auth_struct.confounder, + sizeof(auth_struct.confounder)); + + auth_info_array = talloc_array(mem_ctx, + struct AuthenticationInformation, 1); + if (auth_info_array == NULL) { + return false; + } + + auth_info_array[0].AuthType = TRUST_AUTH_TYPE_CLEAR; + if (!convert_string_talloc(mem_ctx, CH_UNIX, CH_UTF16, password, + strlen(password), + &auth_info_array[0].AuthInfo.clear.password, + &converted_size)) { + return false; + } + + auth_info_array[0].AuthInfo.clear.size = converted_size; + + auth_struct.outgoing.count = 1; + auth_struct.outgoing.current.count = 1; + auth_struct.outgoing.current.array = auth_info_array; + auth_struct.outgoing.previous.count = 0; + auth_struct.outgoing.previous.array = NULL; + + auth_struct.incoming.count = 1; + auth_struct.incoming.current.count = 1; + auth_struct.incoming.current.array = auth_info_array; + auth_struct.incoming.previous.count = 0; + auth_struct.incoming.previous.array = NULL; + + ndr_err = ndr_push_struct_blob(auth_blob, mem_ctx, &auth_struct, + (ndr_push_flags_fn_t)ndr_push_trustDomainPasswords); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + return false; + } + + return true; +} + +static int parse_trust_args(TALLOC_CTX *mem_ctx, int argc, const char **argv, struct other_dom_data **_o, char **_trustpw) +{ + size_t c; + struct other_dom_data *o = NULL; + char *trustpw = NULL; + int ret = EFAULT; + + if (argc == 0) { + return EINVAL; + } + + o = talloc_zero(mem_ctx, struct other_dom_data); + if (o == NULL) { + DEBUG(0, ("talloc_zero failed.\n")); + return ENOMEM; + } + + for (c = 0; c < argc; c++) { + if (strnequal(argv[c], ARG_OTHERSERVER, sizeof(ARG_OTHERSERVER)-1)) { + o->host = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERSERVER)-1); + if (o->host == NULL) { + ret = ENOMEM; + goto failed; + } + } else if (strnequal(argv[c], ARG_OTHERUSER, sizeof(ARG_OTHERUSER)-1)) { + o->user_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERUSER)-1); + if (o->user_name == NULL) { + ret = ENOMEM; + goto failed; + } + } else if (strnequal(argv[c], ARG_OTHERDOMAINSID, sizeof(ARG_OTHERDOMAINSID)-1)) { + o->domain_sid_str = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAINSID)-1); + if (o->domain_sid_str == NULL) { + ret = ENOMEM; + goto failed; + } + } else if (strnequal(argv[c], ARG_OTHERDOMAIN, sizeof(ARG_OTHERDOMAIN)-1)) { + o->dns_domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERDOMAIN)-1); + if (o->dns_domain_name == NULL) { + ret = ENOMEM; + goto failed; + } + } else if (strnequal(argv[c], ARG_OTHERNETBIOSDOMAIN, sizeof(ARG_OTHERNETBIOSDOMAIN)-1)) { + o->domain_name = talloc_strdup(o, argv[c] + sizeof(ARG_OTHERNETBIOSDOMAIN)-1); + if (o->domain_name == NULL) { + ret = ENOMEM; + goto failed; + } + } else if (strnequal(argv[c], ARG_TRUSTPW, sizeof(ARG_TRUSTPW)-1)) { + trustpw = talloc_strdup(mem_ctx, argv[c] + sizeof(ARG_TRUSTPW)-1); + if (trustpw == NULL) { + ret = ENOMEM; + goto failed; + } + } else { + DEBUG(0, ("Unsupported option [%s].\n", argv[c])); + ret = EINVAL; + goto failed; + } + } + + *_o = o; + *_trustpw = trustpw; + + return 0; + +failed: + talloc_free(o); + talloc_free(trustpw); + return ret; +} + +static void print_trust_delete_usage(void) +{ + d_printf( "%s\n" + "net rpc trust delete [options]\n" + "\nOptions:\n" + "\totherserver=DC in other domain\n" + "\totheruser=Admin user in other domain\n" + "\totherdomainsid=SID of other domain\n" + "\nExamples:\n" + "\tnet rpc trust delete otherserver=oname otheruser=ouser -S lname -U luser\n" + "\tnet rpc trust delete otherdomainsid=S-... -S lname -U luser\n" + " %s\n", + _("Usage:"), + _("Remove trust between two domains")); +} + +static void print_trust_usage(void) +{ + d_printf( "%s\n" + "net rpc trust create [options]\n" + "\nOptions:\n" + "\totherserver=DC in other domain\n" + "\totheruser=Admin user in other domain\n" + "\totherdomainsid=SID of other domain\n" + "\tother_netbios_domain=NetBIOS/short name of other domain\n" + "\totherdomain=Full/DNS name of other domain (if not used, create an NT4 trust)\n" + "\ttrustpw=Trust password\n" + "\nExamples:\n" + "\tnet rpc trust create otherserver=oname otheruser=ouser -S lname -U luser\n" + "\tnet rpc trust create otherdomainsid=S-... other_netbios_domain=odom otherdomain=odom.org trustpw=secret -S lname -U luser\n" + " %s\n", + _("Usage:"), + _("Create trust between two domains")); +} + +static int rpc_trust_common(struct net_context *net_ctx, int argc, + const char **argv, enum trust_op op) +{ + TALLOC_CTX *mem_ctx; + NTSTATUS status; + int ret; + int success = -1; + struct cli_state *cli[2] = {NULL, NULL}; + struct rpc_pipe_client *pipe_hnd[2] = {NULL, NULL}; + DATA_BLOB session_key[2]; + struct policy_handle pol_hnd[2]; + struct lsa_TrustDomainInfoAuthInfoInternal authinfo; + DATA_BLOB auth_blob; + char *trust_pw = NULL; + struct other_dom_data *other_dom_data; + struct net_context *other_net_ctx = NULL; + struct dom_data dom_data[2]; + void (*usage)(void); + + ZERO_STRUCT(session_key); + + switch (op) { + case TRUST_CREATE: + usage = print_trust_usage; + break; + case TRUST_DELETE: + usage = print_trust_delete_usage; + break; + default: + DEBUG(0, ("Unsupported trust operation.\n")); + return -1; + } + + if (net_ctx->display_usage) { + usage(); + return 0; + } + + mem_ctx = talloc_init("trust op"); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_init failed.\n")); + return -1; + } + + ret = parse_trust_args(mem_ctx, argc, argv, &other_dom_data, &trust_pw); + if (ret != 0) { + if (ret == EINVAL) { + usage(); + } else { + DEBUG(0, ("Failed to parse arguments.\n")); + } + goto done; + } + + if (other_dom_data->host != 0) { + other_net_ctx = talloc_zero(other_dom_data, struct net_context); + if (other_net_ctx == NULL) { + DEBUG(0, ("talloc_zero failed.\n")); + goto done; + } + + other_net_ctx->opt_host = other_dom_data->host; + other_net_ctx->creds = cli_credentials_init(other_net_ctx); + cli_credentials_parse_string(other_net_ctx->creds, + other_dom_data->user_name, + CRED_SPECIFIED); + } else { + dom_data[1].domsid = dom_sid_parse_talloc(mem_ctx, + other_dom_data->domain_sid_str); + dom_data[1].domain_name = other_dom_data->domain_name; + dom_data[1].dns_domain_name = other_dom_data->dns_domain_name; + + if (dom_data[1].dns_domain_name == NULL) { + fprintf(stdout, "No DNS domain name passed, " + "assuming NT4 trust!\n"); + } + + if (dom_data[1].domsid == NULL || + (op == TRUST_CREATE && + (dom_data[1].domain_name == NULL))) { + DEBUG(0, ("Missing required argument.\n")); + usage(); + goto done; + } + } + + status = connect_and_get_info(mem_ctx, net_ctx, &cli[0], &pipe_hnd[0], + &pol_hnd[0], &dom_data[0], &session_key[0]); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("connect_and_get_info failed with error [%s]\n", + nt_errstr(status))); + goto done; + } + + if (other_net_ctx != NULL) { + status = connect_and_get_info(mem_ctx, other_net_ctx, + &cli[1], &pipe_hnd[1], + &pol_hnd[1], &dom_data[1], + &session_key[1]); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("connect_and_get_info failed with error [%s]\n", + nt_errstr(status))); + goto done; + } + } + + if (op == TRUST_CREATE) { + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t enc_session_key = { + .data = session_key[0].data, + .size = session_key[0].length, + }; + int rc; + + if (trust_pw == NULL) { + if (other_net_ctx == NULL) { + DEBUG(0, ("Missing either trustpw or otherhost.\n")); + goto done; + } + + DEBUG(0, ("Using random trust password.\n")); + trust_pw = trust_pw_new_value(mem_ctx, + SEC_CHAN_DOMAIN, + SEC_DOMAIN); + if (trust_pw == NULL) { + DEBUG(0, ("generate_random_password failed.\n")); + goto done; + } + } else { + DEBUG(0, ("Using user provided password.\n")); + } + + if (!get_trust_domain_passwords_auth_blob(mem_ctx, trust_pw, + &auth_blob)) { + DEBUG(0, ("get_trust_domain_passwords_auth_blob failed\n")); + goto done; + } + + authinfo.auth_blob.data = (uint8_t *)talloc_memdup( + mem_ctx, + auth_blob.data, + auth_blob.length); + if (authinfo.auth_blob.data == NULL) { + goto done; + } + authinfo.auth_blob.size = auth_blob.length; + + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &enc_session_key, + NULL); + if (rc < 0) { + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto done; + } + rc = gnutls_cipher_encrypt(cipher_hnd, + authinfo.auth_blob.data, + authinfo.auth_blob.size); + gnutls_cipher_deinit(cipher_hnd); + if (rc < 0) { + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto done; + } + + status = create_trust(mem_ctx, pipe_hnd[0]->binding_handle, + &pol_hnd[0], + dom_data[1].domain_name, + dom_data[1].dns_domain_name, + dom_data[1].domsid, + &authinfo); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("create_trust failed with error [%s].\n", + nt_errstr(status))); + goto done; + } + + if (other_net_ctx != NULL) { + talloc_free(authinfo.auth_blob.data); + authinfo.auth_blob.data = (uint8_t *)talloc_memdup( + mem_ctx, + auth_blob.data, + auth_blob.length); + if (authinfo.auth_blob.data == NULL) { + goto done; + } + authinfo.auth_blob.size = auth_blob.length; + + enc_session_key = (gnutls_datum_t) { + .data = session_key[1].data, + .size = session_key[1].length, + }; + + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &enc_session_key, + NULL); + if (rc < 0) { + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto done; + } + rc = gnutls_cipher_encrypt(cipher_hnd, + authinfo.auth_blob.data, + authinfo.auth_blob.size); + gnutls_cipher_deinit(cipher_hnd); + if (rc < 0) { + status = gnutls_error_to_ntstatus(rc, NT_STATUS_CRYPTO_SYSTEM_INVALID); + goto done; + } + + status = create_trust(mem_ctx, + pipe_hnd[1]->binding_handle, + &pol_hnd[1], + dom_data[0].domain_name, + dom_data[0].dns_domain_name, + dom_data[0].domsid, &authinfo); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("create_trust failed with error [%s].\n", + nt_errstr(status))); + goto done; + } + } + } else if (op == TRUST_DELETE) { + status = delete_trust(mem_ctx, pipe_hnd[0]->binding_handle, + &pol_hnd[0], dom_data[1].domsid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("delete_trust failed with [%s].\n", + nt_errstr(status))); + goto done; + } + + if (other_net_ctx != NULL) { + status = delete_trust(mem_ctx, + pipe_hnd[1]->binding_handle, + &pol_hnd[1], dom_data[0].domsid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("delete_trust failed with [%s].\n", + nt_errstr(status))); + goto done; + } + } + } + + status = close_handle(mem_ctx, pipe_hnd[0]->binding_handle, + &pol_hnd[0]); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("close_handle failed with error [%s].\n", + nt_errstr(status))); + goto done; + } + + if (other_net_ctx != NULL) { + status = close_handle(mem_ctx, pipe_hnd[1]->binding_handle, + &pol_hnd[1]); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("close_handle failed with error [%s].\n", + nt_errstr(status))); + goto done; + } + } + + success = 0; + +done: + data_blob_clear_free(&session_key[0]); + data_blob_clear_free(&session_key[1]); + cli_shutdown(cli[0]); + cli_shutdown(cli[1]); + talloc_destroy(mem_ctx); + return success; +} + +static int rpc_trust_create(struct net_context *net_ctx, int argc, + const char **argv) +{ + return rpc_trust_common(net_ctx, argc, argv, TRUST_CREATE); +} + +static int rpc_trust_delete(struct net_context *net_ctx, int argc, + const char **argv) +{ + return rpc_trust_common(net_ctx, argc, argv, TRUST_DELETE); +} + +int net_rpc_trust(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "create", + rpc_trust_create, + NET_TRANSPORT_RPC, + N_("Create trusts"), + N_("net rpc trust create\n" + " Create trusts") + }, + { + "delete", + rpc_trust_delete, + NET_TRANSPORT_RPC, + N_("Remove trusts"), + N_("net rpc trust delete\n" + " Remove trusts") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net rpc trust", func); +} diff --git a/source3/utils/net_sam.c b/source3/utils/net_sam.c new file mode 100644 index 0000000..5c1e007 --- /dev/null +++ b/source3/utils/net_sam.c @@ -0,0 +1,2308 @@ +/* + * Unix SMB/CIFS implementation. + * Local SAM access routines + * Copyright (C) Volker Lendecke 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "includes.h" +#include "system/passwd.h" +#include "utils/net.h" +#include "../librpc/gen_ndr/samr.h" +#include "smbldap.h" +#include "../libcli/security/security.h" +#include "lib/winbind_util.h" +#include "passdb.h" +#include "passdb/pdb_ldap_util.h" +#include "passdb/pdb_ldap_schema.h" +#include "lib/privileges.h" +#include "secrets.h" +#include "idmap.h" +#include "lib/util/smb_strtox.h" +#include "lib/util/string_wrappers.h" +#include "source3/lib/substitute.h" + +/* + * Set a user's data + */ + +static int net_sam_userset(struct net_context *c, int argc, const char **argv, + const char *field, + bool (*fn)(struct samu *, const char *, + enum pdb_value_state)) +{ + struct samu *sam_acct = NULL; + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n", _("Usage:")); + d_fprintf(stderr, _("net sam set %s <user> <value>\n"), + field); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + if (type != SID_NAME_USER) { + d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0], + sid_type_lookup(type)); + return -1; + } + + if ( !(sam_acct = samu_new( NULL )) ) { + d_fprintf(stderr, _("Internal error\n")); + return -1; + } + + if (!pdb_getsampwsid(sam_acct, &sid)) { + d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]); + return -1; + } + + if (!fn(sam_acct, argv[1], PDB_CHANGED)) { + d_fprintf(stderr, _("Internal error\n")); + return -1; + } + + status = pdb_update_sam_account(sam_acct); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Updating sam account %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + TALLOC_FREE(sam_acct); + + d_printf(_("Updated %s for %s\\%s to %s\n"), field, dom, name, argv[1]); + return 0; +} + +static int net_sam_set_fullname(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "fullname", + pdb_set_fullname); +} + +static int net_sam_set_logonscript(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "logonscript", + pdb_set_logon_script); +} + +static int net_sam_set_profilepath(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "profilepath", + pdb_set_profile_path); +} + +static int net_sam_set_homedrive(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "homedrive", + pdb_set_dir_drive); +} + +static int net_sam_set_homedir(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "homedir", + pdb_set_homedir); +} + +static int net_sam_set_workstations(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_userset(c, argc, argv, "workstations", + pdb_set_workstations); +} + +/* + * Set account flags + */ + +static int net_sam_set_userflag(struct net_context *c, int argc, + const char **argv, const char *field, + uint16_t flag) +{ + struct samu *sam_acct = NULL; + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + uint32_t acct_flags; + + if ((argc != 2) || c->display_usage || + (!strequal(argv[1], "yes") && + !strequal(argv[1], "no"))) { + d_fprintf(stderr, "%s\n", _("Usage:")); + d_fprintf(stderr, _("net sam set %s <user> [yes|no]\n"), + field); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + if (type != SID_NAME_USER) { + d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0], + sid_type_lookup(type)); + return -1; + } + + if ( !(sam_acct = samu_new( NULL )) ) { + d_fprintf(stderr, _("Internal error\n")); + return -1; + } + + if (!pdb_getsampwsid(sam_acct, &sid)) { + d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]); + return -1; + } + + acct_flags = pdb_get_acct_ctrl(sam_acct); + + if (strequal(argv[1], "yes")) { + acct_flags |= flag; + } else { + acct_flags &= ~flag; + } + + pdb_set_acct_ctrl(sam_acct, acct_flags, PDB_CHANGED); + + status = pdb_update_sam_account(sam_acct); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Updating sam account %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + TALLOC_FREE(sam_acct); + + d_fprintf(stderr, _("Updated flag %s for %s\\%s to %s\n"), field, dom, + name, argv[1]); + return 0; +} + +static int net_sam_set_disabled(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_set_userflag(c, argc, argv, "disabled", ACB_DISABLED); +} + +static int net_sam_set_pwnotreq(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_set_userflag(c, argc, argv, "pwnotreq", ACB_PWNOTREQ); +} + +static int net_sam_set_autolock(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_set_userflag(c, argc, argv, "autolock", ACB_AUTOLOCK); +} + +static int net_sam_set_pwnoexp(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_set_userflag(c, argc, argv, "pwnoexp", ACB_PWNOEXP); +} + +/* + * Set pass last change time, based on force pass change now + */ + +static int net_sam_set_pwdmustchangenow(struct net_context *c, int argc, + const char **argv) +{ + struct samu *sam_acct = NULL; + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + + if ((argc != 2) || c->display_usage || + (!strequal(argv[1], "yes") && + !strequal(argv[1], "no"))) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam set pwdmustchangenow <user> [yes|no]\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + if (type != SID_NAME_USER) { + d_fprintf(stderr, _("%s is a %s, not a user\n"), argv[0], + sid_type_lookup(type)); + return -1; + } + + if ( !(sam_acct = samu_new( NULL )) ) { + d_fprintf(stderr, _("Internal error\n")); + return -1; + } + + if (!pdb_getsampwsid(sam_acct, &sid)) { + d_fprintf(stderr, _("Loading user %s failed\n"), argv[0]); + return -1; + } + + if (strequal(argv[1], "yes")) { + pdb_set_pass_last_set_time(sam_acct, 0, PDB_CHANGED); + } else { + pdb_set_pass_last_set_time(sam_acct, time(NULL), PDB_CHANGED); + } + + status = pdb_update_sam_account(sam_acct); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Updating sam account %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + TALLOC_FREE(sam_acct); + + d_fprintf(stderr, _("Updated 'user must change password at next logon' " + "for %s\\%s to %s\n"), dom, + name, argv[1]); + return 0; +} + + +/* + * Set a user's or a group's comment + */ + +static int net_sam_set_comment(struct net_context *c, int argc, + const char **argv) +{ + GROUP_MAP *map; + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam set comment <name> <comment>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + if (type == SID_NAME_USER) { + return net_sam_userset(c, argc, argv, "comment", + pdb_set_acct_desc); + } + + if ((type != SID_NAME_DOM_GRP) && (type != SID_NAME_ALIAS) && + (type != SID_NAME_WKN_GRP)) { + d_fprintf(stderr, _("%s is a %s, not a group\n"), argv[0], + sid_type_lookup(type)); + return -1; + } + + map = talloc_zero(talloc_tos(), GROUP_MAP); + if (!map) { + d_fprintf(stderr, _("Out of memory!\n")); + return -1; + } + + if (!pdb_getgrsid(map, sid)) { + d_fprintf(stderr, _("Could not load group %s\n"), argv[0]); + return -1; + } + + map->comment = talloc_strdup(map, argv[1]); + if (!map->comment) { + d_fprintf(stderr, _("Out of memory!\n")); + return -1; + } + + status = pdb_update_group_mapping_entry(map); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Updating group mapping entry failed with " + "%s\n"), nt_errstr(status)); + return -1; + } + + d_printf("Updated comment of group %s\\%s to %s\n", dom, name, + argv[1]); + + TALLOC_FREE(map); + return 0; +} + +static int net_sam_set(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "homedir", + net_sam_set_homedir, + NET_TRANSPORT_LOCAL, + N_("Change a user's home directory"), + N_("net sam set homedir\n" + " Change a user's home directory") + }, + { + "profilepath", + net_sam_set_profilepath, + NET_TRANSPORT_LOCAL, + N_("Change a user's profile path"), + N_("net sam set profilepath\n" + " Change a user's profile path") + }, + { + "comment", + net_sam_set_comment, + NET_TRANSPORT_LOCAL, + N_("Change a users or groups description"), + N_("net sam set comment\n" + " Change a users or groups description") + }, + { + "fullname", + net_sam_set_fullname, + NET_TRANSPORT_LOCAL, + N_("Change a user's full name"), + N_("net sam set fullname\n" + " Change a user's full name") + }, + { + "logonscript", + net_sam_set_logonscript, + NET_TRANSPORT_LOCAL, + N_("Change a user's logon script"), + N_("net sam set logonscript\n" + " Change a user's logon script") + }, + { + "homedrive", + net_sam_set_homedrive, + NET_TRANSPORT_LOCAL, + N_("Change a user's home drive"), + N_("net sam set homedrive\n" + " Change a user's home drive") + }, + { + "workstations", + net_sam_set_workstations, + NET_TRANSPORT_LOCAL, + N_("Change a user's allowed workstations"), + N_("net sam set workstations\n" + " Change a user's allowed workstations") + }, + { + "disabled", + net_sam_set_disabled, + NET_TRANSPORT_LOCAL, + N_("Disable/Enable a user"), + N_("net sam set disable\n" + " Disable/Enable a user") + }, + { + "pwnotreq", + net_sam_set_pwnotreq, + NET_TRANSPORT_LOCAL, + N_("Disable/Enable the password not required flag"), + N_("net sam set pwnotreq\n" + " Disable/Enable the password not required flag") + }, + { + "autolock", + net_sam_set_autolock, + NET_TRANSPORT_LOCAL, + N_("Disable/Enable a user's lockout flag"), + N_("net sam set autolock\n" + " Disable/Enable a user's lockout flag") + }, + { + "pwnoexp", + net_sam_set_pwnoexp, + NET_TRANSPORT_LOCAL, + N_("Disable/Enable whether a user's pw does not " + "expire"), + N_("net sam set pwnoexp\n" + " Disable/Enable whether a user's pw does not " + "expire") + }, + { + "pwdmustchangenow", + net_sam_set_pwdmustchangenow, + NET_TRANSPORT_LOCAL, + N_("Force users password must change at next logon"), + N_("net sam set pwdmustchangenow\n" + " Force users password must change at next logon") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net sam set", func); +} + +/* + * Manage account policies + */ + +static int net_sam_policy_set(struct net_context *c, int argc, const char **argv) +{ + const char *account_policy = NULL; + uint32_t value = 0; + uint32_t old_value = 0; + enum pdb_policy_type field; + int err = 0; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam policy set \"<account policy>\" <value>\n")); + return -1; + } + + account_policy = argv[0]; + field = account_policy_name_to_typenum(account_policy); + + if (strequal(argv[1], "forever") || strequal(argv[1], "never") + || strequal(argv[1], "off")) { + value = -1; + } + else { + value = smb_strtoul(argv[1], + NULL, + 10, + &err, + SMB_STR_FULL_STR_CONV); + + if (err != 0) { + d_printf(_("Unable to set policy \"%s\"! Invalid value " + "\"%s\".\n"), + account_policy, argv[1]); + return -1; + } + } + + if (field == 0) { + const char **names; + int i, count; + + account_policy_names_list(talloc_tos(), &names, &count); + d_fprintf(stderr, _("No account policy \"%s\"!\n\n"), argv[0]); + d_fprintf(stderr, _("Valid account policies are:\n")); + + for (i=0; i<count; i++) { + d_fprintf(stderr, "%s\n", names[i]); + } + + TALLOC_FREE(names); + + return -1; + } + + if (!pdb_get_account_policy(field, &old_value)) { + d_fprintf(stderr, _("Valid account policy, but unable to fetch " + "value!\n")); + } else { + d_printf(_("Account policy \"%s\" value was: %d\n"), + account_policy, old_value); + } + + if (!pdb_set_account_policy(field, value)) { + d_fprintf(stderr, _("Valid account policy, but unable to " + "set value!\n")); + return -1; + } else { + d_printf(_("Account policy \"%s\" value is now: %d\n"), + account_policy, value); + } + + return 0; +} + +static int net_sam_policy_show(struct net_context *c, int argc, const char **argv) +{ + const char *account_policy = NULL; + uint32_t old_value; + enum pdb_policy_type field; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam policy show \"<account policy>\"\n")); + return -1; + } + + account_policy = argv[0]; + field = account_policy_name_to_typenum(account_policy); + + if (field == 0) { + const char **names; + int count; + int i; + account_policy_names_list(talloc_tos(), &names, &count); + d_fprintf(stderr, _("No account policy by that name!\n")); + if (count != 0) { + d_fprintf(stderr, _("Valid account policies " + "are:\n")); + for (i=0; i<count; i++) { + d_fprintf(stderr, "%s\n", names[i]); + } + } + TALLOC_FREE(names); + return -1; + } + + if (!pdb_get_account_policy(field, &old_value)) { + fprintf(stderr, _("Valid account policy, but unable to " + "fetch value!\n")); + return -1; + } + + printf(_("Account policy \"%s\" description: %s\n"), + account_policy, account_policy_get_desc(field)); + printf(_("Account policy \"%s\" value is: %d\n"), account_policy, + old_value); + return 0; +} + +static int net_sam_policy_list(struct net_context *c, int argc, const char **argv) +{ + const char **names; + int count; + int i; + + if (c->display_usage) { + d_printf( "%s\n" + "net sam policy list\n" + " %s\n", + _("Usage:"), + _("List account policies")); + return 0; + } + + account_policy_names_list(talloc_tos(), &names, &count); + if (count != 0) { + d_fprintf(stderr, _("Valid account policies " + "are:\n")); + for (i = 0; i < count ; i++) { + d_fprintf(stderr, "%s\n", names[i]); + } + } + TALLOC_FREE(names); + return -1; +} + +static int net_sam_policy(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_sam_policy_list, + NET_TRANSPORT_LOCAL, + N_("List account policies"), + N_("net sam policy list\n" + " List account policies") + }, + { + "show", + net_sam_policy_show, + NET_TRANSPORT_LOCAL, + N_("Show account policies"), + N_("net sam policy show\n" + " Show account policies") + }, + { + "set", + net_sam_policy_set, + NET_TRANSPORT_LOCAL, + N_("Change account policies"), + N_("net sam policy set\n" + " Change account policies") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net sam policy", func); +} + +static int net_sam_rights_list(struct net_context *c, int argc, + const char **argv) +{ + enum sec_privilege privilege; + + if (argc > 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam rights list [privilege name]\n")); + return -1; + } + + if (argc == 0) { + int i; + int num = num_privileges_in_short_list(); + + for (i=0; i<num; i++) { + d_printf("%s\n", sec_privilege_name_from_index(i)); + } + return 0; + } + + privilege = sec_privilege_id(argv[0]); + + if (privilege != SEC_PRIV_INVALID) { + struct dom_sid *sids; + int i, num_sids; + NTSTATUS status; + + status = privilege_enum_sids(privilege, talloc_tos(), + &sids, &num_sids); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not list rights: %s\n"), + nt_errstr(status)); + return -1; + } + + for (i=0; i<num_sids; i++) { + const char *dom, *name; + enum lsa_SidType type; + struct dom_sid_buf buf; + + if (lookup_sid(talloc_tos(), &sids[i], &dom, &name, + &type)) { + d_printf("%s\\%s\n", dom, name); + } + else { + d_printf("%s\n", + dom_sid_str_buf(&sids[i], &buf)); + } + } + return 0; + } + + return -1; +} + +static int net_sam_rights_grant(struct net_context *c, int argc, + const char **argv) +{ + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + int i; + + if (argc < 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam rights grant <name> <rights> ...\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + for (i=1; i < argc; i++) { + enum sec_privilege privilege = sec_privilege_id(argv[i]); + if (privilege == SEC_PRIV_INVALID) { + d_fprintf(stderr, _("%s unknown\n"), argv[i]); + return -1; + } + + if (!grant_privilege_by_name(&sid, argv[i])) { + d_fprintf(stderr, _("Could not grant privilege\n")); + return -1; + } + + d_printf(_("Granted %s to %s\\%s\n"), argv[i], dom, name); + } + + return 0; +} + +static int net_sam_rights_revoke(struct net_context *c, int argc, + const char **argv) +{ + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + int i; + + if (argc < 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam rights revoke <name> <rights>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + for (i=1; i < argc; i++) { + enum sec_privilege privilege = sec_privilege_id(argv[i]); + if (privilege == SEC_PRIV_INVALID) { + d_fprintf(stderr, _("%s unknown\n"), argv[i]); + return -1; + } + + if (!revoke_privilege_by_name(&sid, argv[i])) { + d_fprintf(stderr, _("Could not revoke privilege\n")); + return -1; + } + + d_printf(_("Revoked %s from %s\\%s\n"), argv[i], dom, name); + } + + return 0; +} + +static int net_sam_rights(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_sam_rights_list, + NET_TRANSPORT_LOCAL, + N_("List possible user rights"), + N_("net sam rights list\n" + " List possible user rights") + }, + { + "grant", + net_sam_rights_grant, + NET_TRANSPORT_LOCAL, + N_("Grant right(s)"), + N_("net sam rights grant\n" + " Grant right(s)") + }, + { + "revoke", + net_sam_rights_revoke, + NET_TRANSPORT_LOCAL, + N_("Revoke right(s)"), + N_("net sam rights revoke\n" + " Revoke right(s)") + }, + {NULL, NULL, 0, NULL, NULL} + }; + return net_run_function(c, argc, argv, "net sam rights", func); +} + +/* + * Map a unix group to a domain group + */ + +static NTSTATUS map_unix_group(const struct group *grp, GROUP_MAP *map) +{ + const char *dom, *name; + uint32_t rid; + + if (pdb_getgrgid(map, grp->gr_gid)) { + return NT_STATUS_GROUP_EXISTS; + } + + map->gid = grp->gr_gid; + + if (lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL, + &dom, &name, NULL, NULL)) { + + map->nt_name = talloc_asprintf(map, "Unix Group %s", + grp->gr_name); + + DEBUG(5, ("%s exists as %s\\%s, retrying as \"%s\"\n", + grp->gr_name, dom, name, map->nt_name)); + } + + if (lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL, + NULL, NULL, NULL, NULL)) { + DEBUG(3, ("\"%s\" exists, can't map it\n", grp->gr_name)); + return NT_STATUS_GROUP_EXISTS; + } + + if (pdb_capabilities() & PDB_CAP_STORE_RIDS) { + if (!pdb_new_rid(&rid)) { + DEBUG(3, ("Could not get a new RID for %s\n", + grp->gr_name)); + return NT_STATUS_ACCESS_DENIED; + } + } else { + rid = algorithmic_pdb_gid_to_group_rid( grp->gr_gid ); + } + + sid_compose(&map->sid, get_global_sam_sid(), rid); + map->sid_name_use = SID_NAME_DOM_GRP; + map->comment = talloc_asprintf(map, "Unix Group %s", grp->gr_name); + + return pdb_add_group_mapping_entry(map); +} + +static int net_sam_mapunixgroup(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + GROUP_MAP *map; + struct group *grp; + struct dom_sid_buf buf; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam mapunixgroup <name>\n")); + return -1; + } + + grp = getgrnam(argv[0]); + if (grp == NULL) { + d_fprintf(stderr, _("Could not find group %s\n"), argv[0]); + return -1; + } + + map = talloc_zero(talloc_tos(), GROUP_MAP); + if (!map) { + d_fprintf(stderr, _("Out of memory!\n")); + return -1; + } + + status = map_unix_group(grp, map); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Mapping group %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Mapped unix group %s to SID %s\n"), argv[0], + dom_sid_str_buf(&map->sid, &buf)); + + TALLOC_FREE(map); + return 0; +} + +/* + * Remove a group mapping + */ + +static NTSTATUS unmap_unix_group(const struct group *grp) +{ + struct dom_sid dom_sid; + struct unixid id; + + if (!lookup_name(talloc_tos(), grp->gr_name, LOOKUP_NAME_LOCAL, + NULL, NULL, NULL, NULL)) { + DEBUG(3, ("\"%s\" does not exist, can't unmap it\n", grp->gr_name)); + return NT_STATUS_NO_SUCH_GROUP; + } + + id.id = grp->gr_gid; + id.type = ID_TYPE_GID; + if (!pdb_id_to_sid(&id, &dom_sid)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return pdb_delete_group_mapping_entry(dom_sid); +} + +static int net_sam_unmapunixgroup(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + struct group *grp; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam unmapunixgroup <name>\n")); + return -1; + } + + grp = getgrnam(argv[0]); + if (grp == NULL) { + d_fprintf(stderr, _("Could not find mapping for group %s.\n"), + argv[0]); + return -1; + } + + status = unmap_unix_group(grp); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Unmapping group %s failed with %s.\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Unmapped unix group %s.\n"), argv[0]); + + return 0; +} + +/* + * Create a domain group + */ + +static int net_sam_createdomaingroup(struct net_context *c, int argc, + const char **argv) +{ + NTSTATUS status; + uint32_t rid; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam createdomaingroup <name>\n")); + return -1; + } + + status = pdb_create_dom_group(talloc_tos(), argv[0], &rid); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Creating %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Created domain group %s with RID %d\n"), argv[0], rid); + + return 0; +} + +/* + * Delete a domain group + */ + +static int net_sam_deletedomaingroup(struct net_context *c, int argc, + const char **argv) +{ + struct dom_sid sid; + uint32_t rid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam deletelocalgroup <name>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find %s.\n"), argv[0]); + return -1; + } + + if (type != SID_NAME_DOM_GRP) { + d_fprintf(stderr, _("%s is a %s, not a domain group.\n"), + argv[0], sid_type_lookup(type)); + return -1; + } + + sid_peek_rid(&sid, &rid); + + status = pdb_delete_dom_group(talloc_tos(), rid); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr,_("Deleting domain group %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Deleted domain group %s.\n"), argv[0]); + + return 0; +} + +/* + * Create a local group + */ + +static int net_sam_createlocalgroup(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + uint32_t rid; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam createlocalgroup <name>\n")); + return -1; + } + + if (!winbind_ping()) { + d_fprintf(stderr, _("winbind seems not to run. " + "createlocalgroup only works when winbind runs.\n")); + return -1; + } + + status = pdb_create_alias(argv[0], &rid); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Creating %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Created local group %s with RID %d\n"), argv[0], rid); + + return 0; +} + +/* + * Delete a local group + */ + +static int net_sam_deletelocalgroup(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + enum lsa_SidType type; + const char *dom, *name; + NTSTATUS status; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam deletelocalgroup <name>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr,_("Could not find %s.\n"), argv[0]); + return -1; + } + + if (type != SID_NAME_ALIAS) { + d_fprintf(stderr, _("%s is a %s, not a local group.\n"),argv[0], + sid_type_lookup(type)); + return -1; + } + + status = pdb_delete_alias(&sid); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Deleting local group %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Deleted local group %s.\n"), argv[0]); + + return 0; +} + +/* + * Create a builtin group + */ + +static int net_sam_createbuiltingroup(struct net_context *c, int argc, const char **argv) +{ + NTSTATUS status; + uint32_t rid; + enum lsa_SidType type; + fstring groupname; + struct dom_sid sid; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam createbuiltingroup <name>\n")); + return -1; + } + + if (!winbind_ping()) { + d_fprintf(stderr, _("winbind seems not to run. " + "createbuiltingroup only works when winbind " + "runs.\n")); + return -1; + } + + /* validate the name and get the group */ + + fstrcpy( groupname, "BUILTIN\\" ); + fstrcat( groupname, argv[0] ); + + if ( !lookup_name(talloc_tos(), groupname, LOOKUP_NAME_ALL, NULL, + NULL, &sid, &type)) { + d_fprintf(stderr, _("%s is not a BUILTIN group\n"), argv[0]); + return -1; + } + + if ( !sid_peek_rid( &sid, &rid ) ) { + d_fprintf(stderr, _("Failed to get RID for %s\n"), argv[0]); + return -1; + } + + status = pdb_create_builtin(rid); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Creating %s failed with %s\n"), + argv[0], nt_errstr(status)); + return -1; + } + + d_printf(_("Created BUILTIN group %s with RID %d\n"), argv[0], rid); + + return 0; +} + +/* + * Add a group member + */ + +static int net_sam_addmem(struct net_context *c, int argc, const char **argv) +{ + const char *groupdomain, *groupname, *memberdomain, *membername; + struct dom_sid group, member; + enum lsa_SidType grouptype, membertype; + NTSTATUS status; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam addmem <group> <member>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &groupdomain, &groupname, &group, &grouptype)) { + d_fprintf(stderr, _("Could not find group %s\n"), argv[0]); + return -1; + } + + /* check to see if the member to be added is a name or a SID */ + + if (!lookup_name(talloc_tos(), argv[1], LOOKUP_NAME_LOCAL, + &memberdomain, &membername, &member, &membertype)) + { + /* try it as a SID */ + + if ( !string_to_sid( &member, argv[1] ) ) { + d_fprintf(stderr, _("Could not find member %s\n"), + argv[1]); + return -1; + } + + if ( !lookup_sid(talloc_tos(), &member, &memberdomain, + &membername, &membertype) ) + { + d_fprintf(stderr, _("Could not resolve SID %s\n"), + argv[1]); + return -1; + } + } + + if ((grouptype == SID_NAME_ALIAS) || (grouptype == SID_NAME_WKN_GRP)) { + if ((membertype != SID_NAME_USER) && + (membertype != SID_NAME_ALIAS) && + (membertype != SID_NAME_DOM_GRP)) { + d_fprintf(stderr, _("Can't add %s: only users, domain " + "groups and domain local groups " + "can be added. %s is a %s\n"), + argv[0], argv[1], + sid_type_lookup(membertype)); + return -1; + } + status = pdb_add_aliasmem(&group, &member); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Adding local group member failed " + "with %s\n"), nt_errstr(status)); + return -1; + } + } else if (grouptype == SID_NAME_DOM_GRP) { + uint32_t grouprid, memberrid; + + sid_peek_rid(&group, &grouprid); + sid_peek_rid(&member, &memberrid); + + status = pdb_add_groupmem(talloc_tos(), grouprid, memberrid); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Adding domain group member failed " + "with %s\n"), nt_errstr(status)); + return -1; + } + } else { + d_fprintf(stderr, _("Can only add members to local groups so " + "far, %s is a %s\n"), argv[0], + sid_type_lookup(grouptype)); + return -1; + } + + d_printf(_("Added %s\\%s to %s\\%s\n"), memberdomain, membername, + groupdomain, groupname); + + return 0; +} + +/* + * Delete a group member + */ + +static int net_sam_delmem(struct net_context *c, int argc, const char **argv) +{ + const char *groupdomain, *groupname; + const char *memberdomain = NULL; + const char *membername = NULL; + struct dom_sid group, member; + enum lsa_SidType grouptype; + NTSTATUS status; + + if (argc != 2 || c->display_usage) { + d_fprintf(stderr,"%s\n%s", + _("Usage:"), + _("net sam delmem <group> <member>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &groupdomain, &groupname, &group, &grouptype)) { + d_fprintf(stderr, _("Could not find group %s\n"), argv[0]); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[1], LOOKUP_NAME_LOCAL, + &memberdomain, &membername, &member, NULL)) { + if (!string_to_sid(&member, argv[1])) { + d_fprintf(stderr, _("Could not find member %s\n"), + argv[1]); + return -1; + } + } + + if ((grouptype == SID_NAME_ALIAS) || + (grouptype == SID_NAME_WKN_GRP)) { + status = pdb_del_aliasmem(&group, &member); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr,_("Deleting local group member failed " + "with %s\n"), nt_errstr(status)); + return -1; + } + } else if (grouptype == SID_NAME_DOM_GRP) { + uint32_t grouprid, memberrid; + + sid_peek_rid(&group, &grouprid); + sid_peek_rid(&member, &memberrid); + + status = pdb_del_groupmem(talloc_tos(), grouprid, memberrid); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Deleting domain group member " + "failed with %s\n"), nt_errstr(status)); + return -1; + } + } else { + d_fprintf(stderr, _("Can only delete members from local groups " + "so far, %s is a %s\n"), argv[0], + sid_type_lookup(grouptype)); + return -1; + } + + if (membername != NULL) { + d_printf(_("Deleted %s\\%s from %s\\%s\n"), + memberdomain, membername, groupdomain, groupname); + } else { + struct dom_sid_buf buf; + d_printf(_("Deleted %s from %s\\%s\n"), + dom_sid_str_buf(&member, &buf), + groupdomain, + groupname); + } + + return 0; +} + +/* + * List group members + */ + +static int net_sam_listmem(struct net_context *c, int argc, const char **argv) +{ + const char *groupdomain, *groupname; + struct dom_sid group; + struct dom_sid *members = NULL; + size_t i, num_members = 0; + enum lsa_SidType grouptype; + NTSTATUS status; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam listmem <group>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &groupdomain, &groupname, &group, &grouptype)) { + d_fprintf(stderr, _("Could not find group %s\n"), argv[0]); + return -1; + } + + if ((grouptype == SID_NAME_ALIAS) || + (grouptype == SID_NAME_WKN_GRP)) { + status = pdb_enum_aliasmem(&group, talloc_tos(), &members, + &num_members); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Listing group members failed with " + "%s\n"), nt_errstr(status)); + return -1; + } + } else if (grouptype == SID_NAME_DOM_GRP) { + uint32_t *rids; + + status = pdb_enum_group_members(talloc_tos(), &group, + &rids, &num_members); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Listing group members failed with " + "%s\n"), nt_errstr(status)); + return -1; + } + + members = talloc_array(talloc_tos(), struct dom_sid, + num_members); + if (members == NULL) { + TALLOC_FREE(rids); + return -1; + } + + for (i=0; i<num_members; i++) { + sid_compose(&members[i], get_global_sam_sid(), + rids[i]); + } + TALLOC_FREE(rids); + } else { + d_fprintf(stderr,_("Can only list local group members so far.\n" + "%s is a %s\n"), argv[0], sid_type_lookup(grouptype)); + return -1; + } + + d_printf(_("%s\\%s has %u members\n"), groupdomain, groupname, + (unsigned int)num_members); + for (i=0; i<num_members; i++) { + const char *dom, *name; + if (lookup_sid(talloc_tos(), &members[i], &dom, &name, NULL)) { + d_printf(" %s\\%s\n", dom, name); + } else { + struct dom_sid_buf buf; + d_printf(" %s\n", + dom_sid_str_buf(&members[i], &buf)); + } + } + + TALLOC_FREE(members); + + return 0; +} + +/* + * Do the listing + */ +static int net_sam_do_list(struct net_context *c, int argc, const char **argv, + struct pdb_search *search, const char *what) +{ + bool verbose = (argc == 1); + + if ((argc > 1) || c->display_usage || + ((argc == 1) && !strequal(argv[0], "verbose"))) { + d_fprintf(stderr, "%s\n", _("Usage:")); + d_fprintf(stderr, _("net sam list %s [verbose]\n"), what); + return -1; + } + + if (search == NULL) { + d_fprintf(stderr, _("Could not start search\n")); + return -1; + } + + while (true) { + struct samr_displayentry entry; + if (!search->next_entry(search, &entry)) { + break; + } + if (verbose) { + d_printf("%s:%d:%s\n", + entry.account_name, + entry.rid, + entry.description); + } else { + d_printf("%s\n", entry.account_name); + } + } + + TALLOC_FREE(search); + return 0; +} + +static int net_sam_list_users(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_do_list(c, argc, argv, + pdb_search_users(talloc_tos(), ACB_NORMAL), + "users"); +} + +static int net_sam_list_groups(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_do_list(c, argc, argv, pdb_search_groups(talloc_tos()), + "groups"); +} + +static int net_sam_list_localgroups(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_do_list(c, argc, argv, + pdb_search_aliases(talloc_tos(), + get_global_sam_sid()), + "localgroups"); +} + +static int net_sam_list_builtin(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_do_list(c, argc, argv, + pdb_search_aliases(talloc_tos(), + &global_sid_Builtin), + "builtin"); +} + +static int net_sam_list_workstations(struct net_context *c, int argc, + const char **argv) +{ + return net_sam_do_list(c, argc, argv, + pdb_search_users(talloc_tos(), ACB_WSTRUST), + "workstations"); +} + +/* + * List stuff + */ + +static int net_sam_list(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "users", + net_sam_list_users, + NET_TRANSPORT_LOCAL, + N_("List SAM users"), + N_("net sam list users\n" + " List SAM users") + }, + { + "groups", + net_sam_list_groups, + NET_TRANSPORT_LOCAL, + N_("List SAM groups"), + N_("net sam list groups\n" + " List SAM groups") + }, + { + "localgroups", + net_sam_list_localgroups, + NET_TRANSPORT_LOCAL, + N_("List SAM local groups"), + N_("net sam list localgroups\n" + " List SAM local groups") + }, + { + "builtin", + net_sam_list_builtin, + NET_TRANSPORT_LOCAL, + N_("List builtin groups"), + N_("net sam list builtin\n" + " List builtin groups") + }, + { + "workstations", + net_sam_list_workstations, + NET_TRANSPORT_LOCAL, + N_("List domain member workstations"), + N_("net sam list workstations\n" + " List domain member workstations") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net sam list", func); +} + +/* + * Show details of SAM entries + */ + +static int net_sam_show(struct net_context *c, int argc, const char **argv) +{ + struct dom_sid sid; + struct dom_sid_buf buf; + enum lsa_SidType type; + const char *dom, *name; + + if (argc != 1 || c->display_usage) { + d_fprintf(stderr, "%s\n%s", + _("Usage:"), + _("net sam show <name>\n")); + return -1; + } + + if (!lookup_name(talloc_tos(), argv[0], LOOKUP_NAME_LOCAL, + &dom, &name, &sid, &type)) { + d_fprintf(stderr, _("Could not find name %s\n"), argv[0]); + return -1; + } + + d_printf(_("%s\\%s is a %s with SID %s\n"), dom, name, + sid_type_lookup(type), dom_sid_str_buf(&sid, &buf)); + + return 0; +} + +#ifdef HAVE_LDAP + +/* + * Init an LDAP tree with default users and Groups + * if ldapsam:editposix is enabled + */ + +static int net_sam_provision(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *tc; + char *ldap_bk; + char *ldap_uri = NULL; + char *p; + struct smbldap_state *state = NULL; + GROUP_MAP *gmap = NULL; + struct dom_sid gsid; + gid_t domusers_gid = -1; + gid_t domadmins_gid = -1; + struct samu *samuser; + struct passwd *pwd; + bool is_ipa = false; + char *bind_dn = NULL; + char *bind_secret = NULL; + NTSTATUS status; + + if (c->display_usage) { + d_printf( "%s\n" + "net sam provision\n" + " %s\n", + _("Usage:"), + _("Init an LDAP tree with default users/groups")); + return 0; + } + + tc = talloc_new(NULL); + if (!tc) { + d_fprintf(stderr, _("Out of Memory!\n")); + return -1; + } + + if ((ldap_bk = talloc_strdup(tc, lp_passdb_backend())) == NULL) { + d_fprintf(stderr, _("talloc failed\n")); + talloc_free(tc); + return -1; + } + p = strchr(ldap_bk, ':'); + if (p) { + *p = 0; + ldap_uri = talloc_strdup(tc, p+1); + trim_char(ldap_uri, ' ', ' '); + } + + trim_char(ldap_bk, ' ', ' '); + + if (strcmp(ldap_bk, "IPA_ldapsam") == 0 ) { + is_ipa = true; + } + + if (strcmp(ldap_bk, "ldapsam") != 0 && !is_ipa ) { + d_fprintf(stderr, + _("Provisioning works only with ldapsam backend\n")); + goto failed; + } + + if (!lp_parm_bool(-1, "ldapsam", "trusted", false) || + !lp_parm_bool(-1, "ldapsam", "editposix", false)) { + + d_fprintf(stderr, _("Provisioning works only if ldapsam:trusted" + " and ldapsam:editposix are enabled.\n")); + goto failed; + } + + if (!is_ipa && !winbind_ping()) { + d_fprintf(stderr, _("winbind seems not to run. Provisioning " + "LDAP only works when winbind runs.\n")); + goto failed; + } + + if (!fetch_ldap_pw(&bind_dn, &bind_secret)) { + d_fprintf(stderr, _("Failed to retrieve LDAP password from secrets.tdb\n")); + goto failed; + } + + status = smbldap_init(tc, NULL, ldap_uri, false, bind_dn, bind_secret, &state); + + BURN_FREE_STR(bind_secret); + SAFE_FREE(bind_dn); + + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Unable to connect to the LDAP server.\n")); + goto failed; + } + + d_printf(_("Checking for Domain Users group.\n")); + + sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_USERS); + + gmap = talloc_zero(tc, GROUP_MAP); + if (!gmap) { + d_printf(_("Out of memory!\n")); + goto failed; + } + + if (!pdb_getgrsid(gmap, gsid)) { + struct dom_sid_buf gsid_str; + LDAPMod **mods = NULL; + char *dn; + char *uname; + char *wname; + char *gidstr; + char *gtype; + int rc; + + d_printf(_("Adding the Domain Users group.\n")); + + /* lets allocate a new groupid for this group */ + if (is_ipa) { + domusers_gid = 999; + } else { + if (!winbind_allocate_gid(&domusers_gid)) { + d_fprintf(stderr, _("Unable to allocate a new gid to " + "create Domain Users group!\n")); + goto domu_done; + } + } + + uname = talloc_strdup(tc, "domusers"); + wname = talloc_strdup(tc, "Domain Users"); + dn = talloc_asprintf(tc, "cn=%s,%s", "domusers", + lp_ldap_group_suffix(talloc_tos())); + gidstr = talloc_asprintf(tc, "%u", (unsigned int)domusers_gid); + gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP); + + if (!uname || !wname || !dn || !gidstr || !gtype) { + d_fprintf(stderr, "Out of Memory!\n"); + goto failed; + } + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP); + if (is_ipa) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup"); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid", + dom_sid_str_buf(&gsid, &gsid_str)); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype); + + smbldap_talloc_autofree_ldapmod(tc, mods); + + rc = smbldap_add(state, dn, mods); + + if (rc != LDAP_SUCCESS) { + d_fprintf(stderr, _("Failed to add Domain Users group " + "to ldap directory\n")); + } + + if (is_ipa) { + if (!pdb_getgrsid(gmap, gsid)) { + d_fprintf(stderr, _("Failed to read just " + "created domain group.\n")); + goto failed; + } else { + domusers_gid = gmap->gid; + } + } + } else { + domusers_gid = gmap->gid; + d_printf(_("found!\n")); + } + +domu_done: + + d_printf(_("Checking for Domain Admins group.\n")); + + sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_ADMINS); + + if (!pdb_getgrsid(gmap, gsid)) { + struct dom_sid_buf gsid_str; + LDAPMod **mods = NULL; + char *dn; + char *uname; + char *wname; + char *gidstr; + char *gtype; + int rc; + + d_printf(_("Adding the Domain Admins group.\n")); + + /* lets allocate a new groupid for this group */ + if (is_ipa) { + domadmins_gid = 999; + } else { + if (!winbind_allocate_gid(&domadmins_gid)) { + d_fprintf(stderr, _("Unable to allocate a new gid to " + "create Domain Admins group!\n")); + goto doma_done; + } + } + + uname = talloc_strdup(tc, "domadmins"); + wname = talloc_strdup(tc, "Domain Admins"); + dn = talloc_asprintf(tc, "cn=%s,%s", "domadmins", + lp_ldap_group_suffix(talloc_tos())); + gidstr = talloc_asprintf(tc, "%u", (unsigned int)domadmins_gid); + gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP); + + if (!uname || !wname || !dn || !gidstr || !gtype) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP); + if (is_ipa) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup"); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid", + dom_sid_str_buf(&gsid, &gsid_str)); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype); + + smbldap_talloc_autofree_ldapmod(tc, mods); + + rc = smbldap_add(state, dn, mods); + + if (rc != LDAP_SUCCESS) { + d_fprintf(stderr, _("Failed to add Domain Admins group " + "to ldap directory\n")); + } + + if (is_ipa) { + if (!pdb_getgrsid(gmap, gsid)) { + d_fprintf(stderr, _("Failed to read just " + "created domain group.\n")); + goto failed; + } else { + domadmins_gid = gmap->gid; + } + } + } else { + domadmins_gid = gmap->gid; + d_printf(_("found!\n")); + } + +doma_done: + + d_printf(_("Check for Administrator account.\n")); + + samuser = samu_new(tc); + if (!samuser) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + if (!pdb_getsampwnam(samuser, "Administrator")) { + LDAPMod **mods = NULL; + struct dom_sid sid; + struct dom_sid_buf sid_str; + char *dn; + char *name; + char *uidstr; + char *gidstr; + char *shell; + char *dir; + char *princ; + uid_t uid; + int rc; + + d_printf(_("Adding the Administrator user.\n")); + + if (domadmins_gid == -1) { + d_fprintf(stderr, + _("Can't create Administrator user, Domain " + "Admins group not available!\n")); + goto done; + } + + if (is_ipa) { + uid = 999; + } else { + if (!winbind_allocate_uid(&uid)) { + d_fprintf(stderr, + _("Unable to allocate a new uid to create " + "the Administrator user!\n")); + goto done; + } + } + + name = talloc_strdup(tc, "Administrator"); + dn = talloc_asprintf(tc, "uid=Administrator,%s", + lp_ldap_user_suffix(talloc_tos())); + uidstr = talloc_asprintf(tc, "%u", (unsigned int)uid); + gidstr = talloc_asprintf(tc, "%u", (unsigned int)domadmins_gid); + dir = talloc_sub_specified(tc, lp_template_homedir(), + "Administrator", + NULL, + get_global_sam_name(), + uid, domadmins_gid); + shell = talloc_sub_specified(tc, lp_template_shell(), + "Administrator", + NULL, + get_global_sam_name(), + uid, domadmins_gid); + + if (!name || !dn || !uidstr || !gidstr || !dir || !shell) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + sid_compose(&sid, get_global_sam_sid(), DOMAIN_RID_ADMINISTRATOR); + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_ACCOUNT); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXACCOUNT); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SAMBASAMACCOUNT); + if (is_ipa) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "person"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "organizationalperson"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetorgperson"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetuser"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbprincipalaux"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbticketpolicyaux"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sn", name); + princ = talloc_asprintf(tc, "%s@%s", name, lp_realm()); + if (!princ) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "krbPrincipalName", princ); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "uid", name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "uidNumber", uidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "homeDirectory", dir); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "loginShell", shell); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSID", + dom_sid_str_buf(&sid, &sid_str)); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags", + pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED, + NEW_PW_FORMAT_SPACE_PADDED_LEN)); + + smbldap_talloc_autofree_ldapmod(tc, mods); + + rc = smbldap_add(state, dn, mods); + + if (rc != LDAP_SUCCESS) { + d_fprintf(stderr, _("Failed to add Administrator user " + "to ldap directory\n")); + } + + if (is_ipa) { + if (!pdb_getsampwnam(samuser, "Administrator")) { + d_fprintf(stderr, _("Failed to read just " + "created user.\n")); + goto failed; + } + } + } else { + d_printf(_("found!\n")); + } + + d_printf(_("Checking for Guest user.\n")); + + samuser = samu_new(tc); + if (!samuser) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + if (!pdb_getsampwnam(samuser, lp_guest_account())) { + LDAPMod **mods = NULL; + struct dom_sid sid; + struct dom_sid_buf sid_str; + char *dn; + char *uidstr; + char *gidstr; + int rc; + + d_printf(_("Adding the Guest user.\n")); + + sid_compose(&sid, get_global_sam_sid(), DOMAIN_RID_GUEST); + + pwd = Get_Pwnam_alloc(tc, lp_guest_account()); + + if (!pwd) { + if (domusers_gid == -1) { + d_fprintf(stderr, + _("Can't create Guest user, Domain " + "Users group not available!\n")); + goto done; + } + if ((pwd = talloc(tc, struct passwd)) == NULL) { + d_fprintf(stderr, _("talloc failed\n")); + goto done; + } + pwd->pw_name = talloc_strdup(pwd, lp_guest_account()); + + if (is_ipa) { + pwd->pw_uid = 999; + } else { + if (!winbind_allocate_uid(&(pwd->pw_uid))) { + d_fprintf(stderr, + _("Unable to allocate a new uid to " + "create the Guest user!\n")); + goto done; + } + } + pwd->pw_gid = domusers_gid; + pwd->pw_dir = talloc_strdup(tc, "/"); + pwd->pw_shell = talloc_strdup(tc, "/bin/false"); + if (!pwd->pw_dir || !pwd->pw_shell) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + } + + dn = talloc_asprintf(tc, "uid=%s,%s", pwd->pw_name, + lp_ldap_user_suffix (talloc_tos())); + uidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_uid); + gidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_gid); + if (!dn || !uidstr || !gidstr) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_ACCOUNT); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXACCOUNT); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_SAMBASAMACCOUNT); + if (is_ipa) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "person"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "organizationalperson"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetorgperson"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "inetuser"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbprincipalaux"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "krbticketpolicyaux"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sn", pwd->pw_name); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "uid", pwd->pw_name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", pwd->pw_name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", pwd->pw_name); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "uidNumber", uidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr); + if ((pwd->pw_dir != NULL) && (pwd->pw_dir[0] != '\0')) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "homeDirectory", pwd->pw_dir); + } + if ((pwd->pw_shell != NULL) && (pwd->pw_shell[0] != '\0')) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "loginShell", pwd->pw_shell); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSID", + dom_sid_str_buf(&sid, &sid_str)); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags", + pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED, + NEW_PW_FORMAT_SPACE_PADDED_LEN)); + + smbldap_talloc_autofree_ldapmod(tc, mods); + + rc = smbldap_add(state, dn, mods); + + if (rc != LDAP_SUCCESS) { + d_fprintf(stderr, _("Failed to add Guest user to " + "ldap directory\n")); + } + + if (is_ipa) { + if (!pdb_getsampwnam(samuser, lp_guest_account())) { + d_fprintf(stderr, _("Failed to read just " + "created user.\n")); + goto failed; + } + } + } else { + d_printf(_("found!\n")); + } + + d_printf(_("Checking Guest's group.\n")); + + pwd = Get_Pwnam_alloc(tc, lp_guest_account()); + if (!pwd) { + d_fprintf(stderr, + _("Failed to find just created Guest account!\n" + " Is nss properly configured?!\n")); + goto failed; + } + + if (pwd->pw_gid == domusers_gid) { + d_printf(_("found!\n")); + goto done; + } + + if (!pdb_getgrgid(gmap, pwd->pw_gid)) { + struct dom_sid_buf gsid_str; + LDAPMod **mods = NULL; + char *dn; + char *uname; + char *wname; + char *gidstr; + char *gtype; + int rc; + + d_printf(_("Adding the Domain Guests group.\n")); + + uname = talloc_strdup(tc, "domguests"); + wname = talloc_strdup(tc, "Domain Guests"); + dn = talloc_asprintf(tc, "cn=%s,%s", "domguests", + lp_ldap_group_suffix(talloc_tos())); + gidstr = talloc_asprintf(tc, "%u", (unsigned int)pwd->pw_gid); + gtype = talloc_asprintf(tc, "%d", SID_NAME_DOM_GRP); + + if (!uname || !wname || !dn || !gidstr || !gtype) { + d_fprintf(stderr, _("Out of Memory!\n")); + goto failed; + } + + sid_compose(&gsid, get_global_sam_sid(), DOMAIN_RID_GUESTS); + + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP); + if (is_ipa) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "groupofnames"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "nestedgroup"); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", "ipausergroup"); + } + smbldap_set_mod(&mods, LDAP_MOD_ADD, "cn", uname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "displayName", wname); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "gidNumber", gidstr); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaSid", + dom_sid_str_buf(&gsid, &gsid_str)); + smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype); + + smbldap_talloc_autofree_ldapmod(tc, mods); + + rc = smbldap_add(state, dn, mods); + + if (rc != LDAP_SUCCESS) { + d_fprintf(stderr, + _("Failed to add Domain Guests group to ldap " + "directory\n")); + } + } else { + d_printf(_("found!\n")); + } + + +done: + talloc_free(state); + return 0; + +failed: + talloc_free(state); + return -1; +} + +#endif + +/*********************************************************** + migrated functionality from smbgroupedit + **********************************************************/ +int net_sam(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "createbuiltingroup", + net_sam_createbuiltingroup, + NET_TRANSPORT_LOCAL, + N_("Create a new BUILTIN group"), + N_("net sam createbuiltingroup\n" + " Create a new BUILTIN group") + }, + { + "createlocalgroup", + net_sam_createlocalgroup, + NET_TRANSPORT_LOCAL, + N_("Create a new local group"), + N_("net sam createlocalgroup\n" + " Create a new local group") + }, + { + "createdomaingroup", + net_sam_createdomaingroup, + NET_TRANSPORT_LOCAL, + N_("Create a new group"), + N_("net sam createdomaingroup\n" + " Create a new group") + }, + { + "deletelocalgroup", + net_sam_deletelocalgroup, + NET_TRANSPORT_LOCAL, + N_("Delete an existing local group"), + N_("net sam deletelocalgroup\n" + " Delete an existing local group") + }, + { + "deletedomaingroup", + net_sam_deletedomaingroup, + NET_TRANSPORT_LOCAL, + N_("Delete a domain group"), + N_("net sam deletedomaingroup\n" + " Delete a group") + }, + { + "mapunixgroup", + net_sam_mapunixgroup, + NET_TRANSPORT_LOCAL, + N_("Map a unix group to a domain group"), + N_("net sam mapunixgroup\n" + " Map a unix group to a domain group") + }, + { + "unmapunixgroup", + net_sam_unmapunixgroup, + NET_TRANSPORT_LOCAL, + N_("Remove a group mapping of an unix group to a " + "domain group"), + N_("net sam unmapunixgroup\n" + " Remove a group mapping of an unix group to a " + "domain group") + }, + { + "addmem", + net_sam_addmem, + NET_TRANSPORT_LOCAL, + N_("Add a member to a group"), + N_("net sam addmem\n" + " Add a member to a group") + }, + { + "delmem", + net_sam_delmem, + NET_TRANSPORT_LOCAL, + N_("Delete a member from a group"), + N_("net sam delmem\n" + " Delete a member from a group") + }, + { + "listmem", + net_sam_listmem, + NET_TRANSPORT_LOCAL, + N_("List group members"), + N_("net sam listmem\n" + " List group members") + }, + { + "list", + net_sam_list, + NET_TRANSPORT_LOCAL, + N_("List users, groups and local groups"), + N_("net sam list\n" + " List users, groups and local groups") + }, + { + "show", + net_sam_show, + NET_TRANSPORT_LOCAL, + N_("Show details of a SAM entry"), + N_("net sam show\n" + " Show details of a SAM entry") + }, + { + "set", + net_sam_set, + NET_TRANSPORT_LOCAL, + N_("Set details of a SAM account"), + N_("net sam set\n" + " Set details of a SAM account") + }, + { + "policy", + net_sam_policy, + NET_TRANSPORT_LOCAL, + N_("Set account policies"), + N_("net sam policy\n" + " Set account policies") + }, + { + "rights", + net_sam_rights, + NET_TRANSPORT_LOCAL, + N_("Manipulate user privileges"), + N_("net sam rights\n" + " Manipulate user privileges") + }, +#ifdef HAVE_LDAP + { + "provision", + net_sam_provision, + NET_TRANSPORT_LOCAL, + N_("Provision a clean user database"), + N_("net sam privison\n" + " Provision a clear user database") + }, +#endif + {NULL, NULL, 0, NULL, NULL} + }; + + if (getuid() != 0) { + d_fprintf(stderr, _("You are not root, most things won't " + "work\n")); + } + + return net_run_function(c, argc, argv, "net sam", func); +} + diff --git a/source3/utils/net_serverid.c b/source3/utils/net_serverid.c new file mode 100644 index 0000000..435b63b --- /dev/null +++ b/source3/utils/net_serverid.c @@ -0,0 +1,703 @@ +/* + Samba Unix/Linux SMB client library + net serverid commands + Copyright (C) Volker Lendecke 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "lib/util/server_id.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_rbt.h" +#include "serverid.h" +#include "session.h" +#include "smbd/globals.h" +#include "smbd/smbXsrv_open.h" +#include "util_tdb.h" +#include "librpc/gen_ndr/ndr_open_files.h" + +struct wipedbs_record_marker { + struct wipedbs_record_marker *prev, *next; + TDB_DATA key, val; + const char *desc; +}; + +struct wipedbs_server_data { + struct server_id server_id; + const char *server_id_str; + bool exists; + struct wipedbs_record_marker *session_records; + struct wipedbs_record_marker *tcon_records; + struct wipedbs_record_marker *open_records; +}; + +struct wipedbs_state { + struct db_context *id2server_data; + struct { + struct { + int total; + int existing; + int disconnected; + } server; + struct { + int total; + int disconnected; + int todelete; + int failure; + } session, tcon, open; + int open_timed_out; + } stat; + struct server_id *server_ids; + bool *server_exists; + int idx; + struct db_context *session_db; + struct db_context *tcon_db; + struct db_context *open_db; + struct timeval now; + bool testmode; + bool verbose; +}; + +static struct wipedbs_server_data *get_server_data(struct wipedbs_state *state, + const struct server_id *id) +{ + struct wipedbs_server_data *ret = NULL; + TDB_DATA key, val = tdb_null; + NTSTATUS status; + + key = make_tdb_data((const void*)&id->unique_id, sizeof(id->unique_id)); + status = dbwrap_fetch(state->id2server_data, talloc_tos(), key, &val); + if (NT_STATUS_IS_OK(status)) { + ret = *(struct wipedbs_server_data**) val.dptr; + TALLOC_FREE(val.dptr); + } else if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + struct server_id_buf idbuf; + + server_id_str_buf(*id, &idbuf); + + ret = talloc_zero(state->id2server_data, + struct wipedbs_server_data); + if (ret == NULL) { + DEBUG(0, ("Failed to allocate server entry for %s\n", + idbuf.buf)); + goto done; + } + ret->server_id = *id; + ret->server_id_str = talloc_strdup(ret, idbuf.buf); + ret->exists = true; + val = make_tdb_data((const void*)&ret, sizeof(ret)); + status = dbwrap_store(state->id2server_data, + key, val, TDB_INSERT); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to store server entry for %s: %s\n", + idbuf.buf, nt_errstr(status))); + } + goto done; + } else { + struct server_id_buf idbuf; + DEBUG(0, ("Failed to fetch server entry for %s: %s\n", + server_id_str_buf(*id, &idbuf), nt_errstr(status))); + goto done; + } + if (!server_id_equal(id, &ret->server_id)) { + struct server_id_buf idbuf1, idbuf2; + DEBUG(0, ("uniq id collision for %s and %s\n", + server_id_str_buf(*id, &idbuf1), + server_id_str_buf(ret->server_id, &idbuf2))); + smb_panic("server_id->unique_id not unique!"); + } +done: + return ret; +} + +static int wipedbs_traverse_sessions(struct smbXsrv_session_global0 *session, + void *wipedbs_state) +{ + struct wipedbs_state *state = + talloc_get_type_abort(wipedbs_state, + struct wipedbs_state); + struct wipedbs_server_data *sd; + struct wipedbs_record_marker *rec; + TDB_DATA tmp; + int ret = -1; + + assert(session->num_channels == 1); + + state->stat.session.total++; + + sd = get_server_data(state, &session->channels[0].server_id); + if (sd == NULL) { + goto done; + } + + if (server_id_is_disconnected(&sd->server_id)) { + state->stat.session.disconnected++; + } + + rec = talloc_zero(sd, struct wipedbs_record_marker); + if (rec == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + tmp = dbwrap_record_get_key(session->db_rec); + rec->key = tdb_data_talloc_copy(rec, tmp); + tmp = dbwrap_record_get_value(session->db_rec); + rec->val = tdb_data_talloc_copy(rec, tmp); + + rec->desc = talloc_asprintf( + rec, "session[global: %u wire: %llu]", + session->session_global_id, + (long long unsigned)session->session_wire_id); + + if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) || + (rec->desc == NULL)) + { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + state->session_db = dbwrap_record_get_db(session->db_rec); + + DLIST_ADD(sd->session_records, rec); + ret = 0; +done: + return ret; +} + +static int wipedbs_traverse_tcon(struct smbXsrv_tcon_global0 *tcon, + void *wipedbs_state) +{ + struct wipedbs_state *state = + talloc_get_type_abort(wipedbs_state, + struct wipedbs_state); + struct wipedbs_server_data *sd; + struct wipedbs_record_marker *rec; + TDB_DATA tmp; + int ret = -1; + + state->stat.tcon.total++; + + sd = get_server_data(state, &tcon->server_id); + if (sd == NULL) { + goto done; + } + + if (server_id_is_disconnected(&sd->server_id)) { + state->stat.tcon.disconnected++; + } + + rec = talloc_zero(sd, struct wipedbs_record_marker); + if (rec == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + tmp = dbwrap_record_get_key(tcon->db_rec); + rec->key = tdb_data_talloc_copy(rec, tmp); + tmp = dbwrap_record_get_value(tcon->db_rec); + rec->val = tdb_data_talloc_copy(rec, tmp); + + rec->desc = talloc_asprintf( + rec, "tcon[global: %u wire: %u session: %u share: %s]", + tcon->tcon_global_id, tcon->tcon_wire_id, + tcon->session_global_id, tcon->share_name); + + if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) || + (rec->desc == NULL)) + { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + state->tcon_db = dbwrap_record_get_db(tcon->db_rec); + + DLIST_ADD(sd->tcon_records, rec); + ret = 0; + +done: + return ret; +} + +static int wipedbs_traverse_open(struct db_record *db_rec, + struct smbXsrv_open_global0 *open, + void *wipedbs_state) +{ + struct wipedbs_state *state = + talloc_get_type_abort(wipedbs_state, + struct wipedbs_state); + struct wipedbs_server_data *sd; + struct wipedbs_record_marker *rec; + TDB_DATA tmp; + int ret = -1; + + state->stat.open.total++; + + sd = get_server_data(state, &open->server_id); + if (sd == NULL) { + goto done; + } + + if (server_id_is_disconnected(&sd->server_id)) { + struct timeval disconnect_time; + int64_t tdiff; + bool reached; + + state->stat.open.disconnected++; + + nttime_to_timeval(&disconnect_time, open->disconnect_time); + tdiff = usec_time_diff(&state->now, &disconnect_time); + reached = (tdiff >= INT64_C(1000)*open->durable_timeout_msec); + + if (state->verbose) { + TALLOC_CTX *mem_ctx = talloc_new(talloc_tos()); + enum ndr_err_code ndr_err; + struct vfs_default_durable_cookie cookie; + + ndr_err = ndr_pull_struct_blob( + &open->backend_cookie, mem_ctx, &cookie, + (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + d_printf("ndr_pull_struct_blob failed\n"); + ret = -1; + goto done; + } + + d_printf("open[%s/%s id: 0x%" PRIx32 "] disconnected at " + "[%s] %us ago with timeout of %us " + "-%s reached\n", + cookie.servicepath, cookie.base_name, + open->open_global_id, + nt_time_string(mem_ctx, open->disconnect_time), + (unsigned)(tdiff/1000000), + open->durable_timeout_msec / 1000, + reached ? "" : " not"); + talloc_free(mem_ctx); + } + + if (!reached) { + ret = 0; + goto done; + } + state->stat.open_timed_out++; + } + + rec = talloc_zero(sd, struct wipedbs_record_marker); + if (rec == NULL) { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + tmp = dbwrap_record_get_key(db_rec); + rec->key = tdb_data_talloc_copy(rec, tmp); + tmp = dbwrap_record_get_value(db_rec); + rec->val = tdb_data_talloc_copy(rec, tmp); + + rec->desc = talloc_asprintf( + rec, "open[global: %u persistent: %llu volatile: %llu]", + open->open_global_id, + (long long unsigned)open->open_persistent_id, + (long long unsigned)open->open_volatile_id); + + if ((rec->key.dptr == NULL) || (rec->val.dptr == NULL) || + (rec->desc == NULL)) + { + DEBUG(0, ("Out of memory!\n")); + goto done; + } + + state->open_db = dbwrap_record_get_db(db_rec); + + DLIST_ADD(sd->open_records, rec); + ret = 0; + +done: + return ret; +} + +static int wipedbs_traverse_nop(struct db_record *rec, void *private_data) +{ + return 0; +} + +static int wipedbs_traverse_fill_ids(struct db_record *rec, void *wipedbs_state) +{ + struct wipedbs_state *state = talloc_get_type_abort( + wipedbs_state, struct wipedbs_state); + + TDB_DATA val = dbwrap_record_get_value(rec); + + struct wipedbs_server_data *sd = talloc_get_type_abort( + *(void**)val.dptr, struct wipedbs_server_data); + + state->server_ids[state->idx] = sd->server_id; + state->idx++; + return 0; +} + +static int wipedbs_traverse_set_exists(struct db_record *rec, + void *wipedbs_state) +{ + struct wipedbs_state *state = talloc_get_type_abort( + wipedbs_state, struct wipedbs_state); + + TDB_DATA val = dbwrap_record_get_value(rec); + + struct wipedbs_server_data *sd = talloc_get_type_abort( + *(void**)val.dptr, struct wipedbs_server_data); + + /* assume a stable traverse order for rbt */ + SMB_ASSERT(server_id_equal(&state->server_ids[state->idx], + &sd->server_id)); + sd->exists = state->server_exists[state->idx]; + + if (sd->exists) { + state->stat.server.existing++; + } + if (server_id_is_disconnected(&sd->server_id)) { + state->stat.server.disconnected++; + } + + state->idx++; + return 0; +} + +static bool serverids_exist(const struct server_id *ids, int num_ids, + bool *results) +{ + int i; + + for (i=0; i<num_ids; i++) { + results[i] = serverid_exists(&ids[i]); + } + + return true; +} + + +static NTSTATUS wipedbs_check_server_exists(struct wipedbs_state *state) +{ + NTSTATUS status; + bool ok; + int num_servers; + + status = dbwrap_traverse_read(state->id2server_data, + wipedbs_traverse_nop, NULL, &num_servers); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse temporary database\n")); + goto done; + } + state->stat.server.total = num_servers; + + state->server_ids = talloc_array(state, struct server_id, num_servers); + state->server_exists = talloc_array(state, bool, num_servers); + if (state->server_ids == NULL || state->server_exists == NULL) { + DEBUG(0, ("Out of memory\n")); + goto done; + } + + state->idx = 0; + status = dbwrap_traverse_read(state->id2server_data, + wipedbs_traverse_fill_ids, + state, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse temporary database\n")); + goto done; + } + + ok = serverids_exist(state->server_ids, num_servers, state->server_exists); + if (!ok) { + DEBUG(0, ("Calling serverids_exist failed\n")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + state->idx = 0; + status = dbwrap_traverse_read(state->id2server_data, + wipedbs_traverse_set_exists, state, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse temporary database\n")); + goto done; + } +done: + TALLOC_FREE(state->server_ids); + TALLOC_FREE(state->server_exists); + return status; +} + +struct wipedbs_delete_state { + struct wipedbs_record_marker *cur; + bool verbose; + bool dry_run; + size_t total; + size_t num; +}; + +static void wipedbs_delete_fn( + struct db_record *rec, TDB_DATA value, void *private_data) +{ + struct db_context *db = dbwrap_record_get_db(rec); + struct wipedbs_delete_state *state = private_data; + struct wipedbs_record_marker *cur = state->cur; + NTSTATUS status = NT_STATUS_OK; + + state->total += 1; + + if (!tdb_data_equal(value, cur->val)) { + DBG_ERR("Warning: record <%s> from %s changed," + "skip record!\n", + cur->desc, dbwrap_name(db)); + return; + } + + if (state->verbose) { + d_printf("deleting %s\n", cur->desc); + } + + if (!state->dry_run) { + status = dbwrap_record_delete(rec); + } + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to delete record <%s> from %s: %s\n", + cur->desc, + dbwrap_name(db), + nt_errstr(status)); + return; + } + + state->num += 1; +} + +static int wipedbs_delete_records(struct db_context *db, + struct wipedbs_record_marker *records, + bool dry_run, bool verbose, int *count) +{ + struct wipedbs_delete_state state = { + .verbose = verbose, .dry_run = dry_run, + }; + + if (db == NULL) { + return 0; + } + + for (state.cur = records; + state.cur != NULL; + state.cur = state.cur->next) { + + NTSTATUS status = dbwrap_do_locked( + db, state.cur->key, wipedbs_delete_fn, &state); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("dbwrap_do_locked failed for record <%s> " + "from %s\n", + state.cur->desc, + dbwrap_name(db)); + } + } + + if (verbose) { + d_printf("Deleted %zu of %zu records from %s\n", + state.num, + state.total, + dbwrap_name(db)); + } + + if (count) { + *count += state.total; + } + + return state.total - state.num; +} + +static int wipedbs_traverse_server_data(struct db_record *rec, + void *wipedbs_state) +{ + struct wipedbs_state *state = talloc_get_type_abort( + wipedbs_state, struct wipedbs_state); + bool dry_run = state->testmode; + TDB_DATA val = dbwrap_record_get_value(rec); + int ret; + struct wipedbs_server_data *sd = talloc_get_type_abort( + *(void**)val.dptr, struct wipedbs_server_data); + + if (state->verbose) { + d_printf("Server: '%s' %s\n", sd->server_id_str, + sd->exists ? + "exists" : + "does not exist, cleaning up..."); + } + + if (sd->exists) { + return 0; + } + + ret = wipedbs_delete_records(state->session_db, sd->session_records, + dry_run, state->verbose, + &state->stat.session.todelete); + state->stat.session.failure += ret; + + ret = wipedbs_delete_records(state->tcon_db, sd->tcon_records, + dry_run, state->verbose, + &state->stat.tcon.todelete); + state->stat.tcon.failure += ret; + + ret = wipedbs_delete_records(state->open_db, sd->open_records, + dry_run, state->verbose, + &state->stat.open.todelete); + state->stat.open.failure += ret; + + return 0; +} + +static int net_serverid_wipedbs(struct net_context *c, int argc, + const char **argv) +{ + int ret = -1; + NTSTATUS status; + struct wipedbs_state *state = talloc_zero(talloc_tos(), + struct wipedbs_state); + + if (c->display_usage) { + d_printf("%s\n%s", + _("Usage:"), + _("net serverid wipedbs [--test] [--verbose]\n")); + d_printf("%s\n%s", + _("Example:"), + _("net serverid wipedbs -v\n")); + return -1; + } + + state->now = timeval_current(); + state->testmode = c->opt_testmode; + state->verbose = c->opt_verbose; + + state->id2server_data = db_open_rbt(state); + if (state->id2server_data == NULL) { + DEBUG(0, ("Failed to open temporary database\n")); + goto done; + } + + status = smbXsrv_session_global_traverse(wipedbs_traverse_sessions, + state); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = smbXsrv_tcon_global_traverse(wipedbs_traverse_tcon, state); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = smbXsrv_open_global_traverse(wipedbs_traverse_open, state); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = wipedbs_check_server_exists(state); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = dbwrap_traverse_read(state->id2server_data, + wipedbs_traverse_server_data, + state, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to traverse db: %s\n", nt_errstr(status))); + goto done; + } + + d_printf("Found %d serverids, %d alive and %d disconnected\n", + state->stat.server.total, + state->stat.server.existing, + state->stat.server.disconnected); + d_printf("Found %d sessions, %d alive and %d disconnected" + ", cleaned up %d of %d entries\n", + state->stat.session.total, + state->stat.session.total - state->stat.session.todelete, + state->stat.session.disconnected, + state->stat.session.todelete - state->stat.session.failure, + state->stat.session.todelete); + d_printf("Found %d tcons, %d alive and %d disconnected" + ", cleaned up %d of %d entries\n", + state->stat.tcon.total, + state->stat.tcon.total - state->stat.tcon.todelete, + state->stat.tcon.disconnected, + state->stat.tcon.todelete - state->stat.tcon.failure, + state->stat.tcon.todelete); + d_printf("Found %d opens, %d alive, %d disconnected and %d timed out" + ", cleaned up %d of %d entries\n", + state->stat.open.total, + state->stat.open.total - state->stat.open.todelete + - (state->stat.open.disconnected - state->stat.open_timed_out), + state->stat.open.disconnected, + state->stat.open_timed_out, + state->stat.open.todelete - state->stat.open.failure, + state->stat.open.todelete); + + ret = 0; +done: + talloc_free(state); + return ret; +} + +static int net_serverid_exists(struct net_context *c, int argc, + const char **argv) +{ + struct server_id pid; + bool ok; + + if ((argc != 1) || (c->display_usage)) { + d_printf("Usage:\n" + "net serverid exists <serverid>\n"); + return -1; + } + + pid = server_id_from_string(get_my_vnn(), argv[0]); + ok = serverid_exists(&pid); + + if (ok) { + d_printf("%s exists\n", argv[0]); + } else { + d_printf("%s does not exist\n", argv[0]); + } + + return 0; +} + +int net_serverid(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "wipedbs", + net_serverid_wipedbs, + NET_TRANSPORT_LOCAL, + N_("Clean dead entries from temporary databases"), + N_("net serverid wipedbs\n" + " Clean dead entries from temporary databases") + }, + { + "exists", + net_serverid_exists, + NET_TRANSPORT_LOCAL, + N_("Show existence of a serverid"), + N_("net serverid exists <id>") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net serverid", func); +} diff --git a/source3/utils/net_share.c b/source3/utils/net_share.c new file mode 100644 index 0000000..56bb2c9 --- /dev/null +++ b/source3/utils/net_share.c @@ -0,0 +1,75 @@ +/* + Samba Unix/Linux SMB client library + net share commands + Copyright (C) 2002 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_share_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "\nnet [<method>] share [misc. options] [targets] \n" + "\tenumerates all exported resources (network shares) " + "on target server\n\n" + "net [<method>] share ADD <name=serverpath> [misc. options] [targets]" + "\n\tadds a share from a server (makes the export active)\n\n" + "net [<method>] share DELETE <sharename> [misc. options] [targets]" + "\n\tdeletes a share from a server (makes the export inactive)\n\n" + "net [<method>] share ALLOWEDUSERS [misc. options] [<filename>|- " + " [targets]]" + "\n\tshows a list of shares (either from [targets] or by default all" + "\n\tshares) together with all users allowed to access them. This" + "\n\tneeds the output of 'net usersidlist' on stdin or in <filename>." + "\n\n" + "net [<method>] share MIGRATE FILES <sharename> [misc. options] [targets]" + "\n\tMigrates files from remote to local server\n\n" + "net [<method>] share MIGRATE SHARES <sharename> [misc. options] [targets]" + "\n\tMigrates shares from remote to local server\n\n" + "net [<method>] share MIGRATE SECURITY <sharename> [misc. options] [targets]" + "\n\tMigrates share-ACLs from remote to local server\n\n" + "net [<method>] share MIGRATE ALL <sharename> [misc. options] [targets]" + "\n\tMigrates shares (including directories, files) from remote\n" + "\tto local server\n\n" + )); + net_common_methods_usage(c, argc, argv); + net_common_flags_usage(c, argc, argv); + d_printf(_( + "\t-C or --comment=<comment>\tdescriptive comment (for add only)\n" + "\t-M or --maxusers=<num>\t\tmax users allowed for share\n" + "\t --acls\t\t\tcopies ACLs as well\n" + "\t --attrs\t\t\tcopies DOS Attributes as well\n" + "\t --timestamps\t\tpreserve timestamps while copying files\n" + "\t --destination\t\tmigration target server (default: localhost)\n" + "\t-e or --exclude\t\t\tlist of shares to be excluded from mirroring\n" + "\t-v or --verbose\t\t\tgive verbose output\n")); + return -1; +} + +int net_share(struct net_context *c, int argc, const char **argv) +{ + if (argc > 0 && strcasecmp_m(argv[0], "HELP") == 0) { + net_share_usage(c, argc, argv); + return 0; + } + + if (net_rpc_check(c, 0)) + return net_rpc_share(c, argc, argv); + return net_rap_share(c, argc, argv); +} + diff --git a/source3/utils/net_status.c b/source3/utils/net_status.c new file mode 100644 index 0000000..a22b45c --- /dev/null +++ b/source3/utils/net_status.c @@ -0,0 +1,246 @@ +/* + Samba Unix/Linux SMB client library + net status command -- possible replacement for smbstatus + Copyright (C) 2003 Volker Lendecke (vl@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "lib/util/server_id.h" +#include "utils/net.h" +#include "session.h" +#include "messages.h" +#include "conn_tdb.h" + +int net_status_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_(" net status sessions [parseable] " + "Show list of open sessions\n")); + d_printf(_(" net status shares [parseable] " + "Show list of open shares\n")); + return -1; +} + +static int show_session(const char *key, struct sessionid *session, + void *private_data) +{ + struct server_id_buf tmp; + bool *parseable = (bool *)private_data; + + if (!process_exists(session->pid)) { + return 0; + } + + if (*parseable) { + d_printf("%s\\%s\\%s\\%s\\%s\n", + server_id_str_buf(session->pid, &tmp), + uidtoname(session->uid), + gidtoname(session->gid), + session->remote_machine, session->hostname); + } else { + d_printf("%7s %-12s %-12s %-12s (%s)\n", + server_id_str_buf(session->pid, &tmp), + uidtoname(session->uid), + gidtoname(session->gid), + session->remote_machine, session->hostname); + } + + return 0; +} + +static int net_status_sessions(struct net_context *c, int argc, const char **argv) +{ + bool parseable; + + if (c->display_usage) { + d_printf( "%s\n" + "net status sessions [parseable]\n" + " %s\n", + _("Usage:"), + _("Display open user sessions.\n" + " If parseable is specified, output is machine-" + "readable.")); + return 0; + } + + if (argc == 0) { + parseable = false; + } else if ((argc == 1) && strequal(argv[0], "parseable")) { + parseable = true; + } else { + return net_status_usage(c, argc, argv); + } + + if (!parseable) { + d_printf(_("PID Username Group Machine" + " \n" + "-------------------------------------------" + "------------------------\n")); + } + + sessionid_traverse_read(show_session, &parseable); + return 0; +} + +static int show_share(const struct connections_data *crec, + void *state) +{ + struct server_id_buf tmp; + + if (crec->cnum == TID_FIELD_INVALID) + return 0; + + if (!process_exists(crec->pid)) { + return 0; + } + + d_printf("%-10.10s %s %-12s %s", + crec->servicename, server_id_str_buf(crec->pid, &tmp), + crec->machine, + time_to_asc(nt_time_to_unix(crec->start))); + + return 0; +} + +struct sessionids { + int num_entries; + struct sessionid *entries; +}; + +static int collect_pids(const char *key, struct sessionid *session, + void *private_data) +{ + struct sessionids *ids = (struct sessionids *)private_data; + + if (!process_exists(session->pid)) + return 0; + + ids->num_entries += 1; + ids->entries = SMB_REALLOC_ARRAY(ids->entries, struct sessionid, ids->num_entries); + if (!ids->entries) { + ids->num_entries = 0; + return 0; + } + ids->entries[ids->num_entries-1] = *session; + + return 0; +} + +static int show_share_parseable(const struct connections_data *crec, + void *state) +{ + struct sessionids *ids = (struct sessionids *)state; + struct server_id_buf tmp; + int i; + bool guest = true; + + if (crec->cnum == TID_FIELD_INVALID) + return 0; + + if (!process_exists(crec->pid)) { + return 0; + } + + for (i=0; i<ids->num_entries; i++) { + struct server_id id = ids->entries[i].pid; + if (server_id_equal(&id, &crec->pid)) { + guest = false; + break; + } + } + + d_printf("%s\\%s\\%s\\%s\\%s\\%s\\%s", + crec->servicename, server_id_str_buf(crec->pid, &tmp), + guest ? "" : uidtoname(ids->entries[i].uid), + guest ? "" : gidtoname(ids->entries[i].gid), + crec->machine, + guest ? "" : ids->entries[i].hostname, + time_to_asc(nt_time_to_unix(crec->start))); + + return 0; +} + +static int net_status_shares_parseable(struct net_context *c, int argc, const char **argv) +{ + struct sessionids ids; + + ids.num_entries = 0; + ids.entries = NULL; + + sessionid_traverse_read(collect_pids, &ids); + + connections_forall_read(show_share_parseable, &ids); + + SAFE_FREE(ids.entries); + + return 0; +} + +static int net_status_shares(struct net_context *c, int argc, const char **argv) +{ + if (c->display_usage) { + d_printf( "%s\n" + "net status shares [parseable]\n" + " %s\n", + _("Usage:"), + _("Display open user shares.\n" + " If parseable is specified, output is machine-" + "readable.")); + return 0; + } + + if (argc == 0) { + + d_printf(_("\nService pid machine " + "Connected at\n" + "-------------------------------------" + "------------------\n")); + + connections_forall_read(show_share, NULL); + + return 0; + } + + if ((argc != 1) || !strequal(argv[0], "parseable")) { + return net_status_usage(c, argc, argv); + } + + return net_status_shares_parseable(c, argc, argv); +} + +int net_status(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "sessions", + net_status_sessions, + NET_TRANSPORT_LOCAL, + N_("Show list of open sessions"), + N_("net status sessions [parseable]\n" + " If parseable is specified, output is presented " + "in a machine-parseable fashion.") + }, + { + "shares", + net_status_shares, + NET_TRANSPORT_LOCAL, + N_("Show list of open shares"), + N_("net status shares [parseable]\n" + " If parseable is specified, output is presented " + "in a machine-parseable fashion.") + }, + {NULL, NULL, 0, NULL, NULL} + }; + return net_run_function(c, argc, argv, "net status", func); +} diff --git a/source3/utils/net_tdb.c b/source3/utils/net_tdb.c new file mode 100644 index 0000000..29585eb --- /dev/null +++ b/source3/utils/net_tdb.c @@ -0,0 +1,105 @@ +/* + * Samba Unix/Linux client library + * net tdb commands to query tdb record information + * Copyright (C) 2016, 2017 Christof Schmitt <cs@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "utils/net.h" +#include "locking/share_mode_lock.h" +#include "locking/proto.h" +#include "librpc/gen_ndr/open_files.h" +#include "librpc/gen_ndr/ndr_open_files.h" + +static int net_tdb_locking(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *mem_ctx = talloc_stackframe(); + struct share_mode_lock *lock; + DATA_BLOB blob = { .data = NULL }; + struct file_id id = { .inode = 0 }; + int ret = -1; + bool ok; + + if (argc < 1) { + d_printf("Usage: net tdb locking <key> [ dump ]\n"); + goto out; + } + + ok = locking_init_readonly(); + if (!ok) { + d_printf("locking_init_readonly failed\n"); + goto out; + } + + blob = strhex_to_data_blob(mem_ctx, argv[0]); + if (blob.length != sizeof(struct file_id)) { + d_printf("Invalid length %zu of key, expected %zu\n", + blob.length, + sizeof(struct file_id)); + goto out; + } + + memcpy(&id, blob.data, blob.length); + + lock = fetch_share_mode_unlocked(mem_ctx, id); + if (lock == NULL) { + d_printf("Record with key %s not found.\n", argv[1]); + goto out; + } + + if (argc == 2 && strequal(argv[1], "dump")) { + char *dump = share_mode_data_dump(mem_ctx, lock); + d_printf("%s\n", dump); + TALLOC_FREE(dump); + } else { + NTSTATUS status; + size_t num_share_modes = 0; + + status = share_mode_count_entries(id, &num_share_modes); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, + "Could not count share entries: %s\n", + nt_errstr(status)); + goto out; + } + + d_printf("Share path: %s\n", + share_mode_servicepath(lock)); + d_printf("Name: %s\n", + share_mode_filename(mem_ctx, lock)); + d_printf("Number of share modes: %zu\n", num_share_modes); + } + + ret = 0; +out: + TALLOC_FREE(mem_ctx); + return ret; +} + +int net_tdb(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { "locking", + net_tdb_locking, + NET_TRANSPORT_LOCAL, + N_("Show information for a record in locking.tdb"), + N_("net tdb locking <key>") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net tdb", func); +} diff --git a/source3/utils/net_time.c b/source3/utils/net_time.c new file mode 100644 index 0000000..f58d62b --- /dev/null +++ b/source3/utils/net_time.c @@ -0,0 +1,262 @@ +/* + Samba Unix/Linux SMB client library + net time command + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ +#include "includes.h" +#include "utils/net.h" +#include "libsmb/nmblib.h" +#include "libsmb/namequery.h" +#include "libsmb/libsmb.h" +#include "../libcli/smb/smbXcli_base.h" + +/* + return the time on a server. This does not require any authentication +*/ +static time_t cli_servertime(const char *host, + const struct sockaddr_storage *dest_ss, + int *zone) +{ + time_t ret = 0; + struct cli_state *cli = NULL; + NTSTATUS status; + + status = cli_connect_nb(host, dest_ss, 0, 0x20, lp_netbios_name(), + SMB_SIGNING_DEFAULT, 0, &cli); + if (!NT_STATUS_IS_OK(status)) { + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { + fprintf(stderr, "Can't contact server %s. NetBIOS support disabled," + " Error %s\n", host, nt_errstr(status)); + } else { + fprintf(stderr, "Can't contact server %s. Error %s\n", + host, nt_errstr(status)); + } + goto done; + } + + status = smbXcli_negprot(cli->conn, + cli->timeout, + lp_client_min_protocol(), + lp_client_max_protocol(), + NULL, + NULL, + NULL); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, _("Protocol negotiation failed: %s\n"), + nt_errstr(status)); + goto done; + } + + ret = cli_state_server_time(cli); + if (zone) *zone = smb1cli_conn_server_time_zone(cli->conn); + +done: + if (cli) { + cli_shutdown(cli); + } + return ret; +} + +/* find the servers time on the opt_host host */ +static time_t nettime(struct net_context *c, int *zone) +{ + return cli_servertime(c->opt_host, + c->opt_have_ip? &c->opt_dest_ip : NULL, zone); +} + +/* return a time as a string ready to be passed to /bin/date */ +static const char *systime(time_t t) +{ + struct tm *tm; + + tm = localtime(&t); + if (!tm) { + return "unknown"; + } + + return talloc_asprintf(talloc_tos(), "%02d%02d%02d%02d%04d.%02d", + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_year + 1900, tm->tm_sec); +} + +int net_time_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( +"net time\n\tdisplays time on a server (-S server)\n\n" +"net time system\n\tdisplays time on a server (-S server) in a format ready for /bin/date\n\n" +"net time set\n\truns /bin/date with the time from the server (-S server)\n\n" +"net time zone\n\tdisplays the timezone in hours from GMT on the remote server (-S server)\n\n" +"\n")); + net_common_flags_usage(c, argc, argv); + return -1; +} + +/* try to set the system clock */ +static int net_time_set(struct net_context *c, int argc, const char **argv) +{ + struct timeval tv; + int result; + + if (c->display_usage || c->opt_host == NULL) { + d_printf( "%s\n" + "net time set\n" + " %s\n", + _("Usage:"), + _("Set local time to that of remote time " + "server (-S server) ")); + return 0; + } + + tv.tv_sec = nettime(c, NULL); + tv.tv_usec=0; + + if (tv.tv_sec == 0) return -1; + + result = settimeofday(&tv,NULL); + + if (result) + d_fprintf(stderr, _("setting system clock failed. Error was (%s)\n"), + strerror(errno)); + + return result; +} + +/* display the time on a remote box in a format ready for /bin/date */ +static int net_time_system(struct net_context *c, int argc, const char **argv) +{ + time_t t; + + if (c->display_usage || c->opt_host == NULL) { + d_printf( "%s\n" + "net time system\n" + " %s\n", + _("Usage:"), + _("Output remote time server (-S server) " + "time in a format ready for /bin/date")); + return 0; + } + + t = nettime(c, NULL); + if (t == 0) return -1; + + printf("%s\n", systime(t)); + + return 0; +} + +/* display the remote time server's offset to UTC */ +static int net_time_zone(struct net_context *c, int argc, const char **argv) +{ + int zone = 0; + int hours, mins; + char zsign; + time_t t; + + if (c->display_usage || c->opt_host == NULL) { + d_printf( "%s\n" + "net time zone\n" + " %s\n", + _("Usage:"), + _("Display the remote time server's (-S server) " + "offset to UTC")); + return 0; + } + + t = nettime(c, &zone); + + if (t == 0) return -1; + + zsign = (zone > 0) ? '-' : '+'; + if (zone < 0) zone = -zone; + + zone /= 60; + hours = zone / 60; + mins = zone % 60; + + printf("%c%02d%02d\n", zsign, hours, mins); + + return 0; +} + +/* display or set the time on a host */ +int net_time(struct net_context *c, int argc, const char **argv) +{ + time_t t; + struct functable func[] = { + { + "system", + net_time_system, + NET_TRANSPORT_LOCAL, + N_("Display time ready for /bin/date"), + N_("net time system\n" + " Display time ready for /bin/date") + }, + { + "set", + net_time_set, + NET_TRANSPORT_LOCAL, + N_("Set the system time from time server"), + N_("net time set\n" + " Set the system time from time server") + }, + { + "zone", + net_time_zone, + NET_TRANSPORT_LOCAL, + N_("Display timezone offset from UTC"), + N_("net time zone\n" + " Display timezone offset from UTC") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (argc != 0) { + return net_run_function(c, argc, argv, "net time", func); + } + + if (c->display_usage) { + d_printf( "%s\n" + "net time\n" + " %s\n", + _("Usage:"), + _("Display the remote time server's time")); + net_display_usage_from_functable(func); + return 0; + } + + if (c->opt_host == NULL && !c->opt_have_ip) { + bool ok; + + ok = find_master_ip(c->opt_target_workgroup, &c->opt_dest_ip); + if (!ok) { + d_fprintf(stderr, + _("Could not locate a time server. " + "Try specifying a target host.\n")); + net_time_usage(c, argc, argv); + return -1; + } + c->opt_have_ip = true; + } + + /* default - print the time */ + t = cli_servertime(c->opt_host, + c->opt_have_ip? &c->opt_dest_ip : NULL, + NULL); + if (t == 0) return -1; + + d_printf("%s", ctime(&t)); + return 0; +} diff --git a/source3/utils/net_user.c b/source3/utils/net_user.c new file mode 100644 index 0000000..9fb6f80 --- /dev/null +++ b/source3/utils/net_user.c @@ -0,0 +1,67 @@ +/* + Samba Unix/Linux SMB client library + net user commands + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2002 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2008 Kai Blin (kai@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" + +int net_user_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("\nnet [<method>] user [misc. options] [targets]" + "\n\tList users\n\n")); + d_printf(_("net [<method>] user DELETE <name> [misc. options] [targets]" + "\n\tDelete specified user\n")); + d_printf(_("\nnet [<method>] user INFO <name> [misc. options] [targets]" + "\n\tList the domain groups of the specified user\n")); + d_printf(_("\nnet [<method>] user ADD <name> [password] [-c container] " + "[-F user flags] [misc. options]" + " [targets]\n\tAdd specified user\n")); + d_printf(_("\nnet [<method>] user RENAME <oldusername> <newusername>" + " [targets]\n\tRename specified user\n\n")); + + net_common_methods_usage(c, argc, argv); + net_common_flags_usage(c, argc, argv); + d_printf(_("\t-C or --comment=<comment>\tdescriptive comment " + "(for add only)\n")); + d_printf(_("\t-c or --container=<container>\tLDAP container, defaults " + "to cn=Users (for add in ADS only)\n")); + return -1; +} + +int net_user(struct net_context *c, int argc, const char **argv) +{ + if (argc < 1) + return net_user_usage(c, argc, argv); + + if (strcasecmp_m(argv[0], "HELP") == 0) { + net_user_usage(c, argc, argv); + return 0; + } + + if (net_ads_check(c) == 0) + return net_ads_user(c, argc, argv); + + /* if server is not specified, default to PDC? */ + if (net_rpc_check(c, NET_FLAGS_PDC)) + return net_rpc_user(c, argc, argv); + + return net_rap_user(c, argc, argv); +} + diff --git a/source3/utils/net_usershare.c b/source3/utils/net_usershare.c new file mode 100644 index 0000000..e5826ee --- /dev/null +++ b/source3/utils/net_usershare.c @@ -0,0 +1,1172 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + + Copyright (C) Jeremy Allison (jra@samba.org) 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/passwd.h" +#include "system/filesys.h" +#include "utils/net.h" +#include "../libcli/security/security.h" +#include "lib/util/string_wrappers.h" + +struct { + const char *us_errstr; + enum usershare_err us_err; +} us_errs [] = { + {"",USERSHARE_OK}, + {N_("Malformed usershare file"), USERSHARE_MALFORMED_FILE}, + {N_("Bad version number"), USERSHARE_BAD_VERSION}, + {N_("Malformed path entry"), USERSHARE_MALFORMED_PATH}, + {N_("Malformed comment entryfile"), USERSHARE_MALFORMED_COMMENT_DEF}, + {N_("Malformed acl definition"), USERSHARE_MALFORMED_ACL_DEF}, + {N_("Acl parse error"), USERSHARE_ACL_ERR}, + {N_("Path not absolute"), USERSHARE_PATH_NOT_ABSOLUTE}, + {N_("Path is denied"), USERSHARE_PATH_IS_DENIED}, + {N_("Path not allowed"), USERSHARE_PATH_NOT_ALLOWED}, + {N_("Path is not a directory"), USERSHARE_PATH_NOT_DIRECTORY}, + {N_("System error"), USERSHARE_POSIX_ERR}, + {N_("Malformed sharename definition"), USERSHARE_MALFORMED_SHARENAME_DEF}, + {N_("Bad sharename (doesn't match filename)"), USERSHARE_BAD_SHARENAME}, + {NULL,(enum usershare_err)-1} +}; + +static const char *get_us_error_code(enum usershare_err us_err) +{ + char *result; + int idx = 0; + + while (us_errs[idx].us_errstr != NULL) { + if (us_errs[idx].us_err == us_err) { + return us_errs[idx].us_errstr; + } + idx++; + } + + result = talloc_asprintf(talloc_tos(), _("Usershare error code (0x%x)"), + (unsigned int)us_err); + SMB_ASSERT(result != NULL); + return result; +} + +/* The help subsystem for the USERSHARE subcommand */ + +static int net_usershare_add_usage(struct net_context *c, int argc, const char **argv) +{ + char chr = *lp_winbind_separator(); + d_printf(_( + "net usershare add [--long] <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>]\n" + "\tAdds the specified share name for this user.\n" + "\t<sharename> is the new share name.\n" + "\t<path> is the path on the filesystem to export.\n" + "\t<comment> is the optional comment for the new share.\n" + "\t<acl> is an optional share acl in the format \"DOMAIN%cname:X,DOMAIN%cname:X,....\"\n" + "\t<guest_ok=y> if present sets \"guest ok = yes\" on this usershare.\n" + "\t\t\"X\" represents a permission and can be any one of the characters f, r or d\n" + "\t\twhere \"f\" means full control, \"r\" means read-only, \"d\" means deny access.\n" + "\t\tname may be a domain user or group. For local users use the local server name " + "instead of \"DOMAIN\"\n" + "\t\tThe default acl is \"Everyone:r\" which allows everyone read-only access.\n" + "\tAdd --long to print the info on the newly added share.\n"), + chr, chr ); + return -1; +} + +static int net_usershare_delete_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net usershare delete <sharename>\n" + "\tdeletes the specified share name for this user.\n")); + return -1; +} + +static int net_usershare_info_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net usershare info [--long] [wildcard sharename]\n" + "\tPrints out the path, comment and acl elements of shares that match the wildcard.\n" + "\tBy default only gives info on shares owned by the current user\n" + "\tAdd --long to apply this to all shares\n" + "\tOmit the sharename or use a wildcard of '*' to see all shares\n")); + return -1; +} + +static int net_usershare_list_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_( + "net usershare list [--long] [wildcard sharename]\n" + "\tLists the names of all shares that match the wildcard.\n" + "\tBy default only lists shares owned by the current user\n" + "\tAdd --long to apply this to all shares\n" + "\tOmit the sharename or use a wildcard of '*' to see all shares\n")); + return -1; +} + +int net_usershare_usage(struct net_context *c, int argc, const char **argv) +{ + d_printf(_("net usershare add <sharename> <path> [<comment>] [<acl>] [<guest_ok=[y|n]>] to " + "add or change a user defined share.\n" + "net usershare delete <sharename> to delete a user defined share.\n" + "net usershare info [--long] [wildcard sharename] to print info about a user defined share.\n" + "net usershare list [--long] [wildcard sharename] to list user defined shares.\n" + "net usershare help\n" + "\nType \"net usershare help <option>\" to get more information on that option\n\n")); + + net_common_flags_usage(c, argc, argv); + return -1; +} + +/*************************************************************************** +***************************************************************************/ + +static char *get_basepath(TALLOC_CTX *ctx) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *basepath = lp_usershare_path(ctx, lp_sub); + + if (!basepath) { + return NULL; + } + if ((basepath[0] != '\0') && (basepath[strlen(basepath)-1] == '/')) { + basepath[strlen(basepath)-1] = '\0'; + } + return basepath; +} + +/*************************************************************************** + Delete a single userlevel share. +***************************************************************************/ + +static int net_usershare_delete(struct net_context *c, int argc, const char **argv) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + char *us_path; + char *sharename; + + if (argc != 1 || c->display_usage) { + return net_usershare_delete_usage(c, argc, argv); + } + + if ((sharename = strlower_talloc(talloc_tos(), argv[0])) == NULL) { + d_fprintf(stderr, _("strlower_talloc failed\n")); + return -1; + } + + if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) { + d_fprintf(stderr, _("net usershare delete: share name %s contains " + "invalid characters (any of %s)\n"), + sharename, INVALID_SHARENAME_CHARS); + TALLOC_FREE(sharename); + return -1; + } + + us_path = talloc_asprintf(talloc_tos(), + "%s/%s", + lp_usershare_path(talloc_tos(), lp_sub), + sharename); + if (!us_path) { + TALLOC_FREE(sharename); + return -1; + } + + if (unlink(us_path) != 0) { + d_fprintf(stderr, _("net usershare delete: unable to remove usershare %s. " + "Error was %s\n"), + us_path, strerror(errno)); + TALLOC_FREE(sharename); + return -1; + } + TALLOC_FREE(sharename); + return 0; +} + +/*************************************************************************** + Data structures to handle a list of usershare files. +***************************************************************************/ + +struct file_list { + struct file_list *next, *prev; + const char *pathname; +}; + +static struct file_list *flist; + +/*************************************************************************** +***************************************************************************/ + +static int get_share_list(TALLOC_CTX *ctx, const char *wcard, bool only_ours) +{ + DIR *dp; + struct dirent *de; + uid_t myuid = geteuid(); + struct file_list *fl = NULL; + char *basepath = get_basepath(ctx); + + if (!basepath) { + return -1; + } + dp = opendir(basepath); + if (!dp) { + d_fprintf(stderr, + _("get_share_list: cannot open usershare directory %s. " + "Error %s\n"), + basepath, strerror(errno) ); + return -1; + } + + while((de = readdir(dp)) != 0) { + SMB_STRUCT_STAT sbuf; + char *path; + const char *n = de->d_name; + + /* Ignore . and .. */ + if (*n == '.') { + if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) { + continue; + } + } + + if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) { + d_fprintf(stderr, + _("get_share_list: ignoring bad share " + "name %s\n"), n); + continue; + } + path = talloc_asprintf(ctx, + "%s/%s", + basepath, + n); + if (!path) { + closedir(dp); + return -1; + } + + if (sys_lstat(path, &sbuf, false) != 0) { + d_fprintf(stderr, + _("get_share_list: can't lstat file %s. Error " + "was %s\n"), + path, strerror(errno) ); + continue; + } + + if (!S_ISREG(sbuf.st_ex_mode)) { + d_fprintf(stderr, + _("get_share_list: file %s is not a regular " + "file. Ignoring.\n"), + path ); + continue; + } + + if (only_ours && sbuf.st_ex_uid != myuid) { + continue; + } + + if (!unix_wild_match(wcard, n)) { + continue; + } + + /* (Finally) - add to list. */ + fl = talloc(ctx, struct file_list); + if (!fl) { + closedir(dp); + return -1; + } + fl->pathname = talloc_strdup(ctx, n); + if (!fl->pathname) { + closedir(dp); + return -1; + } + + DLIST_ADD(flist, fl); + } + + closedir(dp); + return 0; +} + +enum us_priv_op { US_LIST_OP, US_INFO_OP}; + +struct us_priv_info { + TALLOC_CTX *ctx; + enum us_priv_op op; + struct net_context *c; +}; + +/*************************************************************************** + Call a function for every share on the list. +***************************************************************************/ + +static int process_share_list(int (*fn)(struct file_list *, void *), void *priv) +{ + struct file_list *fl; + int ret = 0; + + for (fl = flist; fl; fl = fl->next) { + ret = (*fn)(fl, priv); + } + + return ret; +} + +/*************************************************************************** + Info function. +***************************************************************************/ + +static int info_fn(struct file_list *fl, void *priv) +{ + SMB_STRUCT_STAT sbuf; + char **lines = NULL; + struct us_priv_info *pi = (struct us_priv_info *)priv; + TALLOC_CTX *ctx = pi->ctx; + struct net_context *c = pi->c; + int fd = -1; + int numlines = 0; + struct security_descriptor *psd = NULL; + char *basepath; + char *sharepath = NULL; + char *comment = NULL; + char *cp_sharename = NULL; + char *acl_str; + int num_aces; + char sep_str[2]; + enum usershare_err us_err; + bool guest_ok = false; + + sep_str[0] = *lp_winbind_separator(); + sep_str[1] = '\0'; + + basepath = get_basepath(ctx); + if (!basepath) { + return -1; + } + basepath = talloc_asprintf_append(basepath, + "/%s", + fl->pathname); + if (!basepath) { + return -1; + } + +#ifdef O_NOFOLLOW + fd = open(basepath, O_RDONLY|O_NOFOLLOW, 0); +#else + fd = open(basepath, O_RDONLY, 0); +#endif + + if (fd == -1) { + d_fprintf(stderr, _("info_fn: unable to open %s. %s\n"), + basepath, strerror(errno) ); + return -1; + } + + /* Paranoia... */ + if (sys_fstat(fd, &sbuf, false) != 0) { + d_fprintf(stderr, + _("info_fn: can't fstat file %s. Error was %s\n"), + basepath, strerror(errno) ); + close(fd); + return -1; + } + + if (!S_ISREG(sbuf.st_ex_mode)) { + d_fprintf(stderr, + _("info_fn: file %s is not a regular file. Ignoring.\n"), + basepath ); + close(fd); + return -1; + } + + lines = fd_lines_load(fd, &numlines, 10240, NULL); + close(fd); + + if (lines == NULL) { + return -1; + } + + /* Ensure it's well formed. */ + us_err = parse_usershare_file(ctx, &sbuf, fl->pathname, -1, lines, numlines, + &sharepath, + &comment, + &cp_sharename, + &psd, + &guest_ok); + + TALLOC_FREE(lines); + + if (us_err != USERSHARE_OK) { + d_fprintf(stderr, + _("info_fn: file %s is not a well formed usershare " + "file.\n"), + basepath ); + d_fprintf(stderr, _("info_fn: Error was %s.\n"), + get_us_error_code(us_err) ); + return -1; + } + + acl_str = talloc_strdup(ctx, "usershare_acl="); + if (!acl_str) { + return -1; + } + + for (num_aces = 0; num_aces < psd->dacl->num_aces; num_aces++) { + const char *domain; + const char *name; + NTSTATUS ntstatus; + + ntstatus = net_lookup_name_from_sid(c, ctx, + &psd->dacl->aces[num_aces].trustee, + &domain, &name); + + if (NT_STATUS_IS_OK(ntstatus)) { + if (domain && *domain) { + acl_str = talloc_asprintf_append(acl_str, + "%s%s", + domain, + sep_str); + if (!acl_str) { + return -1; + } + } + acl_str = talloc_asprintf_append(acl_str, + "%s", + name); + if (!acl_str) { + return -1; + } + + } else { + struct dom_sid_buf sidstr; + + acl_str = talloc_asprintf_append( + acl_str, + "%s", + dom_sid_str_buf( + &psd->dacl->aces[num_aces].trustee, + &sidstr)); + if (!acl_str) { + return -1; + } + } + acl_str = talloc_asprintf_append(acl_str, ":"); + if (!acl_str) { + return -1; + } + + if (psd->dacl->aces[num_aces].type == SEC_ACE_TYPE_ACCESS_DENIED) { + acl_str = talloc_asprintf_append(acl_str, "D,"); + if (!acl_str) { + return -1; + } + } else { + if (psd->dacl->aces[num_aces].access_mask & GENERIC_ALL_ACCESS) { + acl_str = talloc_asprintf_append(acl_str, "F,"); + } else { + acl_str = talloc_asprintf_append(acl_str, "R,"); + } + if (!acl_str) { + return -1; + } + } + } + + /* NOTE: This is smb.conf-like output. Do not translate. */ + if (pi->op == US_INFO_OP) { + d_printf("[%s]\n", cp_sharename ); + d_printf("path=%s\n", sharepath ); + d_printf("comment=%s\n", comment); + d_printf("%s\n", acl_str); + d_printf("guest_ok=%c\n\n", guest_ok ? 'y' : 'n'); + } else if (pi->op == US_LIST_OP) { + d_printf("%s\n", cp_sharename); + } + + return 0; +} + +/*************************************************************************** + Print out info (internal detail) on userlevel shares. +***************************************************************************/ + +static int net_usershare_info(struct net_context *c, int argc, const char **argv) +{ + fstring wcard; + bool only_ours = true; + int ret = -1; + struct us_priv_info pi; + TALLOC_CTX *ctx; + + fstrcpy(wcard, "*"); + + if (c->display_usage) + return net_usershare_info_usage(c, argc, argv); + + if (c->opt_long_list_entries) { + only_ours = false; + } + + switch (argc) { + case 0: + break; + case 1: + fstrcpy(wcard, argv[0]); + break; + default: + return net_usershare_info_usage(c, argc, argv); + } + + if (!strlower_m(wcard)) { + return -1; + } + + ctx = talloc_init("share_info"); + ret = get_share_list(ctx, wcard, only_ours); + if (ret) { + return ret; + } + + pi.ctx = ctx; + pi.op = US_INFO_OP; + pi.c = c; + + ret = process_share_list(info_fn, &pi); + talloc_destroy(ctx); + return ret; +} + +/*************************************************************************** + Count the current total number of usershares. +***************************************************************************/ + +static int count_num_usershares(void) +{ + DIR *dp; + struct dirent *de; + int num_usershares = 0; + TALLOC_CTX *ctx = talloc_tos(); + char *basepath = get_basepath(ctx); + + if (!basepath) { + return -1; + } + + dp = opendir(basepath); + if (!dp) { + d_fprintf(stderr, + _("count_num_usershares: cannot open usershare " + "directory %s. Error %s\n"), + basepath, strerror(errno) ); + return -1; + } + + while((de = readdir(dp)) != 0) { + SMB_STRUCT_STAT sbuf; + char *path; + const char *n = de->d_name; + + /* Ignore . and .. */ + if (*n == '.') { + if ((n[1] == '\0') || (n[1] == '.' && n[2] == '\0')) { + continue; + } + } + + if (!validate_net_name(n, INVALID_SHARENAME_CHARS, strlen(n))) { + d_fprintf(stderr, + _("count_num_usershares: ignoring bad share " + "name %s\n"), n); + continue; + } + path = talloc_asprintf(ctx, + "%s/%s", + basepath, + n); + if (!path) { + closedir(dp); + return -1; + } + + if (sys_lstat(path, &sbuf, false) != 0) { + d_fprintf(stderr, + _("count_num_usershares: can't lstat file %s. " + "Error was %s\n"), + path, strerror(errno) ); + continue; + } + + if (!S_ISREG(sbuf.st_ex_mode)) { + d_fprintf(stderr, + _("count_num_usershares: file %s is not a " + "regular file. Ignoring.\n"), + path ); + continue; + } + num_usershares++; + } + + closedir(dp); + return num_usershares; +} + +/*************************************************************************** + Add a single userlevel share. +***************************************************************************/ + +static int net_usershare_add(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *ctx = talloc_stackframe(); + SMB_STRUCT_STAT sbuf; + SMB_STRUCT_STAT lsbuf; + char *sharename; + const char *cp_sharename; + char *full_path; + char *full_path_tmp; + const char *us_path; + const char *us_comment; + const char *arg_acl; + char *us_acl; + char *file_img; + int num_aces = 0; + int i; + int tmpfd; + const char *pacl; + size_t to_write; + uid_t myeuid = geteuid(); + bool guest_ok = false; + int num_usershares; + mode_t mask; + + us_comment = ""; + arg_acl = "S-1-1-0:R"; + + if (c->display_usage) { + TALLOC_FREE(ctx); + return net_usershare_add_usage(c, argc, argv); + } + + switch (argc) { + case 0: + case 1: + default: + TALLOC_FREE(ctx); + return net_usershare_add_usage(c, argc, argv); + case 2: + cp_sharename = argv[0]; + sharename = strlower_talloc(ctx, argv[0]); + us_path = argv[1]; + break; + case 3: + cp_sharename = argv[0]; + sharename = strlower_talloc(ctx, argv[0]); + us_path = argv[1]; + us_comment = argv[2]; + break; + case 4: + cp_sharename = argv[0]; + sharename = strlower_talloc(ctx, argv[0]); + us_path = argv[1]; + us_comment = argv[2]; + arg_acl = argv[3]; + break; + case 5: + cp_sharename = argv[0]; + sharename = strlower_talloc(ctx, argv[0]); + us_path = argv[1]; + us_comment = argv[2]; + arg_acl = argv[3]; + if (strlen(arg_acl) == 0) { + arg_acl = "S-1-1-0:R"; + } + if (!strnequal(argv[4], "guest_ok=", 9)) { + TALLOC_FREE(ctx); + return net_usershare_add_usage(c, argc, argv); + } + switch (argv[4][9]) { + case 'y': + case 'Y': + guest_ok = true; + break; + case 'n': + case 'N': + guest_ok = false; + break; + default: + TALLOC_FREE(ctx); + return net_usershare_add_usage(c, argc, argv); + } + break; + } + + /* Ensure we're under the "usershare max shares" number. Advisory only. */ + num_usershares = count_num_usershares(); + if (num_usershares >= lp_usershare_max_shares()) { + d_fprintf(stderr, + _("net usershare add: maximum number of allowed " + "usershares (%d) reached\n"), + lp_usershare_max_shares() ); + TALLOC_FREE(ctx); + return -1; + } + + if (!validate_net_name(sharename, INVALID_SHARENAME_CHARS, strlen(sharename))) { + d_fprintf(stderr, _("net usershare add: share name %s contains " + "invalid characters (any of %s)\n"), + sharename, INVALID_SHARENAME_CHARS); + TALLOC_FREE(ctx); + return -1; + } + + /* Disallow shares the same as users. */ + if (getpwnam(sharename)) { + d_fprintf(stderr, + _("net usershare add: share name %s is already a valid " + "system user name\n"), + sharename ); + TALLOC_FREE(ctx); + return -1; + } + + /* Construct the full path for the usershare file. */ + full_path = get_basepath(ctx); + if (!full_path) { + TALLOC_FREE(ctx); + return -1; + } + full_path_tmp = talloc_asprintf(ctx, + "%s/:tmpXXXXXX", + full_path); + if (!full_path_tmp) { + TALLOC_FREE(ctx); + return -1; + } + + full_path = talloc_asprintf_append(full_path, + "/%s", + sharename); + if (!full_path) { + TALLOC_FREE(ctx); + return -1; + } + + /* The path *must* be absolute. */ + if (us_path[0] != '/') { + d_fprintf(stderr, + _("net usershare add: path %s is not an absolute " + "path.\n"), + us_path); + TALLOC_FREE(ctx); + return -1; + } + + /* Check the directory to be shared exists. */ + if (sys_stat(us_path, &sbuf, false) != 0) { + d_fprintf(stderr, + _("net usershare add: cannot stat path %s to ensure " + "this is a directory. Error was %s\n"), + us_path, strerror(errno) ); + TALLOC_FREE(ctx); + return -1; + } + + if (!S_ISDIR(sbuf.st_ex_mode)) { + d_fprintf(stderr, + _("net usershare add: path %s is not a directory.\n"), + us_path ); + TALLOC_FREE(ctx); + return -1; + } + + /* If we're not root, check if we're restricted to sharing out directories + that we own only. */ + + if ((myeuid != 0) && lp_usershare_owner_only() && (myeuid != sbuf.st_ex_uid)) { + d_fprintf(stderr, _("net usershare add: cannot share path %s as " + "we are restricted to only sharing directories we own.\n" + "\tAsk the administrator to add the line \"usershare owner only = false\" \n" + "\tto the [global] section of the smb.conf to allow this.\n"), + us_path ); + TALLOC_FREE(ctx); + return -1; + } + + /* No validation needed on comment. Now go through and validate the + acl string. Convert names to SID's as needed. Then run it through + parse_usershare_acl to ensure it's valid. */ + + /* Start off the string we'll append to. */ + us_acl = talloc_strdup(ctx, ""); + if (!us_acl) { + TALLOC_FREE(ctx); + return -1; + } + + pacl = arg_acl; + num_aces = 1; + + /* Add the number of ',' characters to get the number of aces. */ + num_aces += count_chars(pacl,','); + + for (i = 0; i < num_aces; i++) { + struct dom_sid sid; + struct dom_sid_buf buf; + const char *pcolon = strchr_m(pacl, ':'); + const char *name; + + if (pcolon == NULL) { + d_fprintf(stderr, + _("net usershare add: malformed acl %s " + "(missing ':').\n"), + pacl ); + TALLOC_FREE(ctx); + return -1; + } + + switch(pcolon[1]) { + case 'f': + case 'F': + case 'd': + case 'r': + case 'R': + break; + default: + d_fprintf(stderr, + _("net usershare add: malformed acl %s " + "(access control must be 'r', 'f', " + "or 'd')\n"), + pacl ); + TALLOC_FREE(ctx); + return -1; + } + + if (pcolon[2] != ',' && pcolon[2] != '\0') { + d_fprintf(stderr, + _("net usershare add: malformed terminating " + "character for acl %s\n"), + pacl ); + TALLOC_FREE(ctx); + return -1; + } + + /* Get the name */ + if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) { + d_fprintf(stderr, _("talloc_strndup failed\n")); + TALLOC_FREE(ctx); + return -1; + } + if (!string_to_sid(&sid, name)) { + /* Convert to a SID */ + NTSTATUS ntstatus = net_lookup_sid_from_name(c, ctx, name, &sid); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_fprintf(stderr, + _("net usershare add: cannot convert " + "name \"%s\" to a SID. %s."), + name, get_friendly_nt_error_msg(ntstatus) ); + if (NT_STATUS_EQUAL(ntstatus, NT_STATUS_CONNECTION_REFUSED)) { + d_fprintf(stderr, + _(" Maybe smbd is not running.\n")); + } else { + d_fprintf(stderr, "\n"); + } + TALLOC_FREE(ctx); + return -1; + } + } + us_acl = talloc_asprintf_append( + us_acl, + "%s:%c,", + dom_sid_str_buf(&sid, &buf), + pcolon[1]); + if (us_acl == NULL) { + d_fprintf(stderr, + _("net usershare add: talloc_asprintf_append() failed\n")); + TALLOC_FREE(ctx); + return -1; + } + + /* Move to the next ACL entry. */ + if (pcolon[2] == ',') { + pacl = &pcolon[3]; + } + } + + /* Remove the last ',' */ + us_acl[strlen(us_acl)-1] = '\0'; + + if (guest_ok && !lp_usershare_allow_guests()) { + d_fprintf(stderr, _("net usershare add: guest_ok=y requested " + "but the \"usershare allow guests\" parameter is not " + "enabled by this server.\n")); + TALLOC_FREE(ctx); + return -1; + } + + /* Create a temporary filename for this share. */ + mask = umask(S_IRWXO | S_IRWXG); + tmpfd = mkstemp(full_path_tmp); + umask(mask); + + if (tmpfd == -1) { + d_fprintf(stderr, + _("net usershare add: cannot create tmp file %s\n"), + full_path_tmp ); + TALLOC_FREE(ctx); + return -1; + } + + /* Ensure we opened the file we thought we did. */ + if (sys_lstat(full_path_tmp, &lsbuf, false) != 0) { + d_fprintf(stderr, + _("net usershare add: cannot lstat tmp file %s\n"), + full_path_tmp ); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + /* Check this is the same as the file we opened. */ + if (sys_fstat(tmpfd, &sbuf, false) != 0) { + d_fprintf(stderr, + _("net usershare add: cannot fstat tmp file %s\n"), + full_path_tmp ); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + if (!S_ISREG(sbuf.st_ex_mode) || sbuf.st_ex_dev != lsbuf.st_ex_dev || sbuf.st_ex_ino != lsbuf.st_ex_ino) { + d_fprintf(stderr, + _("net usershare add: tmp file %s is not a regular " + "file ?\n"), + full_path_tmp ); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + if (fchmod(tmpfd, 0644) == -1) { + d_fprintf(stderr, + _("net usershare add: failed to fchmod tmp file %s " + "to 0644\n"), + full_path_tmp ); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + /* Create the in-memory image of the file. */ + file_img = talloc_strdup(ctx, "#VERSION 2\npath="); + if (file_img == NULL) { + d_fprintf(stderr, + _("net usershare add: talloc_strdup() failed\n")); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + file_img = talloc_asprintf_append(file_img, + "%s\ncomment=%s\nusershare_acl=%s\n" + "guest_ok=%c\nsharename=%s\n", + us_path, + us_comment, + us_acl, + guest_ok ? 'y' : 'n', + cp_sharename); + if (file_img == NULL) { + d_fprintf(stderr, + _("net usershare add: talloc_asprintf_append() failed\n")); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + to_write = strlen(file_img); + + if (write(tmpfd, file_img, to_write) != to_write) { + d_fprintf(stderr, + _("net usershare add: failed to write %u bytes to " + "file %s. Error was %s\n"), + (unsigned int)to_write, full_path_tmp, strerror(errno)); + unlink(full_path_tmp); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + /* Attempt to replace any existing share by this name. */ + if (rename(full_path_tmp, full_path) != 0) { + unlink(full_path_tmp); + d_fprintf(stderr, + _("net usershare add: failed to add share %s. Error " + "was %s\n"), + sharename, strerror(errno)); + TALLOC_FREE(ctx); + close(tmpfd); + return -1; + } + + close(tmpfd); + + if (c->opt_long_list_entries) { + const char *my_argv[2]; + my_argv[0] = sharename; + my_argv[1] = NULL; + net_usershare_info(c, 1, my_argv); + } + + TALLOC_FREE(ctx); + return 0; +} + +#if 0 +/*************************************************************************** + List function. +***************************************************************************/ + +static int list_fn(struct file_list *fl, void *priv) +{ + d_printf("%s\n", fl->pathname); + return 0; +} +#endif + +/*************************************************************************** + List userlevel shares. +***************************************************************************/ + +static int net_usershare_list(struct net_context *c, int argc, + const char **argv) +{ + fstring wcard; + bool only_ours = true; + int ret = -1; + struct us_priv_info pi; + TALLOC_CTX *ctx; + + fstrcpy(wcard, "*"); + + if (c->display_usage) + return net_usershare_list_usage(c, argc, argv); + + if (c->opt_long_list_entries) { + only_ours = false; + } + + switch (argc) { + case 0: + break; + case 1: + fstrcpy(wcard, argv[0]); + break; + default: + return net_usershare_list_usage(c, argc, argv); + } + + if (!strlower_m(wcard)) { + return -1; + } + + ctx = talloc_init("share_list"); + ret = get_share_list(ctx, wcard, only_ours); + if (ret) { + return ret; + } + + pi.ctx = ctx; + pi.op = US_LIST_OP; + pi.c = c; + + ret = process_share_list(info_fn, &pi); + talloc_destroy(ctx); + return ret; +} + +/*************************************************************************** + Entry-point for all the USERSHARE functions. +***************************************************************************/ + +int net_usershare(struct net_context *c, int argc, const char **argv) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + DIR *dp; + + struct functable func[] = { + { + "add", + net_usershare_add, + NET_TRANSPORT_LOCAL, + N_("Add/modify user defined share"), + N_("net usershare add\n" + " Add/modify user defined share") + }, + { + "delete", + net_usershare_delete, + NET_TRANSPORT_LOCAL, + N_("Delete user defined share"), + N_("net usershare delete\n" + " Delete user defined share") + }, + { + "info", + net_usershare_info, + NET_TRANSPORT_LOCAL, + N_("Display information about a user defined share"), + N_("net usershare info\n" + " Display information about a user defined share") + }, + { + "list", + net_usershare_list, + NET_TRANSPORT_LOCAL, + N_("List user defined shares"), + N_("net usershare list\n" + " List user defined shares") + }, + {NULL, NULL, 0, NULL, NULL} + }; + + if (lp_usershare_max_shares() == 0) { + d_fprintf(stderr, + _("net usershare: usershares are currently " + "disabled\n")); + return -1; + } + + dp = opendir(lp_usershare_path(talloc_tos(), lp_sub)); + if (!dp) { + int err = errno; + d_fprintf(stderr, + _("net usershare: cannot open usershare directory %s. " + "Error %s\n"), + lp_usershare_path(talloc_tos(), lp_sub), strerror(err) ); + if (err == EACCES) { + d_fprintf(stderr, + _("You do not have permission to create a " + "usershare. Ask your administrator to grant " + "you permissions to create a share.\n")); + } else if (err == ENOENT) { + d_fprintf(stderr, + _("Please ask your system administrator to " + "enable user sharing.\n")); + } + return -1; + } + closedir(dp); + + return net_run_function(c, argc, argv, "net usershare", func); +} diff --git a/source3/utils/net_util.c b/source3/utils/net_util.c new file mode 100644 index 0000000..f3b7755 --- /dev/null +++ b/source3/utils/net_util.c @@ -0,0 +1,614 @@ +/* + * Unix SMB/CIFS implementation. + * Helper routines for net + * Copyright (C) Volker Lendecke 2006 + * Copyright (C) Kai Blin 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + + +#include "includes.h" +#include "utils/net.h" +#include "libsmb/namequery.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "rpc_client/cli_lsarpc.h" +#include "../librpc/gen_ndr/ndr_dssetup_c.h" +#include "secrets.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "lib/param/param.h" +#include "auth/gensec/gensec.h" +#include "libcli/auth/netlogon_creds_cli.h" +#include "lib/cmdline/cmdline.h" + +NTSTATUS net_rpc_lookup_name(struct net_context *c, + TALLOC_CTX *mem_ctx, struct cli_state *cli, + const char *name, const char **ret_domain, + const char **ret_name, struct dom_sid *ret_sid, + enum lsa_SidType *ret_type) +{ + struct rpc_pipe_client *lsa_pipe = NULL; + struct policy_handle pol; + NTSTATUS status, result; + const char **dom_names; + struct dom_sid *sids; + enum lsa_SidType *types; + struct dcerpc_binding_handle *b; + + ZERO_STRUCT(pol); + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &lsa_pipe); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, _("Could not initialise lsa pipe\n")); + return status; + } + + b = lsa_pipe->binding_handle; + + status = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol); + if (!NT_STATUS_IS_OK(status)) { + d_fprintf(stderr, "open_policy %s: %s\n", _("failed"), + nt_errstr(status)); + return status; + } + + status = rpccli_lsa_lookup_names(lsa_pipe, mem_ctx, &pol, 1, + &name, &dom_names, 1, &sids, &types); + + if (!NT_STATUS_IS_OK(status)) { + /* This can happen easily, don't log an error */ + goto done; + } + + if (ret_domain != NULL) { + *ret_domain = dom_names[0]; + } + if (ret_name != NULL) { + *ret_name = talloc_strdup(mem_ctx, name); + } + if (ret_sid != NULL) { + sid_copy(ret_sid, &sids[0]); + } + if (ret_type != NULL) { + *ret_type = types[0]; + } + + done: + if (is_valid_policy_hnd(&pol)) { + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + } + TALLOC_FREE(lsa_pipe); + + return status; +} + +/**************************************************************************** + Connect to \\server\service. +****************************************************************************/ + +NTSTATUS connect_to_service(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name, + const char *service_name, + const char *service_type) +{ + NTSTATUS nt_status; + int flags = 0; + + if (strequal(service_type, "IPC")) { + flags |= CLI_FULL_CONNECTION_IPC; + } + + nt_status = cli_full_connection_creds(cli_ctx, NULL, server_name, + server_ss, c->opt_port, + service_name, service_type, + c->creds, + flags); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("Could not connect to server %s\n"), + server_name); + + /* Display a nicer message depending on the result */ + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_LOGON_FAILURE)) + d_fprintf(stderr, + _("The username or password was not " + "correct.\n")); + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_ACCOUNT_LOCKED_OUT)) + d_fprintf(stderr, _("The account was locked out.\n")); + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_ACCOUNT_DISABLED)) + d_fprintf(stderr, _("The account was disabled.\n")); + return nt_status; + } + + return nt_status; +} + +/**************************************************************************** + Connect to \\server\ipc$. +****************************************************************************/ + +NTSTATUS connect_to_ipc(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name) +{ + return connect_to_service(c, cli_ctx, server_ss, server_name, "IPC$", + "IPC"); +} + +/**************************************************************************** + Connect to \\server\ipc$ anonymously. +****************************************************************************/ + +NTSTATUS connect_to_ipc_anonymous(struct net_context *c, + struct cli_state **cli_ctx, + const struct sockaddr_storage *server_ss, + const char *server_name) +{ + NTSTATUS nt_status; + struct cli_credentials *anon_creds = NULL; + + anon_creds = cli_credentials_init_anon(c); + if (anon_creds == NULL) { + DBG_ERR("cli_credentials_init_anon() failed\n"); + return NT_STATUS_NO_MEMORY; + } + + nt_status = cli_full_connection_creds(cli_ctx, c->opt_requester_name, + server_name, server_ss, c->opt_port, + "IPC$", "IPC", + anon_creds, + CLI_FULL_CONNECTION_IPC); + + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } else { + DEBUG(1,("Cannot connect to server (anonymously). Error was %s\n", nt_errstr(nt_status))); + return nt_status; + } +} + +/** + * Connect a server and open a given pipe + * + * @param cli_dst A cli_state + * @param pipe The pipe to open + * @param got_pipe boolean that stores if we got a pipe + * + * @return Normal NTSTATUS return. + **/ +NTSTATUS connect_dst_pipe(struct net_context *c, struct cli_state **cli_dst, + struct rpc_pipe_client **pp_pipe_hnd, + const struct ndr_interface_table *table) +{ + NTSTATUS nt_status; + char *server_name = SMB_STRDUP("127.0.0.1"); + struct cli_state *cli_tmp = NULL; + struct rpc_pipe_client *pipe_hnd = NULL; + + if (server_name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (c->opt_destination) { + SAFE_FREE(server_name); + if ((server_name = SMB_STRDUP(c->opt_destination)) == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + /* make a connection to a named pipe */ + nt_status = connect_to_ipc(c, &cli_tmp, NULL, server_name); + if (!NT_STATUS_IS_OK(nt_status)) { + SAFE_FREE(server_name); + return nt_status; + } + + nt_status = cli_rpc_pipe_open_noauth(cli_tmp, table, + &pipe_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("couldn't not initialize pipe\n")); + cli_shutdown(cli_tmp); + SAFE_FREE(server_name); + return nt_status; + } + + *cli_dst = cli_tmp; + *pp_pipe_hnd = pipe_hnd; + SAFE_FREE(server_name); + + return nt_status; +} + +/**************************************************************************** + Use the local machine account (krb) and password for this session. +****************************************************************************/ + +int net_use_krb_machine_account(struct net_context *c) +{ + char *user_name = NULL; + + if (!secrets_init()) { + d_fprintf(stderr,_("ERROR: Unable to open secrets database\n")); + exit(1); + } + + c->opt_password = secrets_fetch_machine_password( + c->opt_target_workgroup, NULL, NULL); + if (asprintf(&user_name, "%s$@%s", lp_netbios_name(), lp_realm()) == -1) { + return -1; + } + c->opt_user_name = user_name; + c->opt_user_specified = true; + + cli_credentials_set_machine_account(c->creds, c->lp_ctx); + return 0; +} + +bool net_find_server(struct net_context *c, + const char *domain, + unsigned flags, + struct sockaddr_storage *server_ss, + char **server_name) +{ + const char *d = domain ? domain : c->opt_target_workgroup; + + if (c->opt_host) { + *server_name = SMB_STRDUP(c->opt_host); + } + + if (c->opt_have_ip) { + *server_ss = c->opt_dest_ip; + if (!*server_name) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &c->opt_dest_ip); + *server_name = SMB_STRDUP(addr); + } + } else if (*server_name) { + /* resolve the IP address */ + if (!resolve_name(*server_name, server_ss, 0x20, false)) { + DEBUG(1,("Unable to resolve server name\n")); + return false; + } + } else if (flags & NET_FLAGS_PDC) { + fstring dc_name; + struct sockaddr_storage pdc_ss; + + if (!get_pdc_ip(d, &pdc_ss)) { + DEBUG(1,("Unable to resolve PDC server address\n")); + return false; + } + + if (is_zero_addr(&pdc_ss)) { + return false; + } + + if (!name_status_find(d, 0x1b, 0x20, &pdc_ss, dc_name)) { + return false; + } + + *server_name = SMB_STRDUP(dc_name); + *server_ss = pdc_ss; + } else if (flags & NET_FLAGS_DMB) { + struct sockaddr_storage msbrow_ss; + char addr[INET6_ADDRSTRLEN]; + + /* if (!resolve_name(MSBROWSE, &msbrow_ip, 1, false)) */ + if (!resolve_name(d, &msbrow_ss, 0x1B, false)) { + DEBUG(1,("Unable to resolve domain browser via name lookup\n")); + return false; + } + *server_ss = msbrow_ss; + print_sockaddr(addr, sizeof(addr), server_ss); + *server_name = SMB_STRDUP(addr); + } else if (flags & NET_FLAGS_MASTER) { + struct sockaddr_storage brow_ss; + char addr[INET6_ADDRSTRLEN]; + if (!resolve_name(d, &brow_ss, 0x1D, false)) { + /* go looking for workgroups */ + DEBUG(1,("Unable to resolve master browser via name lookup\n")); + return false; + } + *server_ss = brow_ss; + print_sockaddr(addr, sizeof(addr), server_ss); + *server_name = SMB_STRDUP(addr); + } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) { + if (!interpret_string_addr(server_ss, + "127.0.0.1", AI_NUMERICHOST)) { + DEBUG(1,("Unable to resolve 127.0.0.1\n")); + return false; + } + *server_name = SMB_STRDUP("127.0.0.1"); + } + + if (!*server_name) { + DEBUG(1,("no server to connect to\n")); + return false; + } + + return true; +} + +bool net_find_pdc(struct sockaddr_storage *server_ss, + fstring server_name, + const char *domain_name) +{ + if (!get_pdc_ip(domain_name, server_ss)) { + return false; + } + if (is_zero_addr(server_ss)) { + return false; + } + + if (!name_status_find(domain_name, 0x1b, 0x20, server_ss, server_name)) { + return false; + } + + return true; +} + +NTSTATUS net_make_ipc_connection(struct net_context *c, unsigned flags, + struct cli_state **pcli) +{ + return net_make_ipc_connection_ex(c, c->opt_workgroup, NULL, NULL, flags, pcli); +} + +NTSTATUS net_make_ipc_connection_ex(struct net_context *c ,const char *domain, + const char *server, + const struct sockaddr_storage *pss, + unsigned flags, struct cli_state **pcli) +{ + char *server_name = NULL; + struct sockaddr_storage server_ss; + struct cli_state *cli = NULL; + NTSTATUS nt_status; + + if ( !server || !pss ) { + if (!net_find_server(c, domain, flags, &server_ss, + &server_name)) { + d_fprintf(stderr, _("Unable to find a suitable server " + "for domain %s\n"), domain); + nt_status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } else { + server_name = SMB_STRDUP( server ); + server_ss = *pss; + } + + if (flags & NET_FLAGS_ANONYMOUS) { + nt_status = connect_to_ipc_anonymous(c, &cli, &server_ss, + server_name); + } else { + nt_status = connect_to_ipc(c, &cli, &server_ss, + server_name); + } + + /* store the server in the affinity cache if it was a PDC */ + + if ( (flags & NET_FLAGS_PDC) && NT_STATUS_IS_OK(nt_status) ) + saf_store(cli->server_domain, server_name); + + SAFE_FREE(server_name); + if (!NT_STATUS_IS_OK(nt_status)) { + d_fprintf(stderr, _("Connection failed: %s\n"), + nt_errstr(nt_status)); + cli = NULL; + } else if (c->opt_request_timeout) { + cli_set_timeout(cli, c->opt_request_timeout * 1000); + } + +done: + if (pcli != NULL) { + *pcli = cli; + } + return nt_status; +} + +/**************************************************************************** +****************************************************************************/ + +/* TODO FIXME: Pass cli_creds via net_context and get rid of this function. */ +const char *net_prompt_pass(struct net_context *c, const char *user) +{ + struct cli_credentials *creds = samba_cmdline_get_creds(); + + if (c->opt_password == NULL) { + c->opt_password = cli_credentials_get_password(creds); + } + + return c->opt_password; +} + +int net_run_function(struct net_context *c, int argc, const char **argv, + const char *whoami, struct functable *table) +{ + int i; + + if (argc != 0) { + for (i=0; table[i].funcname != NULL; i++) { + if (strcasecmp_m(argv[0], table[i].funcname) == 0) + return table[i].fn(c, argc-1, argv+1); + } + } + + if (c->display_usage == false) { + d_fprintf(stderr, _("Invalid command: %s %s\n"), whoami, + (argc > 0)?argv[0]:""); + } + d_printf(_("Usage:\n")); + for (i=0; table[i].funcname != NULL; i++) { + if(c->display_usage == false) + d_printf("%s %-15s %s\n", whoami, table[i].funcname, + _(table[i].description)); + else + d_printf("%s\n", _(table[i].usage)); + } + + return c->display_usage?0:-1; +} + +void net_display_usage_from_functable(struct functable *table) +{ + int i; + for (i=0; table[i].funcname != NULL; i++) { + d_printf("%s\n", _(table[i].usage)); + } +} + +void net_warn_member_options(void) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + if (lp_ctx != NULL) { + netlogon_creds_cli_warn_options(lp_ctx); + } + + TALLOC_FREE(frame); +} + +const char *net_share_type_str(int num_type) +{ + switch(num_type) { + case 0: return _("Disk"); + case 1: return _("Print"); + case 2: return _("Dev"); + case 3: return _("IPC"); + default: return _("Unknown"); + } +} + +static NTSTATUS net_scan_dc_noad(struct net_context *c, + struct cli_state *cli, + struct net_dc_info *dc_info) +{ + TALLOC_CTX *mem_ctx = talloc_tos(); + struct rpc_pipe_client *pipe_hnd = NULL; + struct dcerpc_binding_handle *b; + NTSTATUS status, result; + struct policy_handle pol; + union lsa_PolicyInformation *info; + + ZERO_STRUCTP(dc_info); + ZERO_STRUCT(pol); + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, + &pipe_hnd); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + b = pipe_hnd->binding_handle; + + status = dcerpc_lsa_open_policy(b, mem_ctx, + false, + SEC_FLAG_MAXIMUM_ALLOWED, + &pol, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + status = dcerpc_lsa_QueryInfoPolicy(b, mem_ctx, + &pol, + LSA_POLICY_INFO_ACCOUNT_DOMAIN, + &info, + &result); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + if (!NT_STATUS_IS_OK(result)) { + status = result; + goto done; + } + + dc_info->netbios_domain_name = talloc_strdup(mem_ctx, info->account_domain.name.string); + if (dc_info->netbios_domain_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + done: + if (is_valid_policy_hnd(&pol)) { + dcerpc_lsa_Close(b, mem_ctx, &pol, &result); + } + + TALLOC_FREE(pipe_hnd); + + return status; +} + +NTSTATUS net_scan_dc(struct net_context *c, + struct cli_state *cli, + struct net_dc_info *dc_info) +{ + TALLOC_CTX *mem_ctx = talloc_tos(); + struct rpc_pipe_client *dssetup_pipe = NULL; + struct dcerpc_binding_handle *dssetup_handle = NULL; + union dssetup_DsRoleInfo info; + NTSTATUS status; + WERROR werr; + + ZERO_STRUCTP(dc_info); + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_dssetup, + &dssetup_pipe); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10,("net_scan_dc: failed to open dssetup pipe with %s, " + "retrying with lsa pipe\n", nt_errstr(status))); + return net_scan_dc_noad(c, cli, dc_info); + } + dssetup_handle = dssetup_pipe->binding_handle; + + status = dcerpc_dssetup_DsRoleGetPrimaryDomainInformation(dssetup_handle, mem_ctx, + DS_ROLE_BASIC_INFORMATION, + &info, + &werr); + TALLOC_FREE(dssetup_pipe); + + if (NT_STATUS_IS_OK(status)) { + status = werror_to_ntstatus(werr); + } + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + dc_info->is_dc = (info.basic.role & (DS_ROLE_PRIMARY_DC|DS_ROLE_BACKUP_DC)); + dc_info->is_pdc = (info.basic.role & DS_ROLE_PRIMARY_DC); + dc_info->is_ad = (info.basic.flags & DS_ROLE_PRIMARY_DS_RUNNING); + dc_info->is_mixed_mode = (info.basic.flags & DS_ROLE_PRIMARY_DS_MIXED_MODE); + dc_info->netbios_domain_name = talloc_strdup(mem_ctx, info.basic.domain); + dc_info->dns_domain_name = talloc_strdup(mem_ctx, info.basic.dns_domain); + dc_info->forest_name = talloc_strdup(mem_ctx, info.basic.forest); + + return NT_STATUS_OK; +} diff --git a/source3/utils/net_vfs.c b/source3/utils/net_vfs.c new file mode 100644 index 0000000..410eef3 --- /dev/null +++ b/source3/utils/net_vfs.c @@ -0,0 +1,469 @@ +/* + * Samba Unix/Linux SMB client library + * Distributed SMB/CIFS Server Management Utility + * Copyright (C) 2019 Ralph Boehme <slow@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include <talloc.h> +#include <tevent.h> +#include <ftw.h> +#include "system/filesys.h" +#include "system/passwd.h" +#include "lib/param/loadparm.h" +#include "lib/param/param.h" +#include "libcli/security/security.h" +#include "smbd/proto.h" +#include "locking/share_mode_lock.h" +#include "locking/proto.h" +#include "auth.h" +#include "client.h" +#include "util_sd.h" +#include "lib/adouble.h" +#include "lib/string_replace.h" +#include "utils/net.h" +#include "lib/global_contexts.h" + +#define NET_VFS_CMD_STREAM_TO_ADOUBLE "stream2adouble" + +static struct net_vfs_state { + TALLOC_CTX *mem_ctx; + struct net_context *c; + struct auth_session_info *session_info; + struct conn_struct_tos *conn_tos; +} state; + +static void net_vfs_usage(void) +{ + fprintf(stderr, + "Usage:\n" + "net vfs [OPTIONS] <share> ....\n"); +} + +static void net_vfs_getntacl_usage(void) +{ + fprintf(stderr, + "Usage:\n" + "net vfs getntacl <share> <path>\n"); +} + +static void net_vfs_stream_to_appledouble_usage(void) +{ + fprintf(stderr, + "Usage:\n" + "net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE + " [OPTIONS] <share> <path> [<path> ...]\n" + "Options:\n" + " --verbose verbose output\n" + " --continue continue on error\n" + " --recursive traverse directory hierarchy\n" + " --follow-symlinks follow symlinks\n"); +} + +static bool net_vfs_make_session_info(struct auth_session_info **session_info) +{ + NTSTATUS status; + + if (non_root_mode()) { + struct passwd *p = NULL; + + p = getpwuid(geteuid()); + if (p == NULL) { + fprintf(stderr, "getpwuid(%d) failed\n", geteuid()); + return false; + } + + status = make_session_info_from_username(state.mem_ctx, + p->pw_name, + false, + session_info); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "session_info from username failed\n"); + return false; + } + + return true; + } + + status = init_system_session_info(state.mem_ctx); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "init_system_session_info failed\n"); + return false; + } + + status = make_session_info_system(state.mem_ctx, session_info); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "make_session_info_system failed\n"); + return false; + } + + return true; +} + +static int net_vfs_init(struct net_context *c, int argc, const char **argv) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *service = NULL; + char *share_root = NULL; + int snum; + NTSTATUS status; + bool ok; + int rc = 1; + + state = (struct net_vfs_state) { + .c = c, + .mem_ctx = c, + }; + + if (argc < 1) { + net_vfs_usage(); + goto done; + } + + if (geteuid() != 0 && !uid_wrapper_enabled()) { + fprintf(stderr, "'net vfs' must be run as root.\n"); + goto done; + } + + smb_init_locale(); + umask(0); + sec_init(); + setup_logging("net", DEBUG_STDOUT); + lpcfg_set_cmdline(c->lp_ctx, "log level", "0"); + + ok = lp_load_with_registry_shares(get_dyn_CONFIGFILE()); + if (!ok) { + fprintf(stderr, "lp_load_with_registry_shares failed\n"); + goto done; + } + + ok = locking_init(); + if (!ok) { + fprintf(stderr, "locking init failed\n"); + goto done; + } + + ok = net_vfs_make_session_info(&state.session_info); + if (!ok) { + goto done; + } + + service = argv[0]; + snum = lp_servicenumber(service); + if (snum == -1) { + fprintf(stderr, "unknown service: %s\n", service); + goto done; + } + + share_root = lp_path(state.mem_ctx, lp_sub, snum); + if (share_root == NULL) { + fprintf(stderr, "Failed to find share root for service: %s\n", + service); + goto done; + } + + status = create_conn_struct_tos_cwd(global_messaging_context(), + snum, + share_root, + state.session_info, + &state.conn_tos); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + state.conn_tos->conn->share_access = FILE_GENERIC_ALL; + state.conn_tos->conn->read_only = false; + file_init(state.conn_tos->conn->sconn); + + ok = become_user_without_service_by_session(state.conn_tos->conn, + state.session_info); + if (!ok) { + fprintf(stderr, + "become_user_without_service_by_session failed\n"); + goto done; + } + + rc = 0; +done: + return rc; +} + +static int net_vfs_get_ntacl(struct net_context *net, + int argc, + const char **argv) +{ + const char *path = NULL; + struct smb_filename *smb_fname = NULL; + files_struct *fsp = NULL; + struct security_descriptor *sd = NULL; + NTSTATUS status; + int ret; + int rc = 1; + + if (argc < 2 || net->display_usage) { + net_vfs_getntacl_usage(); + goto done; + } + + ret = net_vfs_init(net, argc, argv); + if (ret != 0) { + goto done; + } + + path = argv[1]; + smb_fname = synthetic_smb_fname(state.mem_ctx, + path, + NULL, + NULL, + 0, + 0); + if (smb_fname == NULL) { + goto done; + } + + ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname); + if (ret != 0) { + fprintf(stderr, "stat [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), strerror(errno)); + goto done; + } + + status = openat_pathref_fsp(state.conn_tos->conn->cwd_fsp, smb_fname); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("openat_pathref_fsp [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), nt_errstr(status)); + goto done; + } + + status = SMB_VFS_CREATE_FILE( + state.conn_tos->conn, + NULL, /* req */ + NULL, + smb_fname, + FILE_READ_ATTRIBUTES|READ_CONTROL_ACCESS, + FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, + 0, /* create_options */ + 0, /* file_attributes */ + INTERNAL_OPEN_ONLY, /* oplock_request */ + NULL, /* lease */ + 0, /* allocation_size */ + 0, /* private_flags */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, + NULL, /* info */ + NULL, NULL); /* create context */ + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_CREATE_FILE [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), nt_errstr(status)); + goto done; + } + + status = SMB_VFS_FGET_NT_ACL(fsp, + SECINFO_OWNER|SECINFO_GROUP|SECINFO_DACL, + talloc_tos(), + &sd); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("SMB_VFS_FGET_NT_ACL [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), nt_errstr(status)); + goto done; + } + + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("close_file [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + goto done; + } + + sec_desc_print(NULL, stdout, sd, true); + + rc = 0; +done: + TALLOC_FREE(sd); + + if (fsp != NULL) { + status = close_file_free(NULL, &fsp, NORMAL_CLOSE); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("close_file_free() [%s] failed: %s\n", + smb_fname_str_dbg(smb_fname), + nt_errstr(status)); + rc = 1; + } + } + return rc; +} + +static bool do_unfruit(const char *path) +{ + struct smb_filename *smb_fname = NULL; + char *p = NULL; + bool converted; + int ret; + bool ok; + + p = strrchr_m(path, '/'); + if (p != NULL) { + if (p[1] == '.' && p[2] == '_') { + return true; + } + } + + smb_fname = synthetic_smb_fname(state.mem_ctx, + path, + NULL, + NULL, + 0, + 0); + if (smb_fname == NULL) { + return false; + } + + ret = SMB_VFS_STAT(state.conn_tos->conn, smb_fname); + if (ret != 0) { + fprintf(stderr, "%s: %s\n", path, strerror(errno)); + if (state.c->opt_continue_on_error) { + return true; + } + return false; + } + + ok = ad_unconvert(state.mem_ctx, + state.conn_tos->conn->vfs_handles, + macos_string_replace_map, + smb_fname, + &converted); + if (!ok) { + fprintf(stderr, "Converting failed: %s\n", path); + if (state.c->opt_continue_on_error) { + return true; + } + return false; + } + + if (converted) { + fprintf(stdout, "Converted: %s\n", path); + } else if (state.c->opt_verbose) { + fprintf(stdout, "%s\n", path); + } + return true; +} + +static int nftw_cb(const char *path, + const struct stat *sb, + int typeflag, + struct FTW *ftwbuf) +{ + bool ok; + + if (typeflag == FTW_SL) { + if (state.c->opt_verbose) { + fprintf(stdout, "Ignoring symlink: %s\n", path); + } + return 0; + } + + ok = do_unfruit(path); + if (!ok) { + return -1; + } + + return 0; +} + +static int net_vfs_stream_to_appledouble(struct net_context *net, + int argc, + const char **argv) +{ + int i; + int ret; + bool ok; + int rc = 1; + + if (argc < 2 || net->display_usage) { + net_vfs_stream_to_appledouble_usage(); + goto done; + } + + ret = net_vfs_init(net, argc, argv); + if (ret != 0) { + goto done; + } + + for (i = 1; i < argc; i++) { + const char *path = argv[i]; + + if (path[0] == '/') { + fprintf(stderr, "ignoring absolute path: %s\n", path); + if (state.c->opt_continue_on_error) { + continue; + } + goto done; + } + + if (!state.c->opt_recursive) { + ok = do_unfruit(path); + if (!ok) { + if (!state.c->opt_continue_on_error) { + goto done; + } + } + continue; + } + + ret = nftw(path, + nftw_cb, + 256, + state.c->opt_follow_symlink ? 0 : FTW_PHYS); + if (ret != 0) { + fprintf(stderr, "%s: %s\n", path, strerror(errno)); + if (!state.c->opt_continue_on_error) { + goto done; + } + } + } + + rc = 0; + +done: + return rc; +} + +static struct functable func[] = { + { + "getntacl", + net_vfs_get_ntacl, + NET_TRANSPORT_LOCAL, + N_("Display security descriptor of a file or directory"), + N_("net vfs getntacl <share> <path> [<path> ...]") + }, + { + NET_VFS_CMD_STREAM_TO_ADOUBLE, + net_vfs_stream_to_appledouble, + NET_TRANSPORT_LOCAL, + N_("Convert streams to AppleDouble files"), + N_("net vfs " NET_VFS_CMD_STREAM_TO_ADOUBLE " [OPTIONS] <share> <path> [<path> ...]") + }, + {NULL, NULL, 0, NULL, NULL} +}; + +int net_vfs(struct net_context *c, int argc, const char **argv) +{ + return net_run_function(c, argc, argv, "net vfs", func); +} diff --git a/source3/utils/net_witness.c b/source3/utils/net_witness.c new file mode 100644 index 0000000..accff5b --- /dev/null +++ b/source3/utils/net_witness.c @@ -0,0 +1,2361 @@ +/* + * Samba Unix/Linux client library + * net witness commands to manage smb witness registrations + * Copyright (C) 2023 Stefan Metzmacher + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "utils/net.h" +#include "messages.h" +#include "serverid.h" +#include "lib/util/util_tdb.h" +#include "source3/include/util_tdb.h" +#include "libcli/security/dom_sid.h" +#include "lib/dbwrap/dbwrap.h" +#include "lib/dbwrap/dbwrap_rbt.h" +#include "lib/dbwrap/dbwrap_open.h" +#include "lib/param/param.h" +#include "librpc/gen_ndr/ndr_rpcd_witness.h" +#include <regex.h> + +struct json_object; + +#ifdef HAVE_JANSSON +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#endif /* HAVE_JANSSON */ + +#undef strcasecmp + +static struct db_context *net_witness_open_registration_db(void) +{ + static struct db_context *db; + char *global_path = NULL; + + if (db != NULL) { + return db; + } + + global_path = lock_path(talloc_tos(), "rpcd_witness_registration.tdb"); + if (global_path == NULL) { + return NULL; + } + + db = db_open(NULL, + global_path, + 0, /* hash_size */ + TDB_DEFAULT | + TDB_CLEAR_IF_FIRST | + TDB_INCOMPATIBLE_HASH, + O_RDONLY, + 0600, + DBWRAP_LOCK_ORDER_1, + DBWRAP_FLAG_NONE); + TALLOC_FREE(global_path); + if (db == NULL) { + return NULL; + } + + return db; +} + +struct net_witness_scan_registrations_action_state { + bool (*prepare_fn)(void *private_data); + bool (*match_fn)(void *private_data, const struct rpcd_witness_registration *rg); + NTSTATUS (*process_fn)(void *private_data, const struct rpcd_witness_registration *rg); + void *private_data; +}; + +struct net_witness_scan_registrations_regex { + regex_t regex; + bool valid; +}; + +struct net_witness_scan_registrations_state { + struct net_context *c; + struct net_witness_scan_registrations_regex net_name; + struct net_witness_scan_registrations_regex share_name; + struct net_witness_scan_registrations_regex ip_address; + struct net_witness_scan_registrations_regex client_computer; + struct json_object *message_json; +#ifdef HAVE_JANSSON + struct json_object filters_json; + struct json_object registrations_json; +#endif + const struct net_witness_scan_registrations_action_state *action; + NTSTATUS error; +}; + +static bool net_witness_scan_registrations_regex_init( + struct net_witness_scan_registrations_state *state, + struct net_witness_scan_registrations_regex *r, + const char *option, const char *value); +static bool net_witness_scan_registrations_regex_match( + struct net_witness_scan_registrations_regex *r, + const char *name, const char *value); +static void net_witness_scan_registrations_regex_free( + struct net_witness_scan_registrations_regex *r); + +static bool net_witness_scan_registrations_match( + struct net_witness_scan_registrations_state *state, + const struct rpcd_witness_registration *rg) +{ + if (state->net_name.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->net_name, + "net_name", + rg->net_name); + if (!match) { + return false; + } + } + + if (state->share_name.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->share_name, + "share_name", + rg->share_name); + if (!match) { + return false; + } + } + + if (state->ip_address.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->ip_address, + "ip_address", + rg->ip_address); + if (!match) { + return false; + } + } + + if (state->client_computer.valid) { + bool match; + + match = net_witness_scan_registrations_regex_match( + &state->client_computer, + "client_computer_name", + rg->client_computer_name); + if (!match) { + return false; + } + } + + return true; +} + +static bool net_witness_scan_registrations_regex_init( + struct net_witness_scan_registrations_state *state, + struct net_witness_scan_registrations_regex *r, + const char *option, const char *value) +{ +#ifdef HAVE_JANSSON + struct net_context *c = state->c; +#endif /* HAVE_JANSSON */ + int ret; + + r->valid = false; + + if (value == NULL) { + return true; + } + + ret = regcomp(&r->regex, value, REG_EXTENDED|REG_ICASE|REG_NOSUB); + if (ret != 0) { + fstring buf = { 0,}; + regerror(ret, &r->regex, buf, sizeof(buf)); + d_printf("regcomp(%s) failed for %s: " + "%d: %s\n", value, option, ret, buf); + return false; + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + ret = json_add_string(&state->filters_json, + option, + value); + if (ret != 0) { + return false; + } + } +#endif /* HAVE_JANSSON */ + + r->valid = true; + return true; +} + +static bool net_witness_scan_registrations_regex_match( + struct net_witness_scan_registrations_regex *r, + const char *name, const char *value) +{ + int ret; + + if (!r->valid) { + return false; + } + + if (value == NULL) { + /* + * without a share name, + * we match against an empty + * string. + */ + value = ""; + } + + ret = regexec(&r->regex, value, 0, NULL, 0); + if (ret == REG_NOMATCH) { + return false; + } + + return true; +} + +static void net_witness_scan_registrations_regex_free( + struct net_witness_scan_registrations_regex *r) +{ + if (r->valid) { + regfree(&r->regex); + r->valid = false; + } +} + +static bool net_witness_scan_registrations_init( + struct net_witness_scan_registrations_state *state) +{ + struct net_context *c = state->c; + bool ok; + + if (c->opt_json) { +#ifdef HAVE_JANSSON + state->filters_json = json_new_object(); + if (json_is_invalid(&state->filters_json)) { + return false; + } + + if (c->opt_witness_registration != NULL) { + int ret; + + ret = json_add_string(&state->filters_json, + "--witness-registration", + c->opt_witness_registration); + if (ret != 0) { + return false; + } + } + + if (c->opt_witness_apply_to_all != 0) { + int ret; + + ret = json_add_bool(&state->filters_json, + "--witness-apply-to-all", + c->opt_witness_apply_to_all != 0); + if (ret != 0) { + return false; + } + } + + state->registrations_json = json_new_object(); + if (json_is_invalid(&state->registrations_json)) { + return false; + } +#else /* not HAVE_JANSSON */ + d_fprintf(stderr, _("JSON support not available\n")); + return false; +#endif /* not HAVE_JANSSON */ + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->net_name, + "--witness-net-name", + c->opt_witness_net_name); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->share_name, + "--witness-share-name", + c->opt_witness_share_name); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->ip_address, + "--witness-ip-address", + c->opt_witness_ip_address); + if (!ok) { + return false; + } + + ok = net_witness_scan_registrations_regex_init(state, + &state->client_computer, + "--witness-client-computer-name", + c->opt_witness_client_computer_name); + if (!ok) { + return false; + } + + ok = state->action->prepare_fn(state->action->private_data); + if (!ok) { + return false; + } + + if (!c->opt_json) { + d_printf("%-36s %-20s %-15s %-20s %s\n", + "Registration-UUID:", + "NetName", + "ShareName", + "IpAddress", + "ClientComputerName"); + d_printf("%-36s-%-20s-%-15s-%-20s-%s\n", + "------------------------------------", + "--------------------", + "------------------", + "--------------------", + "------------------"); + } + + return true; +} + +static bool net_witness_scan_registrations_finish( + struct net_witness_scan_registrations_state *state) +{ +#ifdef HAVE_JANSSON + struct net_context *c = state->c; + struct json_object root_json = json_empty_object; + TALLOC_CTX *frame = NULL; + const char *json_str = NULL; + int ret; + + if (!c->opt_json) { + return true; + } + + frame = talloc_stackframe(); + + root_json = json_new_object(); + if (json_is_invalid(&root_json)) { + TALLOC_FREE(frame); + return false; + } + + ret = json_add_object(&root_json, + "filters", + &state->filters_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + state->filters_json = json_empty_object; + + if (state->message_json != NULL) { + ret = json_add_object(&root_json, + "message", + state->message_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + *state->message_json = json_empty_object; + } + + ret = json_add_object(&root_json, + "registrations", + &state->registrations_json); + if (ret != 0) { + json_free(&root_json); + TALLOC_FREE(frame); + return false; + } + state->registrations_json = json_empty_object; + + json_str = json_to_string(frame, &root_json); + json_free(&root_json); + if (json_str == NULL) { + TALLOC_FREE(frame); + return false; + } + + d_printf("%s\n", json_str); + TALLOC_FREE(frame); + return true; +#else /* not HAVE_JANSSON */ + return true; +#endif /* not HAVE_JANSSON */ +} + +static void net_witness_scan_registrations_free( + struct net_witness_scan_registrations_state *state) +{ +#ifdef HAVE_JANSSON + if (!json_is_invalid(&state->filters_json)) { + json_free(&state->filters_json); + } + if (!json_is_invalid(&state->registrations_json)) { + json_free(&state->registrations_json); + } +#endif /* HAVE_JANSSON */ + + net_witness_scan_registrations_regex_free(&state->net_name); + net_witness_scan_registrations_regex_free(&state->share_name); + net_witness_scan_registrations_regex_free(&state->ip_address); + net_witness_scan_registrations_regex_free(&state->client_computer); +} + +#ifdef HAVE_JANSSON +static int dump_registration_json(struct json_object *registrations_json, + const char *key_str, + const struct rpcd_witness_registration *rg) +{ + struct json_object jsobj = json_empty_object; + struct json_object flags_json = json_empty_object; + struct json_object context_json = json_empty_object; + struct json_object serverid_json = json_empty_object; + struct json_object auth_json = json_empty_object; + struct json_object connection_json = json_empty_object; + struct timeval tv; + struct dom_sid_buf sid_buf; + int ret = 0; + + jsobj = json_new_object(); + if (json_is_invalid(&jsobj)) { + d_fprintf(stderr, _("error setting up JSON value\n")); + goto failure; + } + + ret = json_add_flags32(&jsobj, "version", rg->version); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "net_name", rg->net_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "share_name", rg->share_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "ip_address", rg->ip_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&jsobj, "client_computer_name", rg->client_computer_name); + if (ret != 0) { + goto failure; + } + + flags_json = json_new_object(); + if (json_is_invalid(&flags_json)) { + goto failure; + } + + ret = json_add_bool(&flags_json, "WITNESS_REGISTER_IP_NOTIFICATION", + (rg->flags & WITNESS_REGISTER_IP_NOTIFICATION) ? + true : false); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&flags_json, "int", rg->flags); + if (ret != 0) { + goto failure; + } + + ret = json_add_flags32(&flags_json, "hex", rg->flags); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "flags", &flags_json); + if (ret != 0) { + goto failure; + } + flags_json = json_empty_object; + + ret = json_add_int(&jsobj, "timeout", rg->timeout); + if (ret != 0) { + goto failure; + } + + context_json = json_new_object(); + if (json_is_invalid(&context_json)) { + goto failure; + } + + ret = json_add_int(&context_json, "handle_type", rg->context_handle.handle_type); + if (ret != 0) { + goto failure; + } + + ret = json_add_guid(&context_json, "uuid", &rg->context_handle.uuid); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "context_handle", &context_json); + if (ret != 0) { + goto failure; + } + context_json = json_empty_object; + + serverid_json = json_new_object(); + if (json_is_invalid(&serverid_json)) { + goto failure; + } + + ret = json_add_int(&serverid_json, "pid", rg->server_id.pid); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "task_id", rg->server_id.task_id); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "vnn", rg->server_id.vnn); + if (ret != 0) { + goto failure; + } + + ret = json_add_int(&serverid_json, "unique_id", rg->server_id.unique_id); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "server_id", &serverid_json); + if (ret != 0) { + goto failure; + } + serverid_json = json_empty_object; + + auth_json = json_new_object(); + if (json_is_invalid(&auth_json)) { + goto failure; + } + + ret = json_add_string(&auth_json, "account_name", rg->account_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&auth_json, "domain_name", rg->domain_name); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&auth_json, + "account_sid", + dom_sid_str_buf(&rg->account_sid, &sid_buf)); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "auth", &auth_json); + if (ret != 0) { + goto failure; + } + auth_json = json_empty_object; + + connection_json = json_new_object(); + if (json_is_invalid(&connection_json)) { + goto failure; + } + + ret = json_add_string(&connection_json, "local_address", rg->local_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_string(&connection_json, "remote_address", rg->remote_address); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(&jsobj, "connection", &connection_json); + if (ret != 0) { + goto failure; + } + connection_json = json_empty_object; + + nttime_to_timeval(&tv, rg->registration_time); + ret = json_add_time(&jsobj, "registration_time", tv); + if (ret != 0) { + goto failure; + } + + ret = json_add_object(registrations_json, key_str, &jsobj); + if (ret != 0) { + goto failure; + } + jsobj = json_empty_object; + +failure: + if (!json_is_invalid(&connection_json)) { + json_free(&connection_json); + } + if (!json_is_invalid(&auth_json)) { + json_free(&auth_json); + } + if (!json_is_invalid(&serverid_json)) { + json_free(&serverid_json); + } + if (!json_is_invalid(&context_json)) { + json_free(&context_json); + } + if (!json_is_invalid(&flags_json)) { + json_free(&flags_json); + } + if (!json_is_invalid(&jsobj)) { + json_free(&jsobj); + } + + return ret; +} +#endif /* HAVE_JANSSON */ + +static NTSTATUS net_witness_scan_registrations_dump_rg( + struct net_witness_scan_registrations_state *state, + const struct rpcd_witness_registration *rg) +{ + struct net_context *c = state->c; + struct GUID_txt_buf key_buf; + const char *key_str = GUID_buf_string(&rg->context_handle.uuid, &key_buf); + + if (c->opt_json) { +#ifdef HAVE_JANSSON + int ret; + + ret = dump_registration_json(&state->registrations_json, + key_str, + rg); + if (ret != 0) { + d_fprintf(stderr, "dump_registration_json(%s) failed\n", + key_str); + return NT_STATUS_INTERNAL_ERROR; + } +#endif /* HAVE_JANSSON */ + return NT_STATUS_OK; + } + + d_printf("%-36s %-20s %-15s %-20s %s\n", + key_str, + rg->net_name, + rg->share_name ? rg->share_name : "''", + rg->ip_address, + rg->client_computer_name); + + return NT_STATUS_OK; +} + +static void net_witness_scan_registrations_parser(TDB_DATA key, + TDB_DATA val, + void *private_data) +{ + struct net_witness_scan_registrations_state *state = + (struct net_witness_scan_registrations_state *)private_data; + DATA_BLOB val_blob = data_blob_const(val.dptr, val.dsize); + struct rpcd_witness_registration rg; + enum ndr_err_code ndr_err; + TALLOC_CTX *frame = NULL; + bool match = false; + + if (val_blob.length == 0) { + return; + } + + frame = talloc_stackframe(); + + ndr_err = ndr_pull_struct_blob(&val_blob, frame, &rg, + (ndr_pull_flags_fn_t)ndr_pull_rpcd_witness_registration); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + DBG_WARNING("Invalid record in rpcd_witness_registration.tdb:" + "key '%s' ndr_pull_struct_blob - %s\n", + tdb_data_dbg(key), + ndr_errstr(ndr_err)); + state->error = ndr_map_error2ntstatus(ndr_err); + TALLOC_FREE(frame); + return; + } + + if (!serverid_exists(&rg.server_id)) { + TALLOC_FREE(frame); + return; + } + + if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration, &rg); + } + + match = net_witness_scan_registrations_match(state, &rg); + if (!NT_STATUS_IS_OK(state->error)) { + TALLOC_FREE(frame); + return; + } + if (!match) { + TALLOC_FREE(frame); + return; + } + + match = state->action->match_fn(state->action->private_data, &rg); + if (!match) { + TALLOC_FREE(frame); + return; + } + + state->error = state->action->process_fn(state->action->private_data, &rg); + if (NT_STATUS_IS_OK(state->error)) { + state->error = net_witness_scan_registrations_dump_rg(state, + &rg); + } + TALLOC_FREE(frame); +} + +static int net_witness_scan_registrations_traverse_cb(struct db_record *rec, void *private_data) +{ + struct net_witness_scan_registrations_state *state = + (struct net_witness_scan_registrations_state *)private_data; + TDB_DATA key = dbwrap_record_get_key(rec); + TDB_DATA val = dbwrap_record_get_value(rec); + + net_witness_scan_registrations_parser(key, val, private_data); + + if (!NT_STATUS_IS_OK(state->error)) { + return -1; + } + + return 0; +} + +static int net_witness_scan_registrations(struct net_context *c, + struct json_object *message_json, + const struct net_witness_scan_registrations_action_state *action) +{ + struct net_witness_scan_registrations_state state = { + .c = c, + .message_json = message_json, + .action = action, + }; + struct db_context *db = NULL; + NTSTATUS status; + bool ok; + + db = net_witness_open_registration_db(); + if (db == NULL) { + d_printf("net_witness_open_registration_db() failed\n"); + return -1; + } + + ok = net_witness_scan_registrations_init(&state); + if (!ok) { + d_printf("net_witness_scan_registrations_init() failed\n"); + return -1; + } + + if (c->opt_witness_registration != NULL) { + const char *key_str = c->opt_witness_registration; + DATA_BLOB key_blob = data_blob_string_const(key_str); + TDB_DATA key = make_tdb_data(key_blob.data, key_blob.length); + + status = dbwrap_parse_record(db, + key, + net_witness_scan_registrations_parser, + &state); + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) { + status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + d_printf("dbwrap_parse_record(%s) failed: %s\n", + key_str, nt_errstr(status)); + net_witness_scan_registrations_free(&state); + return -1; + } + if (!NT_STATUS_IS_OK(state.error)) { + d_printf("net_witness_scan_registrations_parser(%s) failed: %s\n", + key_str, nt_errstr(state.error)); + net_witness_scan_registrations_free(&state); + return -1; + } + } else { + status = dbwrap_traverse_read(db, + net_witness_scan_registrations_traverse_cb, + &state, + NULL); /* count */ + if (!NT_STATUS_IS_OK(status)) { + d_printf("dbwrap_traverse_read() failed\n"); + net_witness_scan_registrations_free(&state); + return -1; + } + if (!NT_STATUS_IS_OK(state.error)) { + d_printf("net_witness_scan_registrations_traverse_cb() failed: %s\n", + nt_errstr(state.error)); + net_witness_scan_registrations_free(&state); + return -1; + } + } + + ok = net_witness_scan_registrations_finish(&state); + if (!ok) { + d_printf("net_witness_scan_registrations_finish() failed\n"); + return -1; + } + + net_witness_scan_registrations_free(&state); + return 0; +} + +struct net_witness_list_state { + struct net_context *c; +}; + +static bool net_witness_list_prepare_fn(void *private_data) +{ + return true; +} + +static bool net_witness_list_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return true; +} + +static NTSTATUS net_witness_list_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return NT_STATUS_OK; +} + +static void net_witness_filter_usage(void) +{ + d_printf(" Note: Only supported with clustering=yes!\n\n"); + d_printf(" Machine readable output can be generated with " + "the following option:\n" + "\n" + " --json\n" + "\n"); + d_printf(" The selection of registrations can be limited by " + "the following options:\n" + "\n" + " --witness-registration=REGISTRATION_UUID\n" + " This does a direct lookup for REGISTRATION_UUID\n" + " instead of doing a database traversal.\n" + "\n" + " The following options all take a " + "POSIX Extended Regular Expression,\n" + " which can further filter the selection of " + "registrations.\n" + " These options are applied as logical AND, " + "but each REGEX \n" + " allows specifying multiple strings using " + "the pipe symbol.\n" + "\n" + " --witness-net-name=REGEX\n" + " This specifies the 'server name' the client\n" + " registered for monitoring.\n" + "\n" + " --witness-share-name=REGEX\n" + " This specifies the 'share name' the client\n" + " registered for monitoring.\n" + " Note that the share name is optional in the\n" + " registration, otherwise an empty string is \n" + " matched.\n" + "\n" + " --witness-ip-address=REGEX\n" + " This specifies the ip address the client\n" + " registered for monitoring.\n" + "\n" + " --witness-client-computer-name=REGEX\n" + " This specifies the client computer name the client\n" + " specified in the registration.\n" + " Note it is just a string chosen by the " + "client itself.\n" + "\n"); +} + +static void net_witness_list_usage(void) +{ + d_printf("%s\n" + "net witness list\n" + " %s\n\n", + _("Usage:"), + _("List witness registrations " + "from rpcd_witness_registration.tdb")); + net_witness_filter_usage(); +} + +static int net_witness_list(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_list_state state = { .c = c, }; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_list_prepare_fn, + .match_fn = net_witness_list_match_fn, + .process_fn = net_witness_list_process_fn, + .private_data = &state, + }; + int ret = -1; + + if (c->display_usage) { + net_witness_list_usage(); + goto out; + } + + if (argc != 0) { + net_witness_list_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ret = net_witness_scan_registrations(c, NULL, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: + TALLOC_FREE(frame); + return ret; +} + +struct net_witness_client_move_state { + struct net_context *c; + struct rpcd_witness_registration_updateB m; + char *headline; +}; + +static bool net_witness_client_move_prepare_fn(void *private_data) +{ + struct net_witness_client_move_state *state = + (struct net_witness_client_move_state *)private_data; + + if (state->headline != NULL) { + d_printf("%s\n", state->headline); + TALLOC_FREE(state->headline); + } + + return true; +} + +static bool net_witness_client_move_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return true; +} + +static NTSTATUS net_witness_client_move_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + struct net_witness_client_move_state *state = + (struct net_witness_client_move_state *)private_data; + struct net_context *c = state->c; + struct rpcd_witness_registration_updateB update = { + .context_handle = rg->context_handle, + .type = state->m.type, + .update = state->m.update, + }; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + NTSTATUS status; + + if (state->headline != NULL) { + d_printf("%s\n", state->headline); + TALLOC_FREE(state->headline); + } + + SMB_ASSERT(update.type != 0); + + if (DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update); + } + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update, + (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status)); + return status; + } + + status = messaging_send(c->msg_ctx, + rg->server_id, + MSG_RPCD_WITNESS_REGISTRATION_UPDATE, + &blob); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("messaging_send() - %s\n", nt_errstr(status)); + return status; + } + + return NT_STATUS_OK; +} + +static void net_witness_update_usage(void) +{ + d_printf(" If the update should be applied to all registrations\n" + " it needs to be explicitly specified:\n" + "\n" + " --witness-apply-to-all\n" + " This selects all registrations.\n" + " Note: This is mutual exclusive to " + "the above options.\n" + "\n"); +} + +static bool net_witness_verify_update_options(struct net_context *c) +{ + if (c->opt_witness_registration == NULL && + c->opt_witness_net_name == NULL && + c->opt_witness_share_name == NULL && + c->opt_witness_ip_address == NULL && + c->opt_witness_client_computer_name == NULL && + c->opt_witness_apply_to_all == 0) + { + d_printf("--witness-apply-to-all or " + "at least one of following requires:\n" + "--witness-registration\n" + "--witness-net-name\n" + "--witness-share-name\n" + "--witness-ip-address\n" + "--witness-client-computer-name\n"); + return false; + } + + if (c->opt_witness_apply_to_all == 0) { + return true; + } + + if (c->opt_witness_registration != NULL || + c->opt_witness_net_name != NULL || + c->opt_witness_share_name != NULL || + c->opt_witness_ip_address != NULL || + c->opt_witness_client_computer_name != NULL) + { + d_printf("--witness-apply-to-all not allowed " + "together with the following options:\n" + "--witness-registration\n" + "--witness-net-name\n" + "--witness-share-name\n" + "--witness-ip-address\n" + "--witness-client-computer-name\n"); + return false; + } + + return true; +} + +static void net_witness_move_usage(const char *name) +{ + d_printf(" The content of the %s notification contains ip addresses\n" + " specified by (exactly one) of the following options:\n" + "\n" + " --witness-new-node=NODEID\n" + " By specifying a NODEID all ip addresses\n" + " currently available on the given node are\n" + " included in the response.\n" + " By specifying '-1' as NODEID all ip addresses\n" + " of the cluster are included in the response.\n" + "\n" + " --witness-new-ip=IPADDRESS\n" + " By specifying an IPADDRESS only the specified\n" + " ip address is included in the response.\n" + "\n", + name); +} + +static bool net_witness_verify_move_options(struct net_context *c, + uint32_t *new_node, + bool *is_ipv4, + bool *is_ipv6) +{ + bool ok; + + *new_node = NONCLUSTER_VNN; + *is_ipv4 = false; + *is_ipv6 = false; + + ok = net_witness_verify_update_options(c); + if (!ok) { + return false; + } + + if (c->opt_witness_new_ip != NULL && + c->opt_witness_new_node != -2) + { + d_printf("--witness-new-ip and " + "--witness-new-node are not allowed together\n"); + return false; + } + + if (c->opt_witness_new_ip == NULL && + c->opt_witness_new_node == -2) + { + d_printf("--witness-new-ip or --witness-new-node required\n"); + return false; + } + + if (c->opt_witness_new_node != -2) { + *new_node = c->opt_witness_new_node; + return true; + } + + if (is_ipaddress_v4(c->opt_witness_new_ip)) { + *is_ipv4 = true; + return true; + } + + if (is_ipaddress_v6(c->opt_witness_new_ip)) { + *is_ipv6 = true; + return true; + } + + d_printf("Invalid ip address for --witness-new-ip=%s\n", + c->opt_witness_new_ip); + return false; +} + +#ifdef HAVE_JANSSON +static bool net_witness_move_message_json(struct net_context *c, + const char *msg_type, + struct json_object *pmessage_json) +{ + struct json_object message_json = json_empty_object; + int ret; + + message_json = json_new_object(); + if (json_is_invalid(&message_json)) { + return false; + } + + ret = json_add_string(&message_json, + "type", + msg_type); + if (ret != 0) { + json_free(&message_json); + return false; + } + + if (c->opt_witness_new_ip != NULL) { + ret = json_add_string(&message_json, + "new_ip", + c->opt_witness_new_ip); + if (ret != 0) { + return false; + } + } else if (c->opt_witness_new_node != -1) { + ret = json_add_int(&message_json, + "new_node", + c->opt_witness_new_node); + if (ret != 0) { + return false; + } + } else { + ret = json_add_bool(&message_json, + "all_nodes", + true); + if (ret != 0) { + return false; + } + } + + *pmessage_json = message_json; + return true; +} +#endif /* HAVE_JANSSON */ + +static void net_witness_client_move_usage(void) +{ + d_printf("%s\n" + "net witness client-move\n" + " %s\n\n", + _("Usage:"), + _("Generate client move notifications for " + "witness registrations to a new ip or node")); + net_witness_filter_usage(); + net_witness_update_usage(); + net_witness_move_usage("CLIENT_MOVE"); +} + +static int net_witness_client_move(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_client_move_state state = { .c = c, }; + struct rpcd_witness_registration_updateB *m = &state.m; +#ifdef HAVE_JANSSON + struct json_object _message_json = json_empty_object; +#endif /* HAVE_JANSSON */ + struct json_object *message_json = NULL; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_client_move_prepare_fn, + .match_fn = net_witness_client_move_match_fn, + .process_fn = net_witness_client_move_process_fn, + .private_data = &state, + }; + int ret = -1; + const char *msg_type = NULL; + uint32_t new_node = NONCLUSTER_VNN; + bool is_ipv4 = false; + bool is_ipv6 = false; + bool ok; + + if (c->display_usage) { + net_witness_client_move_usage(); + goto out; + } + + if (argc != 0) { + net_witness_client_move_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6); + if (!ok) { + goto out; + } + + if (is_ipv4) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV4; + m->update.client_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip; + msg_type = "CLIENT_MOVE_TO_IPV4"; + state.headline = talloc_asprintf(frame, + "CLIENT_MOVE_TO_IPV4: %s", + c->opt_witness_new_ip); + if (state.headline == NULL) { + goto out; + } + } else if (is_ipv6) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_IPV6; + m->update.client_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip; + msg_type = "CLIENT_MOVE_TO_IPV6"; + state.headline = talloc_asprintf(frame, + "CLIENT_MOVE_TO_IPV6: %s", + c->opt_witness_new_ip); + if (state.headline == NULL) { + goto out; + } + } else if (new_node != NONCLUSTER_VNN) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE; + m->update.client_move_to_node.new_node = new_node; + msg_type = "CLIENT_MOVE_TO_NODE"; + state.headline = talloc_asprintf(frame, + "CLIENT_MOVE_TO_NODE: %u", + new_node); + if (state.headline == NULL) { + goto out; + } + } else { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_CLIENT_MOVE_TO_NODE; + m->update.client_move_to_node.new_node = NONCLUSTER_VNN; + msg_type = "CLIENT_MOVE_TO_NODE"; + state.headline = talloc_asprintf(frame, + "CLIENT_MOVE_TO_NODE: ALL"); + if (state.headline == NULL) { + goto out; + } + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + TALLOC_FREE(state.headline); + + ok = net_witness_move_message_json(c, + msg_type, + &_message_json); + if (!ok) { + d_printf("net_witness_move_message_json(%s) failed\n", + msg_type); + goto out; + } + + message_json = &_message_json; + } +#else /* not HAVE_JANSSON */ + (void)msg_type; +#endif /* not HAVE_JANSSON */ + + ret = net_witness_scan_registrations(c, message_json, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: +#ifdef HAVE_JANSSON + if (!json_is_invalid(&_message_json)) { + json_free(&_message_json); + } +#endif /* HAVE_JANSSON */ + TALLOC_FREE(frame); + return ret; +} + +struct net_witness_share_move_state { + struct net_context *c; + struct rpcd_witness_registration_updateB m; + char *headline; +}; + +static bool net_witness_share_move_prepare_fn(void *private_data) +{ + struct net_witness_share_move_state *state = + (struct net_witness_share_move_state *)private_data; + + if (state->headline != NULL) { + d_printf("%s\n", state->headline); + TALLOC_FREE(state->headline); + } + + return true; +} + +static bool net_witness_share_move_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + if (rg->share_name == NULL) { + return false; + } + + return true; +} + +static NTSTATUS net_witness_share_move_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + struct net_witness_share_move_state *state = + (struct net_witness_share_move_state *)private_data; + struct net_context *c = state->c; + struct rpcd_witness_registration_updateB update = { + .context_handle = rg->context_handle, + .type = state->m.type, + .update = state->m.update, + }; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + NTSTATUS status; + + SMB_ASSERT(update.type != 0); + + if (DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update); + } + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update, + (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status)); + return status; + } + + status = messaging_send(c->msg_ctx, + rg->server_id, + MSG_RPCD_WITNESS_REGISTRATION_UPDATE, + &blob); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("messaging_send() - %s\n", nt_errstr(status)); + return status; + } + + return NT_STATUS_OK; +} + +static void net_witness_share_move_usage(void) +{ + d_printf("%s\n" + "net witness share-move\n" + " %s\n\n", + _("Usage:"), + _("Generate share move notifications for " + "witness registrations to a new ip or node")); + net_witness_filter_usage(); + net_witness_update_usage(); + d_printf(" Note: This only applies to registrations with " + "a non empty share name!\n\n"); + net_witness_move_usage("SHARE_MOVE"); +} + +static int net_witness_share_move(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_share_move_state state = { .c = c, }; + struct rpcd_witness_registration_updateB *m = &state.m; +#ifdef HAVE_JANSSON + struct json_object _message_json = json_empty_object; +#endif /* HAVE_JANSSON */ + struct json_object *message_json = NULL; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_share_move_prepare_fn, + .match_fn = net_witness_share_move_match_fn, + .process_fn = net_witness_share_move_process_fn, + .private_data = &state, + }; + int ret = -1; + const char *msg_type = NULL; + uint32_t new_node = NONCLUSTER_VNN; + bool is_ipv4 = false; + bool is_ipv6 = false; + bool ok; + + if (c->display_usage) { + net_witness_share_move_usage(); + goto out; + } + + if (argc != 0) { + net_witness_share_move_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ok = net_witness_verify_move_options(c, &new_node, &is_ipv4, &is_ipv6); + if (!ok) { + goto out; + } + + if (is_ipv4) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV4; + m->update.share_move_to_ipv4.new_ipv4 = c->opt_witness_new_ip; + msg_type = "SHARE_MOVE_TO_IPV4"; + state.headline = talloc_asprintf(frame, + "SHARE_MOVE_TO_IPV4: %s", + c->opt_witness_new_ip); + if (state.headline == NULL) { + goto out; + } + } else if (is_ipv6) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_IPV6; + m->update.share_move_to_ipv6.new_ipv6 = c->opt_witness_new_ip; + msg_type = "SHARE_MOVE_TO_IPV6"; + state.headline = talloc_asprintf(frame, + "SHARE_MOVE_TO_IPV6: %s", + c->opt_witness_new_ip); + if (state.headline == NULL) { + goto out; + } + } else if (new_node != NONCLUSTER_VNN) { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE; + m->update.share_move_to_node.new_node = new_node; + msg_type = "SHARE_MOVE_TO_NODE"; + state.headline = talloc_asprintf(frame, + "SHARE_MOVE_TO_NODE: %u", + new_node); + if (state.headline == NULL) { + goto out; + } + } else { + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_SHARE_MOVE_TO_NODE; + m->update.share_move_to_node.new_node = NONCLUSTER_VNN; + msg_type = "SHARE_MOVE_TO_NODE"; + state.headline = talloc_asprintf(frame, + "SHARE_MOVE_TO_NODE: ALL"); + if (state.headline == NULL) { + goto out; + } + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + TALLOC_FREE(state.headline); + + ok = net_witness_move_message_json(c, + msg_type, + &_message_json); + if (!ok) { + d_printf("net_witness_move_message_json(%s) failed\n", + msg_type); + goto out; + } + + message_json = &_message_json; + } +#else /* not HAVE_JANSSON */ + (void)msg_type; +#endif /* not HAVE_JANSSON */ + + ret = net_witness_scan_registrations(c, message_json, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: +#ifdef HAVE_JANSSON + if (!json_is_invalid(&_message_json)) { + json_free(&_message_json); + } +#endif /* HAVE_JANSSON */ + TALLOC_FREE(frame); + return ret; +} + +struct net_witness_force_unregister_state { + struct net_context *c; + struct rpcd_witness_registration_updateB m; + char *headline; +}; + +static bool net_witness_force_unregister_prepare_fn(void *private_data) +{ + struct net_witness_force_unregister_state *state = + (struct net_witness_force_unregister_state *)private_data; + + if (state->headline != NULL) { + d_printf("%s\n", state->headline); + TALLOC_FREE(state->headline); + } + + return true; +} + +static bool net_witness_force_unregister_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return true; +} + +static NTSTATUS net_witness_force_unregister_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + struct net_witness_force_unregister_state *state = + (struct net_witness_force_unregister_state *)private_data; + struct net_context *c = state->c; + struct rpcd_witness_registration_updateB update = { + .context_handle = rg->context_handle, + .type = state->m.type, + .update = state->m.update, + }; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + NTSTATUS status; + + SMB_ASSERT(update.type != 0); + + if (DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update); + } + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update, + (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status)); + return status; + } + + status = messaging_send(c->msg_ctx, + rg->server_id, + MSG_RPCD_WITNESS_REGISTRATION_UPDATE, + &blob); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("messaging_send() - %s\n", nt_errstr(status)); + return status; + } + + return NT_STATUS_OK; +} + +static void net_witness_force_unregister_usage(void) +{ + d_printf("%s\n" + "net witness force-unregister\n" + " %s\n\n", + _("Usage:"), + _("Force unregistrations for witness registrations")); + net_witness_filter_usage(); + net_witness_update_usage(); + d_printf(" The selected registrations are removed on " + "the server and\n" + " any pending AsyncNotify request will get " + "a NOT_FOUND error.\n" + "\n" + " Typically this triggers a clean re-registration " + "on the client.\n" + "\n"); +} + +static int net_witness_force_unregister(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_force_unregister_state state = { .c = c, }; + struct rpcd_witness_registration_updateB *m = &state.m; +#ifdef HAVE_JANSSON + struct json_object _message_json = json_empty_object; +#endif /* HAVE_JANSSON */ + struct json_object *message_json = NULL; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_force_unregister_prepare_fn, + .match_fn = net_witness_force_unregister_match_fn, + .process_fn = net_witness_force_unregister_process_fn, + .private_data = &state, + }; + int ret = -1; + bool ok; + + if (c->display_usage) { + net_witness_force_unregister_usage(); + goto out; + } + + if (argc != 0) { + net_witness_force_unregister_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ok = net_witness_verify_update_options(c); + if (!ok) { + goto out; + } + + m->type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_UNREGISTER; + + state.headline = talloc_asprintf(frame, "FORCE_UNREGISTER:"); + if (state.headline == NULL) { + goto out; + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + TALLOC_FREE(state.headline); + + _message_json = json_new_object(); + if (json_is_invalid(&_message_json)) { + goto out; + } + + ret = json_add_string(&_message_json, + "type", + "FORCE_UNREGISTER"); + if (ret != 0) { + goto out; + } + + message_json = &_message_json; + } +#endif /* HAVE_JANSSON */ + + ret = net_witness_scan_registrations(c, message_json, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: +#ifdef HAVE_JANSSON + if (!json_is_invalid(&_message_json)) { + json_free(&_message_json); + } +#endif /* HAVE_JANSSON */ + TALLOC_FREE(frame); + return ret; +} + +struct net_witness_force_response_state { + struct net_context *c; + struct rpcd_witness_registration_updateB m; +#ifdef HAVE_JANSSON + struct json_object json_root; +#endif /* HAVE_JANSSON */ + char *headline; +}; + +#ifdef HAVE_JANSSON +static NTSTATUS net_witness_force_response_parse_rc( + struct net_witness_force_response_state *state, + json_t *jsmsg, + TALLOC_CTX *mem_ctx, + size_t mi, + union witness_notifyResponse_message *message) +{ + struct witness_ResourceChange *rc = &message->resource_change; + json_t *jsctype = NULL; + json_int_t ctype; + json_t *jscname = NULL; + const char *cname = NULL; + + if (!json_is_object(jsmsg)) { + DBG_ERR("'message[%zu]' needs to be an object\n", mi); + return NT_STATUS_INVALID_PARAMETER; + } + + jsctype = json_object_get(jsmsg, "type"); + if (jsctype == NULL) { + DBG_ERR("%s: INVALID_PARAMETER\n", __location__); + return NT_STATUS_INVALID_PARAMETER; + } + if (!json_is_integer(jsctype)) { + DBG_ERR("%s: INVALID_PARAMETER\n", __location__); + return NT_STATUS_INVALID_PARAMETER; + } + ctype = json_integer_value(jsctype); + + jscname = json_object_get(jsmsg, "name"); + if (jscname == NULL) { + DBG_ERR("%s: INVALID_PARAMETER\n", __location__); + return NT_STATUS_INVALID_PARAMETER; + } + if (!json_is_string(jscname)) { + DBG_ERR("%s: INVALID_PARAMETER\n", __location__); + return NT_STATUS_INVALID_PARAMETER; + } + cname = json_string_value(jscname); + + rc->type = ctype; + rc->name = talloc_strdup(mem_ctx, cname); + if (rc->name == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +static NTSTATUS net_witness_force_response_parse_ipl( + struct net_witness_force_response_state *state, + json_t *jsmsg, + TALLOC_CTX *mem_ctx, + size_t mi, + union witness_notifyResponse_message *message) +{ + struct witness_IPaddrInfoList *ipl = + &message->client_move; + size_t ai, num_addrs = 0; + struct witness_IPaddrInfo *addrs = NULL; + + if (!json_is_array(jsmsg)) { + DBG_ERR("'messages[%zu]' needs to be an array\n", mi); + return NT_STATUS_INVALID_PARAMETER; + } + + num_addrs = json_array_size(jsmsg); + if (num_addrs > UINT32_MAX) { + DBG_ERR("Too many elements in 'messages[%zu]': %zu\n", + mi, num_addrs); + return NT_STATUS_INVALID_PARAMETER; + } + + addrs = talloc_zero_array(mem_ctx, + struct witness_IPaddrInfo, + num_addrs); + if (addrs == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (ai = 0; ai < num_addrs; ai++) { + struct witness_IPaddrInfo *info = + &addrs[ai]; + json_t *jsaddr = json_array_get(jsmsg, ai); + json_t *jsflags = NULL; + json_int_t flags; + json_t *jsipv4 = NULL; + const char *ipv4 = NULL; + json_t *jsipv6 = NULL; + const char *ipv6 = NULL; + + if (!json_is_object(jsaddr)) { + DBG_ERR("'messages[%zu][%zu]' needs to be an object\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + + jsflags = json_object_get(jsaddr, "flags"); + if (jsflags == NULL) { + DBG_ERR("'messages[%zu][%zu]['flags']' missing\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + if (!json_is_integer(jsflags)) { + DBG_ERR("'messages[%zu][%zu]['flags']' " + "needs to be an integer\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + flags = json_integer_value(jsflags); + + jsipv4 = json_object_get(jsaddr, "ipv4"); + if (jsipv4 != NULL) { + if (!json_is_string(jsipv4)) { + DBG_ERR("'messages[%zu][%zu]['ipv4']' " + "needs to be a string\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + ipv4 = json_string_value(jsipv4); + if (!is_ipaddress_v4(ipv4)) { + DBG_ERR("'messages[%zu][%zu]['ipv4']' " + "needs to be a valid ipv4 address\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + } else { + ipv4 = "0.0.0.0"; + } + + jsipv6 = json_object_get(jsaddr, "ipv6"); + if (jsipv6 != NULL) { + if (!json_is_string(jsipv6)) { + DBG_ERR("'messages[%zu][%zu]['ipv6']' " + "needs to be a string\n", + mi, ai); + DBG_ERR("%s: INVALID_PARAMETER\n", __location__); + return NT_STATUS_INVALID_PARAMETER; + } + ipv6 = json_string_value(jsipv6); + if (!is_ipaddress_v6(ipv6)) { + DBG_ERR("'messages[%zu][%zu]['ipv4']' " + "needs to be a valid ipv6 address\n", + mi, ai); + return NT_STATUS_INVALID_PARAMETER; + } + } else { + ipv6 = "::"; + } + + info->flags = flags; + info->ipv4 = talloc_strdup(addrs, ipv4); + if (info->ipv4 == NULL) { + return NT_STATUS_NO_MEMORY; + } + info->ipv6 = talloc_strdup(addrs, ipv6); + if (info->ipv6 == NULL) { + return NT_STATUS_NO_MEMORY; + } + } + + ipl->num = num_addrs; + ipl->addr = addrs; + + return NT_STATUS_OK; +} +#endif /* HAVE_JANSSON */ + +static NTSTATUS net_witness_force_response_parse(struct net_witness_force_response_state *state) +{ +#ifdef HAVE_JANSSON + struct net_context *c = state->c; + struct rpcd_witness_registration_update_force_response *force = NULL; + struct witness_notifyResponse *response = NULL; + size_t mi, num_messages = 0; + union witness_notifyResponse_message *messages = NULL; + json_t *jsroot = NULL; + json_t *jsresult = NULL; + json_t *jsresponse = NULL; + json_t *jstype = NULL; + json_t *jsmessages = NULL; + + if (c->opt_witness_forced_response != NULL) { + const char *str = c->opt_witness_forced_response; + size_t flags = JSON_REJECT_DUPLICATES; + json_error_t jserror; + + jsroot = json_loads(str, flags, &jserror); + if (jsroot == NULL) { + DBG_ERR("Invalid JSON in " + "--witness-forced-response='%s'\n", + str); + return NT_STATUS_INVALID_PARAMETER; + } + state->json_root = (struct json_object) { + .root = jsroot, + .valid = true, + }; + } + + state->m.type = RPCD_WITNESS_REGISTRATION_UPDATE_FORCE_RESPONSE; + force = &state->m.update.force_response; + force->response = NULL; + force->result = WERR_OK; + + if (jsroot == NULL) { + return NT_STATUS_OK; + } + + jsresult = json_object_get(jsroot, "result"); + if (jsresult != NULL) { + int val_type = json_typeof(jsresult); + + switch (val_type) { + case JSON_INTEGER: { + json_int_t val = json_integer_value(jsresult); + + if (val > UINT32_MAX) { + DBG_ERR("Invalid 'result' value: %d\n", + (int) val); + return NT_STATUS_INVALID_PARAMETER; + } + if (val < 0) { + DBG_ERR("invalid 'result' value: %d\n", + (int) val); + return NT_STATUS_INVALID_PARAMETER; + } + + force->result = W_ERROR(val); + }; break; + default: + DBG_ERR("Invalid json type for 'result' - needs integer\n"); + return NT_STATUS_INVALID_PARAMETER; + } + } + + jsresponse = json_object_get(jsroot, "response"); + if (jsresponse == NULL) { + return NT_STATUS_OK; + } + + if (!json_is_object(jsresponse)) { + DBG_ERR("Invalid json type 'response' needs object\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + response = talloc_zero(talloc_tos(), struct witness_notifyResponse); + if (response == NULL) { + return NT_STATUS_NO_MEMORY; + } + + jstype = json_object_get(jsresponse, "type"); + if (jstype == NULL) { + DBG_ERR("Missing 'type' element in 'response'\n"); + return NT_STATUS_INVALID_PARAMETER; + } + { + int val_type = json_typeof(jstype); + + switch (val_type) { + case JSON_INTEGER: { + json_int_t val = json_integer_value(jstype); + + if (val > WITNESS_NOTIFY_IP_CHANGE) { + DBG_ERR("invalid 'type' value in 'response': " + "%d\n", (int) val); + return NT_STATUS_INVALID_PARAMETER; + } + if (val < WITNESS_NOTIFY_RESOURCE_CHANGE) { + DBG_ERR("invalid 'type' value in 'response': " + "%d\n", (int) val); + return NT_STATUS_INVALID_PARAMETER; + } + + response->type = val; + }; break; + default: + DBG_ERR("Invalid json type for 'type' in 'response' " + "- needs integer\n"); + return NT_STATUS_INVALID_PARAMETER; + } + } + + force->response = response; + + jsmessages = json_object_get(jsresponse, "messages"); + if (jsmessages == NULL) { + return NT_STATUS_OK; + } + + if (!json_is_array(jsmessages)) { + DBG_ERR("'messages' in 'response' needs to be an array\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + num_messages = json_array_size(jsmessages); + if (num_messages > UINT32_MAX) { + DBG_ERR("Too many elements in 'messages': %zu\n", + num_messages); + return NT_STATUS_INVALID_PARAMETER; + } + + messages = talloc_zero_array(response, + union witness_notifyResponse_message, + num_messages); + if (messages == NULL) { + return NT_STATUS_NO_MEMORY; + } + + for (mi = 0; mi < num_messages; mi++) { + json_t *jsmsg = json_array_get(jsmessages, mi); + union witness_notifyResponse_message *message = &messages[mi]; + NTSTATUS status; + + switch (response->type) { + case WITNESS_NOTIFY_RESOURCE_CHANGE: + status = net_witness_force_response_parse_rc(state, + jsmsg, + messages, + mi, + message); + if (!NT_STATUS_IS_OK(status)) { + const char *fn = + "net_witness_force_response_parse_rc"; + DBG_ERR("%s failed: %s\n", + fn, nt_errstr(status)); + return status; + } + + break; + case WITNESS_NOTIFY_CLIENT_MOVE: + case WITNESS_NOTIFY_SHARE_MOVE: + case WITNESS_NOTIFY_IP_CHANGE: + status = net_witness_force_response_parse_ipl(state, + jsmsg, + messages, + mi, + message); + if (!NT_STATUS_IS_OK(status)) { + const char *fn = + "net_witness_force_response_parse_ipl"; + DBG_ERR("%s failed: %s\n", + fn, nt_errstr(status)); + return status; + } + + break; + } + } + + response->num = num_messages; + response->messages = messages; + + return NT_STATUS_OK; +#else /* not HAVE_JANSSON */ + d_fprintf(stderr, _("JSON support not available\n")); + return NT_STATUS_NOT_IMPLEMENTED; +#endif /* not HAVE_JANSSON */ +} + +static bool net_witness_force_response_prepare_fn(void *private_data) +{ + struct net_witness_force_response_state *state = + (struct net_witness_force_response_state *)private_data; + + if (state->headline != NULL) { + d_printf("%s\n", state->headline); + TALLOC_FREE(state->headline); + } + + return true; +} + +static bool net_witness_force_response_match_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + return true; +} + +static NTSTATUS net_witness_force_response_process_fn(void *private_data, + const struct rpcd_witness_registration *rg) +{ + struct net_witness_force_response_state *state = + (struct net_witness_force_response_state *)private_data; + struct net_context *c = state->c; + struct rpcd_witness_registration_updateB update = { + .context_handle = rg->context_handle, + .type = state->m.type, + .update = state->m.update, + }; + DATA_BLOB blob = { .length = 0, }; + enum ndr_err_code ndr_err; + NTSTATUS status; + + SMB_ASSERT(update.type != 0); + + if (DEBUGLVL(DBGLVL_DEBUG)) { + NDR_PRINT_DEBUG(rpcd_witness_registration_updateB, &update); + } + + ndr_err = ndr_push_struct_blob(&blob, talloc_tos(), &update, + (ndr_push_flags_fn_t)ndr_push_rpcd_witness_registration_updateB); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = ndr_map_error2ntstatus(ndr_err); + DBG_ERR("ndr_push_struct_blob - %s\n", nt_errstr(status)); + return status; + } + + status = messaging_send(c->msg_ctx, + rg->server_id, + MSG_RPCD_WITNESS_REGISTRATION_UPDATE, + &blob); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("messaging_send() - %s\n", nt_errstr(status)); + return status; + } + + return NT_STATUS_OK; +} + +static void net_witness_force_response_usage(void) +{ + d_printf("%s\n" + "net witness force-response\n" + " %s\n\n", + _("Usage:"), + _("Force an AsyncNotify response based on " + "json input (mostly for testing)")); + net_witness_filter_usage(); + net_witness_update_usage(); + d_printf(" Note this is designed for testing and debugging!\n" + "\n" + " In short it is not designed to be used by " + "administrators,\n" + " but developers and automated tests.\n" + "\n" + " By default an empty response with WERR_OK is generated,\n" + " but basically any valid response can be specified by a\n" + " specifying a JSON string:\n" + "\n" + " --witness-forced-response=JSON\n" + " This allows the generation of very complex\n" + " witness_notifyResponse structures.\n" + "\n" + " As this is for developers, please read the code\n" + " in order to understand all possible values\n" + " of the JSON string format...\n" + "\n" + " Simple examples are:\n" + "\n" + "# Resource Change:\n%s\n" + "\n" + "# Client Move:\n%s\n" + "\n" + "# Share Move:\n%s\n" + "\n" + "# IP Change:\n%s\n" + "\n", + "'{ \"result\": 0, \"response\": { \"type\": 1, " + "\"messages\": [ { " + "\"type\": 255 , " + "\"name\": \"some-resource-name\" " + "} ]" + "}}'", + "'{ \"result\": 0, \"response\": { \"type\": 2, " + "\"messages\": [" + "[{ " + "\"flags\": 9, " + "\"ipv4\": \"10.0.10.1\" " + "}]" + "]" + "}}'", + "'{ \"result\": 0, \"response\": { \"type\": 3, " + "\"messages\": [" + "[{ " + "\"flags\": 9, " + "\"ipv4\": \"10.0.10.1\" " + "}]" + "]" + "}}'", + "'{ \"result\": 0, \"response\": { \"type\": 4, " + "\"messages\": [" + "[{ " + "\"flags\": 9, " + "\"ipv4\": \"10.0.10.1\" " + "}]" + "]" + "}}'"); +} + +static int net_witness_force_response(struct net_context *c, int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct net_witness_force_response_state state = { .c = c, }; +#ifdef HAVE_JANSSON + struct json_object _message_json = json_empty_object; +#endif /* HAVE_JANSSON */ + struct json_object *message_json = NULL; + struct net_witness_scan_registrations_action_state action = { + .prepare_fn = net_witness_force_response_prepare_fn, + .match_fn = net_witness_force_response_match_fn, + .process_fn = net_witness_force_response_process_fn, + .private_data = &state, + }; + NTSTATUS status; + int ret = -1; + bool ok; + + if (c->display_usage) { + net_witness_force_response_usage(); + goto out; + } + + if (argc != 0) { + net_witness_force_response_usage(); + goto out; + } + + if (!lp_clustering()) { + d_printf("ERROR: Only supported with clustering=yes!\n\n"); + goto out; + } + + ok = net_witness_verify_update_options(c); + if (!ok) { + goto out; + } + + status = net_witness_force_response_parse(&state); + if (!NT_STATUS_IS_OK(status)) { + d_printf("net_witness_force_response_parse failed: %s\n", + nt_errstr(status)); + goto out; + } + + state.headline = talloc_asprintf(frame, "FORCE_RESPONSE:%s%s", + c->opt_witness_forced_response != NULL ? + " " : "", + c->opt_witness_forced_response != NULL ? + c->opt_witness_forced_response : ""); + + if (state.headline == NULL) { + goto out; + } + +#ifdef HAVE_JANSSON + if (c->opt_json) { + TALLOC_FREE(state.headline); + + _message_json = json_new_object(); + if (json_is_invalid(&_message_json)) { + goto out; + } + + ret = json_add_string(&_message_json, + "type", + "FORCE_RESPONSE"); + if (ret != 0) { + goto out; + } + + if (!json_is_invalid(&state.json_root)) { + ret = json_add_object(&_message_json, + "json", + &state.json_root); + if (ret != 0) { + goto out; + } + state.json_root = json_empty_object; + } + message_json = &_message_json; + } +#endif /* HAVE_JANSSON */ + + ret = net_witness_scan_registrations(c, message_json, &action); + if (ret != 0) { + d_printf("net_witness_scan_registrations() failed\n"); + goto out; + } + + ret = 0; +out: +#ifdef HAVE_JANSSON + if (!json_is_invalid(&_message_json)) { + json_free(&_message_json); + } + if (!json_is_invalid(&state.json_root)) { + json_free(&state.json_root); + } +#endif /* HAVE_JANSSON */ + TALLOC_FREE(frame); + return ret; +} + +int net_witness(struct net_context *c, int argc, const char **argv) +{ + struct functable func[] = { + { + "list", + net_witness_list, + NET_TRANSPORT_LOCAL, + N_("List witness registrations " + "from rpcd_witness_registration.tdb"), + N_("net witness list\n" + " List witness registrations " + "from rpcd_witness_registration.tdb"), + }, + { + "client-move", + net_witness_client_move, + NET_TRANSPORT_LOCAL, + N_("Generate client move notifications for " + "witness registrations to a new ip or node"), + N_("net witness client-move\n" + " Generate client move notifications for " + "witness registrations to a new ip or node"), + }, + { + "share-move", + net_witness_share_move, + NET_TRANSPORT_LOCAL, + N_("Generate share move notifications for " + "witness registrations to a new ip or node"), + N_("net witness share-move\n" + " Generate share move notifications for " + "witness registrations to a new ip or node"), + }, + { + "force-unregister", + net_witness_force_unregister, + NET_TRANSPORT_LOCAL, + N_("Force unregistrations for witness registrations"), + N_("net witness force-unregister\n" + " Force unregistrations for " + "witness registrations"), + }, + { + "force-response", + net_witness_force_response, + NET_TRANSPORT_LOCAL, + N_("Force an AsyncNotify response based on " + "json input (mostly for testing)"), + N_("net witness force-response\n" + " Force an AsyncNotify response based on " + "json input (mostly for testing)"), + }, + {NULL, NULL, 0, NULL, NULL} + }; + + return net_run_function(c, argc, argv, "net witness", func); +} diff --git a/source3/utils/netlookup.c b/source3/utils/netlookup.c new file mode 100644 index 0000000..aaf78b0 --- /dev/null +++ b/source3/utils/netlookup.c @@ -0,0 +1,218 @@ +/* + Unix SMB/CIFS implementation. + + Name lookup. + + Copyright (C) Jeremy Allison 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/net.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_lsa.h" +#include "rpc_client/cli_lsarpc.h" +#include "libsmb/libsmb.h" + +/******************************************************** + Connection cachine struct. Goes away when ctx destroyed. +********************************************************/ + +struct con_struct { + bool failed_connect; + NTSTATUS err; + struct cli_state *cli; + struct rpc_pipe_client *lsapipe; + struct policy_handle pol; +}; + +static struct con_struct *cs; + +/******************************************************** + Close connection on context destruction. +********************************************************/ + +static int cs_destructor(struct con_struct *p) +{ + if (cs->cli) { + cli_shutdown(cs->cli); + } + cs = NULL; + return 0; +} + +/******************************************************** + Create the connection to localhost. +********************************************************/ + +static struct con_struct *create_cs(struct net_context *c, + TALLOC_CTX *ctx, NTSTATUS *perr) +{ + NTSTATUS nt_status; + struct sockaddr_storage loopback_ss; + struct cli_credentials *anon_creds = NULL; + + *perr = NT_STATUS_OK; + + if (!interpret_string_addr(&loopback_ss, "127.0.0.1", AI_NUMERICHOST)) { + *perr = NT_STATUS_INVALID_PARAMETER; + return NULL; + } + + if (cs) { + if (cs->failed_connect) { + *perr = cs->err; + return NULL; + } + return cs; + } + + cs = talloc(ctx, struct con_struct); + if (!cs) { + *perr = NT_STATUS_NO_MEMORY; + return NULL; + } + + anon_creds = cli_credentials_init_anon(cs); + if (anon_creds == NULL) { + TALLOC_FREE(cs); + *perr = NT_STATUS_NO_MEMORY; + return NULL; + } + + ZERO_STRUCTP(cs); + talloc_set_destructor(cs, cs_destructor); + + nt_status = cli_full_connection_creds(&cs->cli, lp_netbios_name(), lp_netbios_name(), + &loopback_ss, 0, + "IPC$", "IPC", + anon_creds, + CLI_FULL_CONNECTION_IPC); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(2,("create_cs: Connect failed. Error was %s\n", nt_errstr(nt_status))); + cs->failed_connect = true; + cs->err = nt_status; + *perr = nt_status; + return NULL; + } + + nt_status = cli_rpc_pipe_open_noauth(cs->cli, + &ndr_table_lsarpc, + &cs->lsapipe); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(2,("create_cs: open LSA pipe failed. Error was %s\n", nt_errstr(nt_status))); + cs->failed_connect = true; + cs->err = nt_status; + *perr = nt_status; + return NULL; + } + + nt_status = rpccli_lsa_open_policy(cs->lsapipe, ctx, true, + SEC_FLAG_MAXIMUM_ALLOWED, + &cs->pol); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(2,("create_cs: rpccli_lsa_open_policy failed. Error was %s\n", nt_errstr(nt_status))); + cs->failed_connect = true; + cs->err = nt_status; + *perr = nt_status; + return NULL; + } + + return cs; +} + +/******************************************************** + Do a lookup_sids call to localhost. + Check if the local machine is authoritative for this sid. We can't + check if this is our SID as that's stored in the root-read-only + secrets.tdb. + The local smbd will also ask winbindd for us, so we don't have to. +********************************************************/ + +NTSTATUS net_lookup_name_from_sid(struct net_context *c, + TALLOC_CTX *ctx, + struct dom_sid *psid, + const char **ppdomain, + const char **ppname) +{ + NTSTATUS nt_status; + struct con_struct *csp = NULL; + char **domains; + char **names; + enum lsa_SidType *types; + + *ppdomain = NULL; + *ppname = NULL; + + csp = create_cs(c, ctx, &nt_status); + if (csp == NULL) { + return nt_status; + } + + nt_status = rpccli_lsa_lookup_sids(csp->lsapipe, ctx, + &csp->pol, + 1, psid, + &domains, + &names, + &types); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + *ppdomain = domains[0]; + *ppname = names[0]; + /* Don't care about type here. */ + + /* Converted OK */ + return NT_STATUS_OK; +} + +/******************************************************** + Do a lookup_names call to localhost. +********************************************************/ + +NTSTATUS net_lookup_sid_from_name(struct net_context *c, TALLOC_CTX *ctx, + const char *full_name, struct dom_sid *pret_sid) +{ + NTSTATUS nt_status; + struct con_struct *csp = NULL; + struct dom_sid *sids = NULL; + enum lsa_SidType *types = NULL; + + csp = create_cs(c, ctx, &nt_status); + if (csp == NULL) { + return nt_status; + } + + nt_status = rpccli_lsa_lookup_names(csp->lsapipe, ctx, + &csp->pol, + 1, + &full_name, + NULL, 1, + &sids, &types); + + if (!NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } + + *pret_sid = sids[0]; + + /* Converted OK */ + return NT_STATUS_OK; +} diff --git a/source3/utils/nmblookup.c b/source3/utils/nmblookup.c new file mode 100644 index 0000000..9523f71 --- /dev/null +++ b/source3/utils/nmblookup.c @@ -0,0 +1,468 @@ +/* + Unix SMB/CIFS implementation. + NBT client - used to lookup netbios names + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jelmer Vernooij 2003 (Conversion to popt) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "libsmb/nmblib.h" +#include "libsmb/namequery.h" +#include "lib/util/string_wrappers.h" + +static bool give_flags = false; +static bool use_bcast = true; +static bool got_bcast = false; +static struct sockaddr_storage bcast_addr; +static bool recursion_desired = false; +static bool translate_addresses = false; +static int ServerFD= -1; +static bool RootPort = false; +static bool find_status = false; + +/**************************************************************************** + Open the socket communication. +**************************************************************************/ + +static bool open_sockets(void) +{ + struct sockaddr_storage ss; + const char *sock_addr = lp_nbt_client_socket_address(); + + if (!interpret_string_addr(&ss, sock_addr, + AI_NUMERICHOST|AI_PASSIVE)) { + DEBUG(0,("open_sockets: unable to get socket address " + "from string %s\n", sock_addr)); + return false; + } + ServerFD = open_socket_in( + SOCK_DGRAM, &ss, (RootPort ? 137 : 0), true); + if (ServerFD < 0) { + if (RootPort) { + DBG_ERR("open_socket_in failed: %s\n", + strerror(-ServerFD)); + } else { + DBG_NOTICE("open_socket_in failed: %s\n", + strerror(-ServerFD)); + } + return false; + } + + set_socket_options( ServerFD, "SO_BROADCAST" ); + + DEBUG(3, ("Socket opened.\n")); + return true; +} + +/**************************************************************************** +turn a node status flags field into a string +****************************************************************************/ +static char *node_status_flags(unsigned char flags) +{ + static fstring ret; + fstrcpy(ret,""); + + fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " "); + if ((flags & 0x60) == 0x00) fstrcat(ret,"B "); + if ((flags & 0x60) == 0x20) fstrcat(ret,"P "); + if ((flags & 0x60) == 0x40) fstrcat(ret,"M "); + if ((flags & 0x60) == 0x60) fstrcat(ret,"H "); + if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> "); + if (flags & 0x08) fstrcat(ret,"<CONFLICT> "); + if (flags & 0x04) fstrcat(ret,"<ACTIVE> "); + if (flags & 0x02) fstrcat(ret,"<PERMANENT> "); + + return ret; +} + +/**************************************************************************** + Turn the NMB Query flags into a string. +****************************************************************************/ + +static char *query_flags(int flags) +{ + static fstring ret1; + fstrcpy(ret1, ""); + + if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response "); + if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative "); + if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated "); + if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired "); + if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available "); + if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast "); + + return ret1; +} + +/**************************************************************************** + Do a node status query. +****************************************************************************/ + +static bool do_node_status(const char *name, + int type, + struct sockaddr_storage *pss) +{ + struct nmb_name nname; + size_t count = 0; + size_t i, j; + struct node_status *addrs; + struct node_status_extra extra; + fstring cleanname; + char addr[INET6_ADDRSTRLEN]; + NTSTATUS status; + + print_sockaddr(addr, sizeof(addr), pss); + d_printf("Looking up status of %s\n",addr); + make_nmb_name(&nname, name, type); + status = node_status_query(talloc_tos(), &nname, pss, + &addrs, &count, &extra); + if (NT_STATUS_IS_OK(status)) { + for (i=0;i<count;i++) { + pull_ascii_fstring(cleanname, addrs[i].name); + for (j=0;cleanname[j];j++) { + if (!isprint((int)cleanname[j])) { + cleanname[j] = '.'; + } + } + d_printf("\t%-15s <%02x> - %s\n", + cleanname,addrs[i].type, + node_status_flags(addrs[i].flags)); + } + d_printf("\n\tMAC Address = %02X-%02X-%02X-%02X-%02X-%02X\n", + extra.mac_addr[0], extra.mac_addr[1], + extra.mac_addr[2], extra.mac_addr[3], + extra.mac_addr[4], extra.mac_addr[5]); + d_printf("\n"); + TALLOC_FREE(addrs); + return true; + } else { + d_printf("No reply from %s\n\n",addr); + return false; + } +} + + +/**************************************************************************** + Send out one query. +****************************************************************************/ + +static bool query_one(const char *lookup, unsigned int lookup_type) +{ + size_t j, count = 0; + uint8_t flags = 0; + struct sockaddr_storage *ip_list=NULL; + NTSTATUS status = NT_STATUS_NOT_FOUND; + + if (got_bcast) { + char addr[INET6_ADDRSTRLEN]; + print_sockaddr(addr, sizeof(addr), &bcast_addr); + d_printf("querying %s on %s\n", lookup, addr); + status = name_query(lookup,lookup_type,use_bcast, + use_bcast?true:recursion_desired, + &bcast_addr, talloc_tos(), + &ip_list, &count, &flags); + } else { + status = name_resolve_bcast(talloc_tos(), + lookup, + lookup_type, + &ip_list, + &count); + } + + if (!NT_STATUS_IS_OK(status)) { + return false; + } + + if (give_flags) { + d_printf("Flags: %s\n", query_flags(flags)); + } + + for (j=0;j<count;j++) { + char addr[INET6_ADDRSTRLEN]; + if (translate_addresses) { + char h_name[MAX_DNS_NAME_LENGTH]; + h_name[0] = '\0'; + if (sys_getnameinfo((const struct sockaddr *)&ip_list[j], + sizeof(struct sockaddr_storage), + h_name, sizeof(h_name), + NULL, 0, + NI_NAMEREQD)) { + continue; + } + d_printf("%s, ", h_name); + } + print_sockaddr(addr, sizeof(addr), &ip_list[j]); + d_printf("%s %s<%02x>\n", addr,lookup, lookup_type); + /* We can only do find_status if the ip address returned + was valid - ie. name_query returned true. + */ + if (find_status) { + if (!do_node_status(lookup, lookup_type, &ip_list[j])) { + status = NT_STATUS_UNSUCCESSFUL; + } + } + } + + TALLOC_FREE(ip_list); + + return NT_STATUS_IS_OK(status); +} + + +/**************************************************************************** + main program +****************************************************************************/ +enum nmblookup_cmdline_options { + CMDLINE_RECURSIVE = 1, +}; + +int main(int argc, const char *argv[]) +{ + int opt; + unsigned int lookup_type = 0x0; + fstring lookup; + static bool find_master=False; + static bool lookup_by_ip = False; + poptContext pc = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + int rc = 0; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "broadcast", + .shortName = 'B', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'B', + .descrip = "Specify address to use for broadcasts", + .argDescrip = "BROADCAST-ADDRESS", + }, + { + .longName = "flags", + .shortName = 'f', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'f', + .descrip = "List the NMB flags returned", + }, + { + .longName = "unicast", + .shortName = 'U', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'U', + .descrip = "Specify address to use for unicast", + }, + { + .longName = "master-browser", + .shortName = 'M', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'M', + .descrip = "Search for a master browser", + }, + { + .longName = "recursion", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = CMDLINE_RECURSIVE, + .descrip = "Set recursion desired in package", + }, + { + .longName = "status", + .shortName = 'S', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'S', + .descrip = "Lookup node status as well", + }, + { + .longName = "translate", + .shortName = 'T', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'T', + .descrip = "Translate IP addresses into names", + }, + { + .longName = "root-port", + .shortName = 'r', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'r', + .descrip = "Use root port 137 (Win95 only replies to this)", + }, + { + .longName = "lookup-by-ip", + .shortName = 'A', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'A', + .descrip = "Do a node status on <name> as an IP Address", + }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + *lookup = 0; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "<NODE> ..."); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'f': + give_flags = true; + break; + case 'M': + find_master = true; + break; + case CMDLINE_RECURSIVE: + recursion_desired = true; + break; + case 'S': + find_status = true; + break; + case 'r': + RootPort = true; + break; + case 'A': + lookup_by_ip = true; + break; + case 'B': + if (interpret_string_addr(&bcast_addr, + poptGetOptArg(pc), + NI_NUMERICHOST)) { + got_bcast = True; + use_bcast = True; + } + break; + case 'U': + if (interpret_string_addr(&bcast_addr, + poptGetOptArg(pc), + 0)) { + got_bcast = True; + use_bcast = False; + } + break; + case 'T': + translate_addresses = !translate_addresses; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptGetArg(pc); /* Remove argv[0] */ + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + rc = 1; + goto out; + } + + if (!open_sockets()) { + rc = 1; + goto out; + } + + while(poptPeekArg(pc)) { + char *p; + struct in_addr ip; + size_t nbt_len; + + fstrcpy(lookup,poptGetArg(pc)); + + if(lookup_by_ip) { + struct sockaddr_storage ss; + ip = interpret_addr2(lookup); + in_addr_to_sockaddr_storage(&ss, ip); + fstrcpy(lookup,"*"); + if (!do_node_status(lookup, lookup_type, &ss)) { + rc = 1; + } + continue; + } + + if (find_master) { + if (*lookup == '-') { + fstrcpy(lookup,"\01\02__MSBROWSE__\02"); + lookup_type = 1; + } else { + lookup_type = 0x1d; + } + } + + p = strchr_m(lookup,'#'); + if (p) { + *p = '\0'; + sscanf(++p,"%x",&lookup_type); + } + + nbt_len = strlen(lookup); + if (nbt_len > MAX_NETBIOSNAME_LEN - 1) { + d_printf("The specified netbios name [%s] is too long!\n", + lookup); + continue; + } + + + if (!query_one(lookup, lookup_type)) { + rc = 1; + d_printf( "name_query failed to find name %s", lookup ); + if( 0 != lookup_type ) { + d_printf( "#%02x", lookup_type ); + } + d_printf( "\n" ); + } + } + +out: + poptFreeContext(pc); + TALLOC_FREE(frame); + return rc; +} diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c new file mode 100644 index 0000000..6660a31 --- /dev/null +++ b/source3/utils/ntlm_auth.c @@ -0,0 +1,2856 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 + Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 + Copyright (C) Robert O'Callahan 2006 (added cached credential code). + Copyright (C) Kai Blin <kai@samba.org> 2008 + Copyright (C) Simo Sorce 2010 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/param/param.h" +#include "lib/cmdline/cmdline.h" +#include "libcli/security/security.h" +#include "utils/ntlm_auth.h" +#include "../libcli/auth/libcli_auth.h" +#include "auth/ntlmssp/ntlmssp.h" +#include "auth/gensec/gensec.h" +#include "auth/gensec/gensec_internal.h" +#include "auth/credentials/credentials.h" +#include "librpc/crypto/gse.h" +#include "smb_krb5.h" +#include "lib/util/tiniparser.h" +#include "librpc/gen_ndr/krb5pac.h" +#include "auth/common_auth.h" +#include "source3/include/auth.h" +#include "source3/auth/proto.h" +#include "nsswitch/libwbclient/wbclient.h" +#include "nsswitch/winbind_struct_protocol.h" +#include "nsswitch/libwbclient/wbclient_internal.h" +#include "lib/param/loadparm.h" +#include "lib/util/base64.h" +#include "cmdline_contexts.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/util/string_wrappers.h" + +#include <gnutls/gnutls.h> +#include <gnutls/crypto.h> + +#ifdef HAVE_KRB5 +#include "auth/kerberos/pac_utils.h" +#endif + +#ifndef PAM_WINBIND_CONFIG_FILE +#define PAM_WINBIND_CONFIG_FILE "/etc/security/pam_winbind.conf" +#endif + +#define WINBIND_KRB5_AUTH 0x00000080 + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define INITIAL_BUFFER_SIZE 300 +#define MAX_BUFFER_SIZE 630000 + +enum stdio_helper_mode { + SQUID_2_4_BASIC, + SQUID_2_5_BASIC, + SQUID_2_5_NTLMSSP, + NTLMSSP_CLIENT_1, + GSS_SPNEGO_SERVER, + GSS_SPNEGO_CLIENT, + NTLM_SERVER_1, + NTLM_CHANGE_PASSWORD_1, + NUM_HELPER_MODES +}; + +enum ntlm_auth_cli_state { + CLIENT_INITIAL = 0, + CLIENT_RESPONSE, + CLIENT_FINISHED, + CLIENT_ERROR +}; + +struct ntlm_auth_state { + TALLOC_CTX *mem_ctx; + enum stdio_helper_mode helper_mode; + enum ntlm_auth_cli_state cli_state; + struct ntlmssp_state *ntlmssp_state; + uint32_t neg_flags; + char *want_feature_list; + bool have_session_key; + DATA_BLOB session_key; + DATA_BLOB initial_message; + void *gensec_private_1; +}; +typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, char *buf, + int length, void **private2); + +static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + char *buf, int length, void **private1); + +static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + stdio_helper_function fn, void **private2); + +static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_ntlm_server_1_request (enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2); + +static const struct { + enum stdio_helper_mode mode; + const char *name; + stdio_helper_function fn; +} stdio_helper_protocols[] = { + { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, + { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, + { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request}, + { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request}, + { GSS_SPNEGO_SERVER, "gss-spnego", manage_gss_spnego_request}, + { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request}, + { NTLM_SERVER_1, "ntlm-server-1", manage_ntlm_server_1_request}, + { NTLM_CHANGE_PASSWORD_1, "ntlm-change-password-1", manage_ntlm_change_password_1_request}, + { NUM_HELPER_MODES, NULL, NULL} +}; + +const char *opt_username; +const char *opt_domain; +const char *opt_workstation; +const char *opt_password; +static DATA_BLOB opt_challenge; +static DATA_BLOB opt_lm_response; +static DATA_BLOB opt_nt_response; +static int request_lm_key; +static int request_user_session_key; +static int use_cached_creds; +static int offline_logon; +static int opt_allow_mschapv2; + +static const char *require_membership_of; +static const char *require_membership_of_sid; +static const char *opt_pam_winbind_conf; + +const char *opt_target_service; +const char *opt_target_hostname; + + +/* This is a bit hairy, but the basic idea is to do a password callback + to the calling application. The callback comes from within gensec */ + +static void manage_gensec_get_pw_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, char *buf, int length, + void **password) +{ + DATA_BLOB in; + if (strlen(buf) < 2) { + DEBUG(1, ("query [%s] invalid\n", buf)); + printf("BH Query invalid\n"); + return; + } + + if (strlen(buf) > 3) { + in = base64_decode_data_blob(buf + 3); + } else { + in = data_blob(NULL, 0); + } + + if (strncmp(buf, "PW ", 3) == 0) { + + *password = talloc_strndup(NULL, + (const char *)in.data, in.length); + + if (*password == NULL) { + DEBUG(1, ("Out of memory\n")); + printf("BH Out of memory\n"); + data_blob_free(&in); + return; + } + + printf("OK\n"); + data_blob_free(&in); + return; + } + DEBUG(1, ("Asked for (and expected) a password\n")); + printf("BH Expected a password\n"); + data_blob_free(&in); +} + +/** + * Callback for password credentials. This is not async, and when + * GENSEC and the credentials code is made async, it will look rather + * different. + */ + +static const char *get_password(struct cli_credentials *credentials) +{ + TALLOC_CTX *frame = talloc_stackframe(); + char *password = NULL; + struct ntlm_auth_state *state; + + state = talloc_zero(frame, struct ntlm_auth_state); + if (state == NULL) { + DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n")); + fprintf(stderr, "ERR\n"); + exit(1); + } + + state->mem_ctx = state; + + /* Ask for a password */ + printf("PW\n"); + + manage_squid_request(NUM_HELPER_MODES /* bogus */, NULL, state, manage_gensec_get_pw_request, (void **)&password); + talloc_steal(credentials, password); + TALLOC_FREE(frame); + return password; +} + +/** + * A limited set of features are defined with text strings as needed + * by ntlm_auth + * + */ +static void gensec_want_feature_list(struct gensec_security *state, char* feature_list) +{ + if (in_list("NTLMSSP_FEATURE_SESSION_KEY", feature_list, true)) { + DEBUG(10, ("want GENSEC_FEATURE_SESSION_KEY\n")); + gensec_want_feature(state, GENSEC_FEATURE_SESSION_KEY); + } + if (in_list("NTLMSSP_FEATURE_SIGN", feature_list, true)) { + DEBUG(10, ("want GENSEC_FEATURE_SIGN\n")); + gensec_want_feature(state, GENSEC_FEATURE_SIGN); + } + if (in_list("NTLMSSP_FEATURE_SEAL", feature_list, true)) { + DEBUG(10, ("want GENSEC_FEATURE_SEAL\n")); + gensec_want_feature(state, GENSEC_FEATURE_SEAL); + } + if (in_list("NTLMSSP_FEATURE_CCACHE", feature_list, true)) { + DEBUG(10, ("want GENSEC_FEATURE_NTLM_CCACHE\n")); + gensec_want_feature(state, GENSEC_FEATURE_NTLM_CCACHE); + } +} + +static char winbind_separator(void) +{ + struct wbcInterfaceDetails *details; + wbcErr ret; + static bool got_sep; + static char sep; + + if (got_sep) + return sep; + + ret = wbcInterfaceDetails(&details); + if (!WBC_ERROR_IS_OK(ret)) { + d_fprintf(stderr, "could not obtain winbind separator!\n"); + return *lp_winbind_separator(); + } + + sep = details->winbind_separator; + + wbcFreeMemory(details); + + got_sep = True; + + if (!sep) { + d_fprintf(stderr, "winbind separator was NULL!\n"); + return *lp_winbind_separator(); + } + + return sep; +} + +const char *get_winbind_domain(void) +{ + struct wbcInterfaceDetails *details; + wbcErr ret; + + static fstring winbind_domain; + if (*winbind_domain) { + return winbind_domain; + } + + /* Send off request */ + + ret = wbcInterfaceDetails(&details); + if (!WBC_ERROR_IS_OK(ret)) { + DEBUG(1, ("could not obtain winbind domain name!\n")); + return lp_workgroup(); + } + + fstrcpy(winbind_domain, details->netbios_domain); + + wbcFreeMemory(details); + + return winbind_domain; + +} + +const char *get_winbind_netbios_name(void) +{ + struct wbcInterfaceDetails *details; + wbcErr ret; + + static fstring winbind_netbios_name; + + if (*winbind_netbios_name) { + return winbind_netbios_name; + } + + /* Send off request */ + + ret = wbcInterfaceDetails(&details); + if (!WBC_ERROR_IS_OK(ret)) { + DEBUG(1, ("could not obtain winbind netbios name!\n")); + return lp_netbios_name(); + } + + fstrcpy(winbind_netbios_name, details->netbios_name); + + wbcFreeMemory(details); + + return winbind_netbios_name; + +} + +DATA_BLOB get_challenge(void) +{ + static DATA_BLOB chal; + if (opt_challenge.length) + return opt_challenge; + + chal = data_blob(NULL, 8); + + generate_random_buffer(chal.data, chal.length); + return chal; +} + +/* Copy of parse_domain_user from winbindd_util.c. Parse a string of the + form DOMAIN/user into a domain and a user */ + +static bool parse_ntlm_auth_domain_user(const char *domuser, fstring domain, + fstring user) +{ + + char *p = strchr(domuser,winbind_separator()); + + if (!p) { + return False; + } + + fstrcpy(user, p+1); + fstrcpy(domain, domuser); + domain[PTR_DIFF(p, domuser)] = 0; + return strupper_m(domain); +} + +static bool get_require_membership_sid(void) { + fstring domain, name, sidbuf; + struct wbcDomainSid sid; + enum wbcSidType type; + wbcErr ret; + + if (!require_membership_of) { + return True; + } + + if (require_membership_of_sid) { + return True; + } + + /* Otherwise, ask winbindd for the name->sid request */ + + if (!parse_ntlm_auth_domain_user(require_membership_of, + domain, name)) { + DEBUG(0, ("Could not parse %s into separate domain/name parts!\n", + require_membership_of)); + return False; + } + + ret = wbcLookupName(domain, name, &sid, &type); + if (!WBC_ERROR_IS_OK(ret)) { + DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n", + require_membership_of)); + return False; + } + + wbcSidToStringBuf(&sid, sidbuf, sizeof(sidbuf)); + + require_membership_of_sid = SMB_STRDUP(sidbuf); + + if (require_membership_of_sid) + return True; + + return False; +} + +/* + * Get some configuration from pam_winbind.conf to see if we + * need to contact trusted domain + */ + +int get_pam_winbind_config(void) +{ + int ctrl = 0; + struct tiniparser_dictionary *d = NULL; + + if (!opt_pam_winbind_conf || !*opt_pam_winbind_conf) { + opt_pam_winbind_conf = PAM_WINBIND_CONFIG_FILE; + } + + d = tiniparser_load(opt_pam_winbind_conf); + + if (!d) { + return 0; + } + + if (tiniparser_getboolean(d, "global:krb5_auth", false)) { + ctrl |= WINBIND_KRB5_AUTH; + } + + tiniparser_freedict(d); + + return ctrl; +} + +/* Authenticate a user with a plaintext password */ + +static bool check_plaintext_auth(const char *user, const char *pass, + bool stdout_diagnostics) +{ + struct winbindd_request request; + struct winbindd_response response; + wbcErr ret; + + if (!get_require_membership_sid()) { + return False; + } + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.auth.user, user); + fstrcpy(request.data.auth.pass, pass); + if (require_membership_of_sid) { + strlcpy(request.data.auth.require_membership_of_sid, + require_membership_of_sid, + sizeof(request.data.auth.require_membership_of_sid)); + } + + if (offline_logon) { + request.flags |= WBFLAG_PAM_CACHED_LOGIN; + } + + ret = wbcRequestResponse(NULL, WINBINDD_PAM_AUTH, + &request, &response); + + /* Display response */ + + if (stdout_diagnostics) { + if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) { + d_fprintf(stderr, "Reading winbind reply failed! (0x01)\n"); + } + + d_printf("%s: %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.error_string, + response.data.auth.nt_status); + } else { + if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) { + DEBUG(1, ("Reading winbind reply failed! (0x01)\n")); + } + + DEBUG(3, ("%s: %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.error_string, + response.data.auth.nt_status)); + } + + return WBC_ERROR_IS_OK(ret); +} + +/* authenticate a user with an encrypted username/password */ + +NTSTATUS contact_winbind_auth_crap(const char *username, + const char *domain, + const char *workstation, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + uint32_t flags, + uint32_t extra_logon_parameters, + uint8_t lm_key[8], + uint8_t user_session_key[16], + uint8_t *pauthoritative, + char **error_string, + char **unix_name) +{ + NTSTATUS nt_status; + wbcErr ret; + struct winbindd_request request; + struct winbindd_response response; + + *pauthoritative = 1; + + if (!get_require_membership_sid()) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.flags = flags; + + request.data.auth_crap.logon_parameters = extra_logon_parameters + | MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT; + + if (opt_allow_mschapv2) { + request.data.auth_crap.logon_parameters |= MSV1_0_ALLOW_MSVCHAPV2; + } + + if (require_membership_of_sid) + fstrcpy(request.data.auth_crap.require_membership_of_sid, require_membership_of_sid); + + fstrcpy(request.data.auth_crap.user, username); + fstrcpy(request.data.auth_crap.domain, domain); + + fstrcpy(request.data.auth_crap.workstation, + workstation); + + memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8)); + + if (lm_response && lm_response->length) { + size_t capped_lm_response_len = MIN( + lm_response->length, + sizeof(request.data.auth_crap.lm_resp)); + + memcpy(request.data.auth_crap.lm_resp, + lm_response->data, + capped_lm_response_len); + request.data.auth_crap.lm_resp_len = capped_lm_response_len; + } + + if (nt_response && nt_response->length) { + if (nt_response->length > sizeof(request.data.auth_crap.nt_resp)) { + request.flags = request.flags | WBFLAG_BIG_NTLMV2_BLOB; + request.extra_len = nt_response->length; + request.extra_data.data = SMB_MALLOC_ARRAY(char, request.extra_len); + if (request.extra_data.data == NULL) { + return NT_STATUS_NO_MEMORY; + } + memcpy(request.extra_data.data, nt_response->data, + nt_response->length); + + } else { + memcpy(request.data.auth_crap.nt_resp, + nt_response->data, nt_response->length); + } + request.data.auth_crap.nt_resp_len = nt_response->length; + } + + ret = wbcRequestResponsePriv( + NULL, + WINBINDD_PAM_AUTH_CRAP, + &request, + &response); + SAFE_FREE(request.extra_data.data); + + /* Display response */ + + if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + if (error_string) + *error_string = smb_xstrdup("Reading winbind reply failed!"); + winbindd_free_response(&response); + return nt_status; + } + + nt_status = (NT_STATUS(response.data.auth.nt_status)); + if (!NT_STATUS_IS_OK(nt_status)) { + if (error_string) + *error_string = smb_xstrdup(response.data.auth.error_string); + *pauthoritative = response.data.auth.authoritative; + winbindd_free_response(&response); + return nt_status; + } + + if ((flags & WBFLAG_PAM_LMKEY) && lm_key) { + memcpy(lm_key, response.data.auth.first_8_lm_hash, + sizeof(response.data.auth.first_8_lm_hash)); + } + if ((flags & WBFLAG_PAM_USER_SESSION_KEY) && user_session_key) { + memcpy(user_session_key, response.data.auth.user_session_key, + sizeof(response.data.auth.user_session_key)); + } + + if (flags & WBFLAG_PAM_UNIX_NAME) { + *unix_name = SMB_STRDUP(response.data.auth.unix_username); + if (!*unix_name) { + winbindd_free_response(&response); + return NT_STATUS_NO_MEMORY; + } + } + + winbindd_free_response(&response); + return nt_status; +} + +/* contact server to change user password using auth crap */ +static NTSTATUS contact_winbind_change_pswd_auth_crap(const char *username, + const char *domain, + const DATA_BLOB new_nt_pswd, + const DATA_BLOB old_nt_hash_enc, + const DATA_BLOB new_lm_pswd, + const DATA_BLOB old_lm_hash_enc, + char **error_string) +{ + NTSTATUS nt_status; + wbcErr ret; + struct winbindd_request request; + struct winbindd_response response; + + if (!get_require_membership_sid()) + { + if(error_string) + *error_string = smb_xstrdup("Can't get membership sid."); + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + if(username != NULL) + fstrcpy(request.data.chng_pswd_auth_crap.user, username); + if(domain != NULL) + fstrcpy(request.data.chng_pswd_auth_crap.domain,domain); + + if(new_nt_pswd.length) + { + memcpy(request.data.chng_pswd_auth_crap.new_nt_pswd, new_nt_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_nt_pswd)); + request.data.chng_pswd_auth_crap.new_nt_pswd_len = new_nt_pswd.length; + } + + if(old_nt_hash_enc.length) + { + memcpy(request.data.chng_pswd_auth_crap.old_nt_hash_enc, old_nt_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_nt_hash_enc)); + request.data.chng_pswd_auth_crap.old_nt_hash_enc_len = old_nt_hash_enc.length; + } + + if(new_lm_pswd.length) + { + memcpy(request.data.chng_pswd_auth_crap.new_lm_pswd, new_lm_pswd.data, sizeof(request.data.chng_pswd_auth_crap.new_lm_pswd)); + request.data.chng_pswd_auth_crap.new_lm_pswd_len = new_lm_pswd.length; + } + + if(old_lm_hash_enc.length) + { + memcpy(request.data.chng_pswd_auth_crap.old_lm_hash_enc, old_lm_hash_enc.data, sizeof(request.data.chng_pswd_auth_crap.old_lm_hash_enc)); + request.data.chng_pswd_auth_crap.old_lm_hash_enc_len = old_lm_hash_enc.length; + } + + ret = wbcRequestResponse(NULL, WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, + &request, &response); + + /* Display response */ + + if (!WBC_ERROR_IS_OK(ret) && (response.data.auth.nt_status == 0)) + { + nt_status = NT_STATUS_UNSUCCESSFUL; + if (error_string) + *error_string = smb_xstrdup("Reading winbind reply failed!"); + winbindd_free_response(&response); + return nt_status; + } + + nt_status = (NT_STATUS(response.data.auth.nt_status)); + if (!NT_STATUS_IS_OK(nt_status)) + { + if (error_string) + *error_string = smb_xstrdup(response.data.auth.error_string); + winbindd_free_response(&response); + return nt_status; + } + + winbindd_free_response(&response); + + return nt_status; +} + +/* + * This function does not create a full auth_session_info, just enough + * for the caller to get the "unix" username + */ +static NTSTATUS ntlm_auth_generate_session_info(struct auth4_context *auth_context, + TALLOC_CTX *mem_ctx, + void *server_returned_info, + const char *original_user_name, + uint32_t session_info_flags, + struct auth_session_info **session_info_out) +{ + const char *unix_username = (const char *)server_returned_info; + struct dom_sid *sids = NULL; + struct auth_session_info *session_info = NULL; + + session_info = talloc_zero(mem_ctx, struct auth_session_info); + if (session_info == NULL) { + return NT_STATUS_NO_MEMORY; + } + + session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix); + if (session_info->unix_info == NULL) { + TALLOC_FREE(session_info); + return NT_STATUS_NO_MEMORY; + } + session_info->unix_info->unix_name = talloc_strdup(session_info->unix_info, + unix_username); + if (session_info->unix_info->unix_name == NULL) { + TALLOC_FREE(session_info); + return NT_STATUS_NO_MEMORY; + } + + /* + * This is not a full session_info - it is not created + * correctly and misses any claims etc, because all we + * actually use in the caller is the unix username. + * + * Therefore so no claims need to be added and + * se_access_check() will never run. + */ + session_info->security_token + = security_token_initialise(talloc_tos(), + CLAIMS_EVALUATION_INVALID_STATE); + if (session_info->security_token == NULL) { + TALLOC_FREE(session_info); + return NT_STATUS_NO_MEMORY; + } + + sids = talloc_zero_array(session_info->security_token, + struct dom_sid, 3); + if (sids == NULL) { + TALLOC_FREE(session_info); + return NT_STATUS_NO_MEMORY; + } + sid_copy(&sids[0], &global_sid_World); + sid_copy(&sids[1], &global_sid_Network); + sid_copy(&sids[2], &global_sid_Authenticated_Users); + + session_info->security_token->num_sids = talloc_array_length(sids); + session_info->security_token->sids = sids; + + *session_info_out = session_info; + + return NT_STATUS_OK; +} + +static NTSTATUS ntlm_auth_generate_session_info_pac(struct auth4_context *auth_ctx, + TALLOC_CTX *mem_ctx, + struct smb_krb5_context *smb_krb5_context, + DATA_BLOB *pac_blob, + const char *princ_name, + const struct tsocket_address *remote_address, + uint32_t session_info_flags, + struct auth_session_info **session_info) +{ + TALLOC_CTX *tmp_ctx; + struct PAC_LOGON_INFO *logon_info = NULL; + char *unixuser; + NTSTATUS status; + const char *domain = ""; + const char *user = ""; + + tmp_ctx = talloc_new(mem_ctx); + if (!tmp_ctx) { + return NT_STATUS_NO_MEMORY; + } + + if (pac_blob) { +#ifdef HAVE_KRB5 + status = kerberos_pac_logon_info(tmp_ctx, *pac_blob, NULL, NULL, + NULL, NULL, 0, &logon_info); +#else + status = NT_STATUS_ACCESS_DENIED; +#endif + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + } else { + status = NT_STATUS_ACCESS_DENIED; + DBG_WARNING("Kerberos ticket for[%s] has no PAC: %s\n", + princ_name, nt_errstr(status)); + goto done; + } + + if (logon_info->info3.base.account_name.string != NULL) { + user = logon_info->info3.base.account_name.string; + } else { + user = ""; + } + if (logon_info->info3.base.logon_domain.string != NULL) { + domain = logon_info->info3.base.logon_domain.string; + } else { + domain = ""; + } + + if (strlen(user) == 0 || strlen(domain) == 0) { + status = NT_STATUS_ACCESS_DENIED; + DBG_WARNING("Kerberos ticket for[%s] has invalid " + "account_name[%s]/logon_domain[%s]: %s\n", + princ_name, + logon_info->info3.base.account_name.string, + logon_info->info3.base.logon_domain.string, + nt_errstr(status)); + goto done; + } + + DBG_NOTICE("Kerberos ticket principal name is [%s] " + "account_name[%s]/logon_domain[%s]\n", + princ_name, user, domain); + + if (!strequal(domain, lp_workgroup())) { + if (!lp_allow_trusted_domains()) { + status = NT_STATUS_LOGON_FAILURE; + goto done; + } + } + + unixuser = talloc_asprintf(tmp_ctx, "%s%c%s", domain, winbind_separator(), user); + if (!unixuser) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + status = ntlm_auth_generate_session_info(auth_ctx, mem_ctx, unixuser, NULL, session_info_flags, session_info); + +done: + TALLOC_FREE(tmp_ctx); + return status; +} + + + +/** + * Return the challenge as determined by the authentication subsystem + * @return an 8 byte random challenge + */ + +static NTSTATUS ntlm_auth_get_challenge(struct auth4_context *auth_ctx, + uint8_t chal[8]) +{ + if (auth_ctx->challenge.data.length == 8) { + DEBUG(5, ("auth_get_challenge: returning previous challenge by module %s (normal)\n", + auth_ctx->challenge.set_by)); + memcpy(chal, auth_ctx->challenge.data.data, 8); + return NT_STATUS_OK; + } + + if (!auth_ctx->challenge.set_by) { + generate_random_buffer(chal, 8); + + auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8); + NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data); + auth_ctx->challenge.set_by = "random"; + } + + DEBUG(10,("auth_get_challenge: challenge set by %s\n", + auth_ctx->challenge.set_by)); + + return NT_STATUS_OK; +} + +/** + * NTLM2 authentication modifies the effective challenge, + * @param challenge The new challenge value + */ +static NTSTATUS ntlm_auth_set_challenge(struct auth4_context *auth_ctx, const uint8_t chal[8], const char *set_by) +{ + auth_ctx->challenge.set_by = talloc_strdup(auth_ctx, set_by); + NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.set_by); + + auth_ctx->challenge.data = data_blob_talloc(auth_ctx, chal, 8); + NT_STATUS_HAVE_NO_MEMORY(auth_ctx->challenge.data.data); + + return NT_STATUS_OK; +} + +/** + * Check the password on an NTLMSSP login. + * + * Return the session keys used on the connection. + */ + +struct winbind_pw_check_state { + uint8_t authoritative; + void *server_info; + DATA_BLOB nt_session_key; + DATA_BLOB lm_session_key; +}; + +static struct tevent_req *winbind_pw_check_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct auth4_context *auth4_context, + const struct auth_usersupplied_info *user_info) +{ + struct tevent_req *req = NULL; + struct winbind_pw_check_state *state = NULL; + NTSTATUS nt_status; + char *error_string = NULL; + uint8_t lm_key[8]; + uint8_t user_sess_key[16]; + char *unix_name = NULL; + + req = tevent_req_create( + mem_ctx, &state, struct winbind_pw_check_state); + if (req == NULL) { + return NULL; + } + + nt_status = contact_winbind_auth_crap( + user_info->client.account_name, + user_info->client.domain_name, + user_info->workstation_name, + &auth4_context->challenge.data, + &user_info->password.response.lanman, + &user_info->password.response.nt, + WBFLAG_PAM_LMKEY | + WBFLAG_PAM_USER_SESSION_KEY | + WBFLAG_PAM_UNIX_NAME, + 0, + lm_key, user_sess_key, + &state->authoritative, + &error_string, + &unix_name); + + if (tevent_req_nterror(req, nt_status)) { + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + DBG_ERR("Login for user [%s]\\[%s]@[%s] failed due " + "to [%s]\n", + user_info->client.domain_name, + user_info->client.account_name, + user_info->workstation_name, + error_string ? + error_string : + "unknown error (NULL)"); + } else { + DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due " + "to [%s]\n", + user_info->client.domain_name, + user_info->client.account_name, + user_info->workstation_name, + error_string ? + error_string : + "unknown error (NULL)"); + } + goto done; + } + + if (!all_zero(lm_key, 8)) { + state->lm_session_key = data_blob_talloc(state, NULL, 16); + if (tevent_req_nomem(state->lm_session_key.data, req)) { + goto done; + } + memcpy(state->lm_session_key.data, lm_key, 8); + memset(state->lm_session_key.data+8, '\0', 8); + } + if (!all_zero(user_sess_key, 16)) { + state->nt_session_key = data_blob_talloc( + state, user_sess_key, 16); + if (tevent_req_nomem(state->nt_session_key.data, req)) { + goto done; + } + } + state->server_info = talloc_strdup(state, unix_name); + if (tevent_req_nomem(state->server_info, req)) { + goto done; + } + tevent_req_done(req); + +done: + SAFE_FREE(error_string); + SAFE_FREE(unix_name); + return tevent_req_post(req, ev); +} + +static NTSTATUS winbind_pw_check_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t *pauthoritative, + void **server_returned_info, + DATA_BLOB *nt_session_key, + DATA_BLOB *lm_session_key) +{ + struct winbind_pw_check_state *state = tevent_req_data( + req, struct winbind_pw_check_state); + NTSTATUS status; + + if (pauthoritative != NULL) { + *pauthoritative = state->authoritative; + } + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (server_returned_info != NULL) { + *server_returned_info = talloc_move( + mem_ctx, &state->server_info); + } + if (nt_session_key != NULL) { + *nt_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->nt_session_key.data), + .length = state->nt_session_key.length, + }; + } + if (lm_session_key != NULL) { + *lm_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->lm_session_key.data), + .length = state->lm_session_key.length, + }; + } + + return NT_STATUS_OK; +} + +struct local_pw_check_state { + uint8_t authoritative; + void *server_info; + DATA_BLOB nt_session_key; + DATA_BLOB lm_session_key; +}; + +static struct tevent_req *local_pw_check_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct auth4_context *auth4_context, + const struct auth_usersupplied_info *user_info) +{ + struct tevent_req *req = NULL; + struct local_pw_check_state *state = NULL; + struct samr_Password lm_pw, nt_pw; + NTSTATUS nt_status; + + req = tevent_req_create( + mem_ctx, &state, struct local_pw_check_state); + if (req == NULL) { + return NULL; + } + state->authoritative = 1; + + nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash); + + nt_status = ntlm_password_check( + state, + true, + NTLM_AUTH_ON, + 0, + &auth4_context->challenge.data, + &user_info->password.response.lanman, + &user_info->password.response.nt, + user_info->client.account_name, + user_info->client.account_name, + user_info->client.domain_name, + &lm_pw, + &nt_pw, + &state->nt_session_key, + &state->lm_session_key); + + if (tevent_req_nterror(req, nt_status)) { + DBG_NOTICE("Login for user [%s]\\[%s]@[%s] failed due to " + "[%s]\n", + user_info->client.domain_name, + user_info->client.account_name, + user_info->workstation_name, + nt_errstr(nt_status)); + return tevent_req_post(req, ev); + } + + state->server_info = talloc_asprintf( + state, + "%s%c%s", + user_info->client.domain_name, + *lp_winbind_separator(), + user_info->client.account_name); + if (tevent_req_nomem(state->server_info, req)) { + return tevent_req_post(req, ev); + } + + tevent_req_done(req); + return tevent_req_post(req, ev); +} + +static NTSTATUS local_pw_check_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uint8_t *pauthoritative, + void **server_returned_info, + DATA_BLOB *nt_session_key, + DATA_BLOB *lm_session_key) +{ + struct local_pw_check_state *state = tevent_req_data( + req, struct local_pw_check_state); + NTSTATUS status; + + if (pauthoritative != NULL) { + *pauthoritative = state->authoritative; + } + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + + if (server_returned_info != NULL) { + *server_returned_info = talloc_move( + mem_ctx, &state->server_info); + } + if (nt_session_key != NULL) { + *nt_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->nt_session_key.data), + .length = state->nt_session_key.length, + }; + } + if (lm_session_key != NULL) { + *lm_session_key = (DATA_BLOB) { + .data = talloc_move( + mem_ctx, &state->lm_session_key.data), + .length = state->lm_session_key.length, + }; + } + + return NT_STATUS_OK; +} + +static NTSTATUS ntlm_auth_prepare_gensec_client(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct gensec_security **gensec_security_out) +{ + struct gensec_security *gensec_security = NULL; + NTSTATUS nt_status; + TALLOC_CTX *tmp_ctx; + const struct gensec_security_ops **backends = NULL; + struct gensec_settings *gensec_settings = NULL; + size_t idx = 0; + + tmp_ctx = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx); + if (gensec_settings == NULL) { + DEBUG(10, ("lpcfg_gensec_settings failed\n")); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + backends = talloc_zero_array(gensec_settings, + const struct gensec_security_ops *, 4); + if (backends == NULL) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + gensec_settings->backends = backends; + + gensec_init(); + + /* These need to be in priority order, krb5 before NTLMSSP */ +#if defined(HAVE_KRB5) + backends[idx++] = &gensec_gse_krb5_security_ops; +#endif + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP); + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO); + + nt_status = gensec_client_start(NULL, &gensec_security, + gensec_settings); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + + talloc_unlink(tmp_ctx, gensec_settings); + + if (opt_target_service != NULL) { + nt_status = gensec_set_target_service(gensec_security, + opt_target_service); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + } + + if (opt_target_hostname != NULL) { + nt_status = gensec_set_target_hostname(gensec_security, + opt_target_hostname); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + } + + *gensec_security_out = talloc_steal(mem_ctx, gensec_security); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +static struct auth4_context *make_auth4_context_ntlm_auth(TALLOC_CTX *mem_ctx, bool local_pw) +{ + struct auth4_context *auth4_context = talloc_zero(mem_ctx, struct auth4_context); + if (auth4_context == NULL) { + DEBUG(10, ("failed to allocate auth4_context\n")); + return NULL; + } + auth4_context->generate_session_info = ntlm_auth_generate_session_info; + auth4_context->generate_session_info_pac = ntlm_auth_generate_session_info_pac; + auth4_context->get_ntlm_challenge = ntlm_auth_get_challenge; + auth4_context->set_ntlm_challenge = ntlm_auth_set_challenge; + if (local_pw) { + auth4_context->check_ntlm_password_send = local_pw_check_send; + auth4_context->check_ntlm_password_recv = local_pw_check_recv; + } else { + auth4_context->check_ntlm_password_send = + winbind_pw_check_send; + auth4_context->check_ntlm_password_recv = + winbind_pw_check_recv; + } + auth4_context->private_data = NULL; + return auth4_context; +} + +static NTSTATUS ntlm_auth_prepare_gensec_server(TALLOC_CTX *mem_ctx, + struct loadparm_context *lp_ctx, + struct gensec_security **gensec_security_out) +{ + struct gensec_security *gensec_security; + NTSTATUS nt_status; + + TALLOC_CTX *tmp_ctx; + const struct gensec_security_ops **backends; + struct gensec_settings *gensec_settings; + size_t idx = 0; + struct cli_credentials *server_credentials; + + struct auth4_context *auth4_context; + + tmp_ctx = talloc_new(mem_ctx); + NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); + + auth4_context = make_auth4_context_ntlm_auth(tmp_ctx, opt_password); + if (auth4_context == NULL) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx); + if (lp_ctx == NULL) { + DEBUG(10, ("lpcfg_gensec_settings failed\n")); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + + /* + * This should be a 'netbios domain -> DNS domain' + * mapping, and can currently validly return NULL on + * poorly configured systems. + * + * This is used for the NTLMSSP server + * + */ + if (opt_password) { + gensec_settings->server_netbios_name = lp_netbios_name(); + gensec_settings->server_netbios_domain = lp_workgroup(); + } else { + gensec_settings->server_netbios_name = get_winbind_netbios_name(); + gensec_settings->server_netbios_domain = get_winbind_domain(); + } + + gensec_settings->server_dns_domain = strlower_talloc(gensec_settings, + get_mydnsdomname(talloc_tos())); + gensec_settings->server_dns_name = strlower_talloc(gensec_settings, + get_mydnsfullname()); + + backends = talloc_zero_array(gensec_settings, + const struct gensec_security_ops *, 4); + + if (backends == NULL) { + TALLOC_FREE(tmp_ctx); + return NT_STATUS_NO_MEMORY; + } + gensec_settings->backends = backends; + + gensec_init(); + + /* These need to be in priority order, krb5 before NTLMSSP */ +#if defined(HAVE_KRB5) + backends[idx++] = &gensec_gse_krb5_security_ops; +#endif + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_NTLMSSP); + + backends[idx++] = gensec_security_by_oid(NULL, GENSEC_OID_SPNEGO); + + /* + * This is anonymous for now, because we just use it + * to set the kerberos state at the moment + */ + server_credentials = cli_credentials_init_anon(tmp_ctx); + if (!server_credentials) { + DBG_ERR("Failed to init server credentials\n"); + return NT_STATUS_NO_MEMORY; + } + + cli_credentials_set_conf(server_credentials, lp_ctx); + + if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC || lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) { + cli_credentials_set_kerberos_state(server_credentials, + CRED_USE_KERBEROS_DESIRED, + CRED_SPECIFIED); + } else { + cli_credentials_set_kerberos_state(server_credentials, + CRED_USE_KERBEROS_DISABLED, + CRED_SPECIFIED); + } + + nt_status = gensec_server_start(tmp_ctx, gensec_settings, + auth4_context, &gensec_security); + + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + + gensec_set_credentials(gensec_security, server_credentials); + + /* + * TODO: Allow the caller to pass their own description here + * via a command-line option + */ + nt_status = gensec_set_target_service_description(gensec_security, + "ntlm_auth"); + if (!NT_STATUS_IS_OK(nt_status)) { + TALLOC_FREE(tmp_ctx); + return nt_status; + } + + talloc_unlink(tmp_ctx, lp_ctx); + talloc_unlink(tmp_ctx, server_credentials); + talloc_unlink(tmp_ctx, gensec_settings); + talloc_unlink(tmp_ctx, auth4_context); + + *gensec_security_out = talloc_steal(mem_ctx, gensec_security); + TALLOC_FREE(tmp_ctx); + return NT_STATUS_OK; +} + +static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1); + return; +} + +static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + char *user, *pass; + user=buf; + + pass=(char *)memchr(buf,' ',length); + if (!pass) { + DEBUG(2, ("Password not found. Denying access\n")); + printf("ERR\n"); + return; + } + *pass='\0'; + pass++; + + if (state->helper_mode == SQUID_2_5_BASIC) { + char *end = rfc1738_unescape(user); + if (end == NULL || (end - user) != strlen(user)) { + DEBUG(2, ("Badly rfc1738 encoded username: %s; " + "denying access\n", user)); + printf("ERR\n"); + return; + } + end = rfc1738_unescape(pass); + if (end == NULL || (end - pass) != strlen(pass)) { + DEBUG(2, ("Badly encoded password for %s; " + "denying access\n", user)); + printf("ERR\n"); + return; + } + } + + if (check_plaintext_auth(user, pass, False)) { + printf("OK\n"); + } else { + printf("ERR\n"); + } +} + +static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + char *buf, int length, void **private1) +{ + DATA_BLOB in; + DATA_BLOB out = data_blob(NULL, 0); + char *out_base64 = NULL; + const char *reply_arg = NULL; + struct gensec_ntlm_state { + struct gensec_security *gensec_state; + const char *set_password; + }; + struct gensec_ntlm_state *state; + + NTSTATUS nt_status; + bool first = false; + const char *reply_code; + struct cli_credentials *creds; + + static char *want_feature_list = NULL; + static DATA_BLOB session_key; + + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx"); + if (mem_ctx == NULL) { + printf("BH No Memory\n"); + exit(1); + } + + if (*private1) { + state = talloc_get_type(*private1, struct gensec_ntlm_state); + if (state == NULL) { + DBG_WARNING("*private1 is of type %s\n", + talloc_get_name(*private1)); + printf("BH *private1 is of type %s\n", + talloc_get_name(*private1)); + exit(1); + } + } else { + state = talloc_zero(NULL, struct gensec_ntlm_state); + if (!state) { + printf("BH No Memory\n"); + exit(1); + } + *private1 = state; + if (opt_password) { + state->set_password = opt_password; + } + } + + if (strlen(buf) < 2) { + DEBUG(1, ("query [%s] invalid\n", buf)); + printf("BH Query invalid\n"); + talloc_free(mem_ctx); + return; + } + + if (strlen(buf) > 3) { + if(strncmp(buf, "SF ", 3) == 0) { + DEBUG(10, ("Setting flags to negotiate\n")); + talloc_free(want_feature_list); + want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3); + printf("OK\n"); + talloc_free(mem_ctx); + return; + } + in = base64_decode_data_blob_talloc(mem_ctx, buf + 3); + } else { + in = data_blob(NULL, 0); + } + + if (strncmp(buf, "YR", 2) == 0) { + if (state->gensec_state) { + talloc_free(state->gensec_state); + state->gensec_state = NULL; + } + } else if ( (strncmp(buf, "OK", 2) == 0)) { + /* Just return BH, like ntlm_auth from Samba 3 does. */ + printf("BH Command expected\n"); + talloc_free(mem_ctx); + return; + } else if ( (strncmp(buf, "TT ", 3) != 0) && + (strncmp(buf, "KK ", 3) != 0) && + (strncmp(buf, "AF ", 3) != 0) && + (strncmp(buf, "NA ", 3) != 0) && + (strncmp(buf, "UG", 2) != 0) && + (strncmp(buf, "PW ", 3) != 0) && + (strncmp(buf, "GK", 2) != 0) && + (strncmp(buf, "GF", 2) != 0)) { + DEBUG(1, ("SPNEGO request [%s] invalid prefix\n", buf)); + printf("BH SPNEGO request invalid prefix\n"); + talloc_free(mem_ctx); + return; + } + + /* setup gensec */ + if (!(state->gensec_state)) { + switch (stdio_helper_mode) { + case GSS_SPNEGO_CLIENT: + /* + * cached credentials are only supported by + * NTLMSSP_CLIENT_1 for now. + */ + use_cached_creds = false; + FALL_THROUGH; + case NTLMSSP_CLIENT_1: + /* setup the client side */ + + if (state->set_password != NULL) { + use_cached_creds = false; + } + + if (use_cached_creds) { + struct wbcCredentialCacheParams params; + struct wbcCredentialCacheInfo *info = NULL; + struct wbcAuthErrorInfo *error = NULL; + wbcErr wbc_status; + + params.account_name = opt_username; + params.domain_name = opt_domain; + params.level = WBC_CREDENTIAL_CACHE_LEVEL_NTLMSSP; + params.num_blobs = 0; + params.blobs = NULL; + + wbc_status = wbcCredentialCache(¶ms, &info, + &error); + wbcFreeMemory(error); + if (!WBC_ERROR_IS_OK(wbc_status)) { + use_cached_creds = false; + } + wbcFreeMemory(info); + } + + nt_status = ntlm_auth_prepare_gensec_client(state, lp_ctx, + &state->gensec_state); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("BH GENSEC mech failed to start: %s\n", + nt_errstr(nt_status)); + talloc_free(mem_ctx); + return; + } + + creds = cli_credentials_init(state->gensec_state); + cli_credentials_set_conf(creds, lp_ctx); + if (opt_username) { + cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED); + } + if (opt_domain) { + cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED); + } + if (use_cached_creds) { + gensec_want_feature(state->gensec_state, + GENSEC_FEATURE_NTLM_CCACHE); + } else if (state->set_password) { + cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED); + } else { + cli_credentials_set_password_callback(creds, get_password); + } + if (opt_workstation) { + cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED); + } + + gensec_set_credentials(state->gensec_state, creds); + + break; + case GSS_SPNEGO_SERVER: + case SQUID_2_5_NTLMSSP: + { + nt_status = ntlm_auth_prepare_gensec_server(state, lp_ctx, + &state->gensec_state); + if (!NT_STATUS_IS_OK(nt_status)) { + printf("BH GENSEC mech failed to start: %s\n", + nt_errstr(nt_status)); + talloc_free(mem_ctx); + return; + } + break; + } + default: + talloc_free(mem_ctx); + abort(); + } + + gensec_want_feature_list(state->gensec_state, want_feature_list); + + /* Session info is not complete, do not pass to auth log */ + gensec_want_feature(state->gensec_state, GENSEC_FEATURE_NO_AUTHZ_LOG); + + switch (stdio_helper_mode) { + case GSS_SPNEGO_CLIENT: + case GSS_SPNEGO_SERVER: + nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO); + if (!in.length) { + first = true; + } + break; + case NTLMSSP_CLIENT_1: + if (!in.length) { + first = true; + } + FALL_THROUGH; + case SQUID_2_5_NTLMSSP: + nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP); + break; + default: + talloc_free(mem_ctx); + abort(); + } + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status))); + printf("BH GENSEC mech failed to start\n"); + talloc_free(mem_ctx); + return; + } + + } + + /* update */ + + if (strncmp(buf, "PW ", 3) == 0) { + state->set_password = talloc_strndup(state, + (const char *)in.data, + in.length); + + cli_credentials_set_password(gensec_get_credentials(state->gensec_state), + state->set_password, + CRED_SPECIFIED); + printf("OK\n"); + talloc_free(mem_ctx); + return; + } + + if (strncmp(buf, "GK", 2) == 0) { + char *base64_key; + DEBUG(10, ("Requested session key\n")); + nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key); + if(!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status))); + printf("BH No session key\n"); + talloc_free(mem_ctx); + return; + } else { + base64_key = base64_encode_data_blob(state, session_key); + SMB_ASSERT(base64_key != NULL); + printf("GK %s\n", base64_key); + talloc_free(base64_key); + } + talloc_free(mem_ctx); + return; + } + + if (strncmp(buf, "GF", 2) == 0) { + uint32_t neg_flags; + + DEBUG(10, ("Requested negotiated NTLMSSP feature flags\n")); + + neg_flags = gensec_ntlmssp_neg_flags(state->gensec_state); + if (neg_flags == 0) { + printf("BH\n"); + talloc_free(mem_ctx); + return; + } + + printf("GF 0x%08x\n", neg_flags); + talloc_free(mem_ctx); + return; + } + + nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out); + + /* don't leak 'bad password'/'no such user' info to the network client */ + nt_status = nt_status_squash(nt_status); + + if (out.length) { + out_base64 = base64_encode_data_blob(mem_ctx, out); + SMB_ASSERT(out_base64 != NULL); + } else { + out_base64 = NULL; + } + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + reply_arg = "*"; + if (first && state->gensec_state->gensec_role == GENSEC_CLIENT) { + reply_code = "YR"; + } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { + reply_code = "KK"; + } else if (state->gensec_state->gensec_role == GENSEC_SERVER) { + reply_code = "TT"; + } else { + abort(); + } + + + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + reply_code = "BH NT_STATUS_ACCESS_DENIED"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { + reply_code = "BH NT_STATUS_UNSUCCESSFUL"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); + } else if (!NT_STATUS_IS_OK(nt_status)) { + reply_code = "NA"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); + } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) { + struct auth_session_info *session_info; + + nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info); + if (!NT_STATUS_IS_OK(nt_status)) { + reply_code = "BH Failed to retrieve session info"; + reply_arg = nt_errstr(nt_status); + DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status))); + } else { + + reply_code = "AF"; + reply_arg = talloc_strdup(state->gensec_state, session_info->unix_info->unix_name); + if (reply_arg == NULL) { + reply_code = "BH out of memory"; + reply_arg = nt_errstr(NT_STATUS_NO_MEMORY); + } + talloc_free(session_info); + } + } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { + reply_code = "AF"; + reply_arg = out_base64; + } else { + abort(); + } + + switch (stdio_helper_mode) { + case GSS_SPNEGO_SERVER: + printf("%s %s %s\n", reply_code, + out_base64 ? out_base64 : "*", + reply_arg ? reply_arg : "*"); + break; + default: + if (out_base64) { + printf("%s %s\n", reply_code, out_base64); + } else if (reply_arg) { + printf("%s %s\n", reply_code, reply_arg); + } else { + printf("%s\n", reply_code); + } + } + + talloc_free(mem_ctx); + return; +} + +static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1); + return; +} + +static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1); + return; +} + +static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + manage_gensec_request(stdio_helper_mode, lp_ctx, buf, length, &state->gensec_private_1); + return; +} + +static void manage_ntlm_server_1_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + char *request, *parameter; + static DATA_BLOB challenge; + static DATA_BLOB lm_response; + static DATA_BLOB nt_response; + static char *full_username; + static char *username; + static char *domain; + static char *plaintext_password; + static bool ntlm_server_1_user_session_key; + static bool ntlm_server_1_lm_session_key; + + if (strequal(buf, ".")) { + if (!full_username && !username) { + printf("Error: No username supplied!\n"); + } else if (plaintext_password) { + /* handle this request as plaintext */ + if (!full_username) { + if (asprintf(&full_username, "%s%c%s", domain, winbind_separator(), username) == -1) { + printf("Error: Out of memory in " + "asprintf!\n.\n"); + return; + } + } + if (check_plaintext_auth(full_username, plaintext_password, False)) { + printf("Authenticated: Yes\n"); + } else { + printf("Authenticated: No\n"); + } + } else if (!lm_response.data && !nt_response.data) { + printf("Error: No password supplied!\n"); + } else if (!challenge.data) { + printf("Error: No lanman-challenge supplied!\n"); + } else { + char *error_string = NULL; + uchar lm_key[8]; + uchar user_session_key[16]; + uint32_t flags = 0; + NTSTATUS nt_status; + if (full_username && !username) { + fstring fstr_user; + fstring fstr_domain; + + if (!parse_ntlm_auth_domain_user(full_username, fstr_user, fstr_domain)) { + /* username might be 'tainted', don't print into our new-line deleimianted stream */ + printf("Error: Could not parse into " + "domain and username\n"); + } + SAFE_FREE(username); + SAFE_FREE(domain); + username = smb_xstrdup(fstr_user); + domain = smb_xstrdup(fstr_domain); + } + + if (opt_password) { + DATA_BLOB nt_session_key, lm_session_key; + struct samr_Password lm_pw, nt_pw; + TALLOC_CTX *mem_ctx = talloc_new(NULL); + ZERO_STRUCT(user_session_key); + ZERO_STRUCT(lm_key); + + nt_lm_owf_gen (opt_password, nt_pw.hash, lm_pw.hash); + nt_status = ntlm_password_check(mem_ctx, + true, + NTLM_AUTH_ON, + 0, + &challenge, + &lm_response, + &nt_response, + username, + username, + domain, + &lm_pw, &nt_pw, + &nt_session_key, + &lm_session_key); + error_string = smb_xstrdup(get_friendly_nt_error_msg(nt_status)); + if (ntlm_server_1_user_session_key) { + if (nt_session_key.length == sizeof(user_session_key)) { + memcpy(user_session_key, + nt_session_key.data, + sizeof(user_session_key)); + } + } + if (ntlm_server_1_lm_session_key) { + if (lm_session_key.length == sizeof(lm_key)) { + memcpy(lm_key, + lm_session_key.data, + sizeof(lm_key)); + } + } + TALLOC_FREE(mem_ctx); + + } else { + uint8_t authoritative = 1; + + if (!domain) { + domain = smb_xstrdup(get_winbind_domain()); + } + + if (ntlm_server_1_lm_session_key) + flags |= WBFLAG_PAM_LMKEY; + + if (ntlm_server_1_user_session_key) + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + nt_status = contact_winbind_auth_crap(username, + domain, + lp_netbios_name(), + &challenge, + &lm_response, + &nt_response, + flags, 0, + lm_key, + user_session_key, + &authoritative, + &error_string, + NULL); + } + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("Authenticated: No\n"); + printf("Authentication-Error: %s\n.\n", + error_string); + } else { + char *hex_lm_key; + char *hex_user_session_key; + + printf("Authenticated: Yes\n"); + + if (ntlm_server_1_lm_session_key + && (!all_zero(lm_key, + sizeof(lm_key)))) { + hex_lm_key = hex_encode_talloc(NULL, + (const unsigned char *)lm_key, + sizeof(lm_key)); + printf("LANMAN-Session-Key: %s\n", + hex_lm_key); + TALLOC_FREE(hex_lm_key); + } + + if (ntlm_server_1_user_session_key + && (!all_zero(user_session_key, + sizeof(user_session_key)))) { + hex_user_session_key = hex_encode_talloc(NULL, + (const unsigned char *)user_session_key, + sizeof(user_session_key)); + printf("User-Session-Key: %s\n", + hex_user_session_key); + TALLOC_FREE(hex_user_session_key); + } + } + SAFE_FREE(error_string); + } + /* clear out the state */ + challenge = data_blob_null; + nt_response = data_blob_null; + lm_response = data_blob_null; + SAFE_FREE(full_username); + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(plaintext_password); + ntlm_server_1_user_session_key = False; + ntlm_server_1_lm_session_key = False; + printf(".\n"); + + return; + } + + request = buf; + + /* Indicates a base64 encoded structure */ + parameter = strstr_m(request, ":: "); + if (!parameter) { + parameter = strstr_m(request, ": "); + + if (!parameter) { + DEBUG(0, ("Parameter not found!\n")); + printf("Error: Parameter not found!\n.\n"); + return; + } + + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + + } else { + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + + base64_decode_inplace(parameter); + } + + if (strequal(request, "LANMAN-Challenge")) { + challenge = strhex_to_data_blob(NULL, parameter); + if (challenge.length != 8) { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 8)\n.\n", + parameter, + (int)challenge.length); + challenge = data_blob_null; + } + } else if (strequal(request, "NT-Response")) { + nt_response = strhex_to_data_blob(NULL, parameter); + if (nt_response.length < 24) { + printf("Error: hex decode of %s failed! " + "(only got %d bytes, needed at least 24)\n.\n", + parameter, + (int)nt_response.length); + nt_response = data_blob_null; + } + } else if (strequal(request, "LANMAN-Response")) { + lm_response = strhex_to_data_blob(NULL, parameter); + if (lm_response.length != 24) { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 24)\n.\n", + parameter, + (int)lm_response.length); + lm_response = data_blob_null; + } + } else if (strequal(request, "Password")) { + plaintext_password = smb_xstrdup(parameter); + } else if (strequal(request, "NT-Domain")) { + domain = smb_xstrdup(parameter); + } else if (strequal(request, "Username")) { + username = smb_xstrdup(parameter); + } else if (strequal(request, "Full-Username")) { + full_username = smb_xstrdup(parameter); + } else if (strequal(request, "Request-User-Session-Key")) { + ntlm_server_1_user_session_key = strequal(parameter, "Yes"); + } else if (strequal(request, "Request-LanMan-Session-Key")) { + ntlm_server_1_lm_session_key = strequal(parameter, "Yes"); + } else { + printf("Error: Unknown request %s\n.\n", request); + } +} + +static void manage_ntlm_change_password_1_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + char *buf, int length, void **private2) +{ + char *request, *parameter; + static DATA_BLOB new_nt_pswd; + static DATA_BLOB old_nt_hash_enc; + static DATA_BLOB new_lm_pswd; + static DATA_BLOB old_lm_hash_enc; + static char *full_username = NULL; + static char *username = NULL; + static char *domain = NULL; + static char *newpswd = NULL; + static char *oldpswd = NULL; + + if (strequal(buf, ".")) { + if(newpswd && oldpswd) { + uchar old_nt_hash[16]; + uchar old_lm_hash[16]; + uchar new_nt_hash[16]; + uchar new_lm_hash[16]; + + gnutls_cipher_hd_t cipher_hnd = NULL; + gnutls_datum_t old_nt_key = { + .data = old_nt_hash, + .size = sizeof(old_nt_hash), + }; + int rc; + + new_nt_pswd = data_blob(NULL, 516); + old_nt_hash_enc = data_blob(NULL, 16); + + /* Calculate the MD4 hash (NT compatible) of the + * password */ + E_md4hash(oldpswd, old_nt_hash); + E_md4hash(newpswd, new_nt_hash); + + /* E_deshash returns false for 'long' + passwords (> 14 DOS chars). + + Therefore, don't send a buffer + encrypted with the truncated hash + (it could allow an even easier + attack on the password) + + Likewise, obey the admin's restriction + */ + + rc = gnutls_cipher_init(&cipher_hnd, + GNUTLS_CIPHER_ARCFOUR_128, + &old_nt_key, + NULL); + if (rc < 0) { + DBG_ERR("gnutls_cipher_init failed: %s\n", + gnutls_strerror(rc)); + if (rc == GNUTLS_E_UNWANTED_ALGORITHM) { + DBG_ERR("Running in FIPS mode, NTLM blocked\n"); + } + return; + } + + if (lp_client_lanman_auth() && + E_deshash(newpswd, new_lm_hash) && + E_deshash(oldpswd, old_lm_hash)) { + new_lm_pswd = data_blob(NULL, 516); + old_lm_hash_enc = data_blob(NULL, 16); + encode_pw_buffer(new_lm_pswd.data, newpswd, + STR_UNICODE); + + rc = gnutls_cipher_encrypt(cipher_hnd, + new_lm_pswd.data, + 516); + if (rc < 0) { + gnutls_cipher_deinit(cipher_hnd); + return; + } + rc = E_old_pw_hash(new_nt_hash, old_lm_hash, + old_lm_hash_enc.data); + if (rc != 0) { + DBG_ERR("E_old_pw_hash failed: %s\n", + gnutls_strerror(rc)); + return; + } + } else { + new_lm_pswd.data = NULL; + new_lm_pswd.length = 0; + old_lm_hash_enc.data = NULL; + old_lm_hash_enc.length = 0; + } + + encode_pw_buffer(new_nt_pswd.data, newpswd, + STR_UNICODE); + + rc = gnutls_cipher_encrypt(cipher_hnd, + new_nt_pswd.data, + 516); + gnutls_cipher_deinit(cipher_hnd); + if (rc < 0) { + return; + } + rc = E_old_pw_hash(new_nt_hash, old_nt_hash, + old_nt_hash_enc.data); + if (rc != 0) { + DBG_ERR("E_old_pw_hash failed: %s\n", + gnutls_strerror(rc)); + return; + } + + ZERO_ARRAY(old_nt_hash); + ZERO_ARRAY(old_lm_hash); + ZERO_ARRAY(new_nt_hash); + ZERO_ARRAY(new_lm_hash); + } + + if (!full_username && !username) { + printf("Error: No username supplied!\n"); + } else if ((!new_nt_pswd.data || !old_nt_hash_enc.data) && + (!new_lm_pswd.data || old_lm_hash_enc.data) ) { + printf("Error: No NT or LM password " + "blobs supplied!\n"); + } else { + char *error_string = NULL; + + if (full_username && !username) { + fstring fstr_user; + fstring fstr_domain; + + if (!parse_ntlm_auth_domain_user(full_username, + fstr_user, + fstr_domain)) { + /* username might be 'tainted', don't + * print into our new-line + * deleimianted stream */ + printf("Error: Could not " + "parse into domain and " + "username\n"); + SAFE_FREE(username); + username = smb_xstrdup(full_username); + } else { + SAFE_FREE(username); + SAFE_FREE(domain); + username = smb_xstrdup(fstr_user); + domain = smb_xstrdup(fstr_domain); + } + + } + + if(!NT_STATUS_IS_OK(contact_winbind_change_pswd_auth_crap( + username, domain, + new_nt_pswd, + old_nt_hash_enc, + new_lm_pswd, + old_lm_hash_enc, + &error_string))) { + printf("Password-Change: No\n"); + printf("Password-Change-Error: %s\n.\n", + error_string); + } else { + printf("Password-Change: Yes\n"); + } + + SAFE_FREE(error_string); + } + /* clear out the state */ + new_nt_pswd = data_blob_null; + old_nt_hash_enc = data_blob_null; + new_lm_pswd = data_blob_null; + old_nt_hash_enc = data_blob_null; + SAFE_FREE(full_username); + SAFE_FREE(username); + SAFE_FREE(domain); + SAFE_FREE(newpswd); + SAFE_FREE(oldpswd); + printf(".\n"); + + return; + } + + request = buf; + + /* Indicates a base64 encoded structure */ + parameter = strstr_m(request, ":: "); + if (!parameter) { + parameter = strstr_m(request, ": "); + + if (!parameter) { + DEBUG(0, ("Parameter not found!\n")); + printf("Error: Parameter not found!\n.\n"); + return; + } + + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + } else { + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + parameter[0] ='\0'; + parameter++; + + base64_decode_inplace(parameter); + } + + if (strequal(request, "new-nt-password-blob")) { + new_nt_pswd = strhex_to_data_blob(NULL, parameter); + if (new_nt_pswd.length != 516) { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 516)\n.\n", + parameter, + (int)new_nt_pswd.length); + new_nt_pswd = data_blob_null; + } + } else if (strequal(request, "old-nt-hash-blob")) { + old_nt_hash_enc = strhex_to_data_blob(NULL, parameter); + if (old_nt_hash_enc.length != 16) { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 16)\n.\n", + parameter, + (int)old_nt_hash_enc.length); + old_nt_hash_enc = data_blob_null; + } + } else if (strequal(request, "new-lm-password-blob")) { + new_lm_pswd = strhex_to_data_blob(NULL, parameter); + if (new_lm_pswd.length != 516) { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 516)\n.\n", + parameter, + (int)new_lm_pswd.length); + new_lm_pswd = data_blob_null; + } + } + else if (strequal(request, "old-lm-hash-blob")) { + old_lm_hash_enc = strhex_to_data_blob(NULL, parameter); + if (old_lm_hash_enc.length != 16) + { + printf("Error: hex decode of %s failed! " + "(got %d bytes, expected 16)\n.\n", + parameter, + (int)old_lm_hash_enc.length); + old_lm_hash_enc = data_blob_null; + } + } else if (strequal(request, "nt-domain")) { + domain = smb_xstrdup(parameter); + } else if(strequal(request, "username")) { + username = smb_xstrdup(parameter); + } else if(strequal(request, "full-username")) { + username = smb_xstrdup(parameter); + } else if(strequal(request, "new-password")) { + newpswd = smb_xstrdup(parameter); + } else if (strequal(request, "old-password")) { + oldpswd = smb_xstrdup(parameter); + } else { + printf("Error: Unknown request %s\n.\n", request); + } +} + +static void manage_squid_request(enum stdio_helper_mode stdio_helper_mode, + struct loadparm_context *lp_ctx, + struct ntlm_auth_state *state, + stdio_helper_function fn, void **private2) +{ + char *buf; + char tmp[INITIAL_BUFFER_SIZE+1]; + int length, buf_size = 0; + char *c; + + buf = talloc_strdup(state->mem_ctx, ""); + if (!buf) { + DEBUG(0, ("Failed to allocate input buffer.\n")); + fprintf(stderr, "ERR\n"); + exit(1); + } + + do { + + /* this is not a typo - x_fgets doesn't work too well under + * squid */ + if (fgets(tmp, sizeof(tmp)-1, stdin) == NULL) { + if (ferror(stdin)) { + DEBUG(1, ("fgets() failed! dying..... errno=%d " + "(%s)\n", ferror(stdin), + strerror(ferror(stdin)))); + + exit(1); + } + exit(0); + } + + buf = talloc_strdup_append_buffer(buf, tmp); + buf_size += INITIAL_BUFFER_SIZE; + + if (buf_size > MAX_BUFFER_SIZE) { + DEBUG(2, ("Oversized message\n")); + fprintf(stderr, "ERR\n"); + talloc_free(buf); + return; + } + + c = strchr(buf, '\n'); + } while (c == NULL); + + *c = '\0'; + length = c-buf; + + DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length)); + + if (buf[0] == '\0') { + DEBUG(2, ("Invalid Request\n")); + fprintf(stderr, "ERR\n"); + talloc_free(buf); + return; + } + + fn(stdio_helper_mode, lp_ctx, state, buf, length, private2); + talloc_free(buf); +} + + +static void squid_stream(enum stdio_helper_mode stdio_mode, + struct loadparm_context *lp_ctx, + stdio_helper_function fn) { + TALLOC_CTX *mem_ctx; + struct ntlm_auth_state *state; + + /* initialize FDescs */ + setbuf(stdout, NULL); + setbuf(stderr, NULL); + + mem_ctx = talloc_init("ntlm_auth"); + if (!mem_ctx) { + DEBUG(0, ("squid_stream: Failed to create talloc context\n")); + fprintf(stderr, "ERR\n"); + exit(1); + } + + state = talloc_zero(mem_ctx, struct ntlm_auth_state); + if (!state) { + DEBUG(0, ("squid_stream: Failed to talloc ntlm_auth_state\n")); + fprintf(stderr, "ERR\n"); + exit(1); + } + + state->mem_ctx = mem_ctx; + state->helper_mode = stdio_mode; + + while(1) { + TALLOC_CTX *frame = talloc_stackframe(); + manage_squid_request(stdio_mode, lp_ctx, state, fn, NULL); + TALLOC_FREE(frame); + } +} + + +/* Authenticate a user with a challenge/response */ + +static bool check_auth_crap(void) +{ + NTSTATUS nt_status; + uint32_t flags = 0; + char lm_key[8]; + char user_session_key[16]; + char *hex_lm_key; + char *hex_user_session_key; + char *error_string; + uint8_t authoritative = 1; + + setbuf(stdout, NULL); + + if (request_lm_key) + flags |= WBFLAG_PAM_LMKEY; + + if (request_user_session_key) + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + flags |= WBFLAG_PAM_NT_STATUS_SQUASH; + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &opt_challenge, + &opt_lm_response, + &opt_nt_response, + flags, 0, + (unsigned char *)lm_key, + (unsigned char *)user_session_key, + &authoritative, + &error_string, NULL); + + if (!NT_STATUS_IS_OK(nt_status)) { + printf("%s (0x%x)\n", error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (request_lm_key + && (!all_zero((uint8_t *)lm_key, sizeof(lm_key)))) { + hex_lm_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)lm_key, + sizeof(lm_key)); + printf("LM_KEY: %s\n", hex_lm_key); + TALLOC_FREE(hex_lm_key); + } + if (request_user_session_key + && (!all_zero((uint8_t *)user_session_key, + sizeof(user_session_key)))) { + hex_user_session_key = hex_encode_talloc(talloc_tos(), (const unsigned char *)user_session_key, + sizeof(user_session_key)); + printf("NT_KEY: %s\n", hex_user_session_key); + TALLOC_FREE(hex_user_session_key); + } + + return True; +} + +/* Main program */ + +enum { + OPT_USERNAME = 1000, + OPT_DOMAIN, + OPT_WORKSTATION, + OPT_CHALLENGE, + OPT_RESPONSE, + OPT_LM, + OPT_NT, + OPT_PASSWORD, + OPT_LM_KEY, + OPT_USER_SESSION_KEY, + OPT_DIAGNOSTICS, + OPT_REQUIRE_MEMBERSHIP, + OPT_USE_CACHED_CREDS, + OPT_ALLOW_MSCHAPV2, + OPT_PAM_WINBIND_CONF, + OPT_TARGET_SERVICE, + OPT_TARGET_HOSTNAME, + OPT_OFFLINE_LOGON +}; + + int main(int argc, const char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + int opt; + const char *helper_protocol = NULL; + int diagnostics = 0; + + const char *hex_challenge = NULL; + const char *hex_lm_response = NULL; + const char *hex_nt_response = NULL; + struct loadparm_context *lp_ctx; + poptContext pc; + bool ok; + + /* NOTE: DO NOT change this interface without considering the implications! + This is an external interface, which other programs will use to interact + with this helper. + */ + + /* We do not use single-letter command abbreviations, because they harm future + interface stability. */ + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "helper-protocol", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &helper_protocol, + .val = OPT_DOMAIN, + .descrip = "operate as a stdio-based helper", + .argDescrip = "helper protocol to use" + }, + { + .longName = "username", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_username, + .val = OPT_USERNAME, + .descrip = "username" + }, + { + .longName = "domain", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_domain, + .val = OPT_DOMAIN, + .descrip = "domain name" + }, + { + .longName = "workstation", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_workstation, + .val = OPT_WORKSTATION, + .descrip = "workstation" + }, + { + .longName = "challenge", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &hex_challenge, + .val = OPT_CHALLENGE, + .descrip = "challenge (HEX encoded)" + }, + { + .longName = "lm-response", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &hex_lm_response, + .val = OPT_LM, + .descrip = "LM Response to the challenge (HEX encoded)" + }, + { + .longName = "nt-response", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &hex_nt_response, + .val = OPT_NT, + .descrip = "NT or NTLMv2 Response to the challenge (HEX encoded)" + }, + { + .longName = "password", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_password, + .val = OPT_PASSWORD, + .descrip = "User's plaintext password" + }, + { + .longName = "request-lm-key", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &request_lm_key, + .val = OPT_LM_KEY, + .descrip = "Retrieve LM session key (or, with --diagnostics, expect LM support)" + }, + { + .longName = "request-nt-key", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &request_user_session_key, + .val = OPT_USER_SESSION_KEY, + .descrip = "Retrieve User (NT) session key" + }, + { + .longName = "use-cached-creds", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &use_cached_creds, + .val = OPT_USE_CACHED_CREDS, + .descrip = "Use cached credentials if no password is given" + }, + { + .longName = "allow-mschapv2", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt_allow_mschapv2, + .val = OPT_ALLOW_MSCHAPV2, + .descrip = "Explicitly allow MSCHAPv2", + }, + { + .longName = "offline-logon", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &offline_logon, + .val = OPT_OFFLINE_LOGON, + .descrip = "Use cached passwords when DC is offline" + }, + { + .longName = "diagnostics", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &diagnostics, + .val = OPT_DIAGNOSTICS, + .descrip = "Perform diagnostics on the authentication chain" + }, + { + .longName = "require-membership-of", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &require_membership_of, + .val = OPT_REQUIRE_MEMBERSHIP, + .descrip = "Require that a user be a member of this group (either name or SID) for authentication to succeed", + }, + { + .longName = "pam-winbind-conf", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_pam_winbind_conf, + .val = OPT_PAM_WINBIND_CONF, + .descrip = "Require that request must set WBFLAG_PAM_CONTACT_TRUSTDOM when krb5 auth is required", + }, + { + .longName = "target-service", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_target_service, + .val = OPT_TARGET_SERVICE, + .descrip = "Target service (eg http)", + }, + { + .longName = "target-hostname", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &opt_target_hostname, + .val = OPT_TARGET_HOSTNAME, + .descrip = "Target hostname", + }, + POPT_COMMON_DEBUG_ONLY + POPT_COMMON_CONFIG_ONLY + POPT_COMMON_OPTION_ONLY + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + /* Samba client initialisation */ + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_CHALLENGE: + opt_challenge = strhex_to_data_blob(NULL, hex_challenge); + if (opt_challenge.length != 8) { + fprintf(stderr, "hex decode of %s failed! " + "(only got %d bytes)\n", + hex_challenge, + (int)opt_challenge.length); + exit(1); + } + break; + case OPT_LM: + opt_lm_response = strhex_to_data_blob(NULL, hex_lm_response); + if (opt_lm_response.length != 24) { + fprintf(stderr, "hex decode of %s failed! " + "(only got %d bytes)\n", + hex_lm_response, + (int)opt_lm_response.length); + exit(1); + } + break; + + case OPT_NT: + opt_nt_response = strhex_to_data_blob(NULL, hex_nt_response); + if (opt_nt_response.length < 24) { + fprintf(stderr, "hex decode of %s failed! " + "(only got %d bytes)\n", + hex_nt_response, + (int)opt_nt_response.length); + exit(1); + } + break; + + case OPT_REQUIRE_MEMBERSHIP: + if (strncasecmp_m("S-", require_membership_of, 2) == 0) { + require_membership_of_sid = require_membership_of; + } + break; + + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (opt_username) { + char *domain = SMB_STRDUP(opt_username); + char *p = strchr_m(domain, *lp_winbind_separator()); + if (p) { + opt_username = p+1; + *p = '\0'; + if (opt_domain && !strequal(opt_domain, domain)) { + fprintf(stderr, "Domain specified in username (%s) " + "doesn't match specified domain (%s)!\n\n", + domain, opt_domain); + poptPrintHelp(pc, stderr, 0); + exit(1); + } + opt_domain = domain; + } else { + SAFE_FREE(domain); + } + } + + /* Note: if opt_domain is "" then send no domain */ + if (opt_domain == NULL) { + opt_domain = get_winbind_domain(); + } + + if (opt_workstation == NULL) { + opt_workstation = ""; + } + + lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + fprintf(stderr, "loadparm_init_s3() failed!\n"); + exit(1); + } + + if (helper_protocol) { + int i; + for (i=0; i<NUM_HELPER_MODES; i++) { + if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) { + squid_stream(stdio_helper_protocols[i].mode, lp_ctx, stdio_helper_protocols[i].fn); + exit(0); + } + } + fprintf(stderr, "unknown helper protocol [%s]\n\n" + "Valid helper protools:\n\n", helper_protocol); + + for (i=0; i<NUM_HELPER_MODES; i++) { + fprintf(stderr, "%s\n", + stdio_helper_protocols[i].name); + } + + exit(1); + } + + if (!opt_username || !*opt_username) { + fprintf(stderr, "username must be specified!\n\n"); + poptPrintHelp(pc, stderr, 0); + exit(1); + } + + if (opt_challenge.length) { + if (!check_auth_crap()) { + exit(1); + } + exit(0); + } + + if (!opt_password) { + char pwd[256] = {0}; + int rc; + + rc = samba_getpass("Password: ", pwd, sizeof(pwd), false, false); + if (rc == 0) { + opt_password = SMB_STRDUP(pwd); + } + } + + if (diagnostics) { + if (!diagnose_ntlm_auth(request_lm_key)) { + poptFreeContext(pc); + return 1; + } + } else { + fstring user; + + fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username); + if (!check_plaintext_auth(user, opt_password, True)) { + poptFreeContext(pc); + return 1; + } + } + + /* Exit code */ + gfree_all(); + poptFreeContext(pc); + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/utils/ntlm_auth.h b/source3/utils/ntlm_auth.h new file mode 100644 index 0000000..fb1dd62 --- /dev/null +++ b/source3/utils/ntlm_auth.h @@ -0,0 +1,26 @@ +/* + Samba Unix/Linux NTLM authentication tool + + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "utils/ntlm_auth_proto.h" + +/* Some of the popt variables are needed in the diagnostics code */ +extern const char *opt_username; +extern const char *opt_domain; +extern const char *opt_workstation; +extern const char *opt_password; + diff --git a/source3/utils/ntlm_auth_diagnostics.c b/source3/utils/ntlm_auth_diagnostics.c new file mode 100644 index 0000000..6a76e73 --- /dev/null +++ b/source3/utils/ntlm_auth_diagnostics.c @@ -0,0 +1,724 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2003 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 2003-2004 + Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "utils/ntlm_auth.h" +#include "../libcli/auth/libcli_auth.h" +#include "nsswitch/winbind_client.h" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +enum ntlm_break { + BREAK_NONE, + BREAK_LM, + BREAK_NT, + NO_LM, + NO_NT +}; + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ + +/* + * Test the normal 'LM and NTLM' combination + */ + +static bool test_lm_ntlm_broken(enum ntlm_break break_which, + bool lanman_support_expected) +{ + bool pass = True; + NTSTATUS nt_status; + uint32_t flags = 0; + DATA_BLOB lm_response = data_blob(NULL, 24); + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + uint8_t authoritative = 1; + uchar lm_key[8]; + uchar user_session_key[16]; + uchar lm_hash[16]; + uchar nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBencrypt(opt_password,chall.data,lm_response.data); + E_deshash(opt_password, lm_hash); + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_md4hash(opt_password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lm_response); + break; + case NO_NT: + data_blob_free(&nt_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, 0, + lm_key, + user_session_key, + &authoritative, + &error_string, NULL); + + data_blob_free(&lm_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + /* If we are told the DC is Samba4, expect an LM key of zeros */ + if (!lanman_support_expected) { + if (!all_zero(lm_key, + sizeof(lm_key))) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected: all zeros\n")); + pass = False; + } + } else { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + } + + if (break_which == NO_NT) { + if (memcmp(lm_hash, user_session_key, + 8) != 0) { + DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, sizeof(user_session_key)); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, sizeof(lm_hash)); + pass = False; + } + } else { + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, session_key.data, session_key.length); + pass = False; + } + } + return pass; +} + +/* + * Test LM authentication, no NT response supplied + */ + +static bool test_lm(bool lanman_support_expected) +{ + + return test_lm_ntlm_broken(NO_NT, lanman_support_expected); +} + +/* + * Test the NTLM response only, no LM. + */ + +static bool test_ntlm(bool lanman_support_expected) +{ + return test_lm_ntlm_broken(NO_LM, lanman_support_expected); +} + +/* + * Test the NTLM response only, but in the LM field. + */ + +static bool test_ntlm_in_lm(bool lanman_support_expected) +{ + bool pass = True; + NTSTATUS nt_status; + uint32_t flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + uint8_t authoritative = 1; + uchar lm_key[8]; + uchar lm_hash[16]; + uchar user_session_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_deshash(opt_password, lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + NULL, + flags, 0, + lm_key, + user_session_key, + &authoritative, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + /* If we are told the DC is Samba4, expect an LM key of zeros */ + if (!lanman_support_expected) { + if (!all_zero(lm_key, + sizeof(lm_key))) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected: all zeros\n")); + pass = False; + } + if (!all_zero(user_session_key, + sizeof(user_session_key))) { + DEBUG(1, ("Session Key (normally first 8 lm hash) does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected all zeros:\n")); + pass = False; + } + } else { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + if (memcmp(lm_hash, user_session_key, 8) != 0) { + DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + } + return pass; +} + +/* + * Test the NTLM response only, but in the both the NT and LM fields. + */ + +static bool test_ntlm_in_both(bool lanman_support_expected) +{ + bool pass = True; + NTSTATUS nt_status; + uint32_t flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + uint8_t authoritative = 1; + uint8_t lm_key[8]; + uint8_t lm_hash[16]; + uint8_t user_session_key[16]; + uint8_t nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + E_md4hash(opt_password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, session_key.data); + + E_deshash(opt_password, lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + &nt_response, + flags, 0, + lm_key, + user_session_key, + &authoritative, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + /* If we are told the DC is Samba4, expect an LM key of zeros */ + if (!lanman_support_expected) { + if (!all_zero(lm_key, + sizeof(lm_key))) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected: all zeros\n")); + pass = False; + } + } else { + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + } + if (memcmp(session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, session_key.data, session_key.length); + pass = False; + } + + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static bool test_lmv2_ntlmv2_broken(enum ntlm_break break_which) +{ + bool pass = True; + NTSTATUS nt_status; + uint32_t flags = 0; + DATA_BLOB ntlmv2_response = data_blob_null; + DATA_BLOB lmv2_response = data_blob_null; + DATA_BLOB ntlmv2_session_key = data_blob_null; + DATA_BLOB names_blob = NTLMv2_generate_names_blob(NULL, get_winbind_netbios_name(), get_winbind_domain()); + uint8_t authoritative = 1; + uchar user_session_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + if (!SMBNTLMv2encrypt(NULL, opt_username, opt_domain, opt_password, &chall, + &names_blob, + &lmv2_response, &ntlmv2_response, NULL, + &ntlmv2_session_key)) { + data_blob_free(&names_blob); + return False; + } + data_blob_free(&names_blob); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lmv2_response.data[0]++; + break; + case BREAK_NT: + ntlmv2_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lmv2_response); + break; + case NO_NT: + data_blob_free(&ntlmv2_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lmv2_response, + &ntlmv2_response, + flags, 0, + NULL, + user_session_key, + &authoritative, + &error_string, NULL); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + if (break_which != NO_NT && break_which != BREAK_NT && memcmp(ntlmv2_session_key.data, user_session_key, + sizeof(user_session_key)) != 0) { + DEBUG(1, ("USER (NTLMv2) Session Key does not match expectations!\n")); + DEBUG(1, ("user_session_key:\n")); + dump_data(1, user_session_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, ntlmv2_session_key.data, ntlmv2_session_key.length); + pass = False; + } + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static bool test_lmv2_ntlmv2(bool lanman_support_expected) +{ + return test_lmv2_ntlmv2_broken(BREAK_NONE); +} + +/* + * Test the LMv2 response only + */ + +static bool test_lmv2(bool lanman_support_expected) +{ + return test_lmv2_ntlmv2_broken(NO_NT); +} + +/* + * Test the NTLMv2 response only + */ + +static bool test_ntlmv2(bool lanman_support_expected) +{ + return test_lmv2_ntlmv2_broken(NO_LM); +} + +static bool test_lm_ntlm(bool lanman_support_expected) +{ + return test_lm_ntlm_broken(BREAK_NONE, lanman_support_expected); +} + +static bool test_ntlm_lm_broken(bool lanman_support_expected) +{ + return test_lm_ntlm_broken(BREAK_LM, lanman_support_expected); +} + +static bool test_ntlm_ntlm_broken(bool lanman_support_expected) +{ + return test_lm_ntlm_broken(BREAK_NT, lanman_support_expected); +} + +static bool test_ntlmv2_lmv2_broken(bool lanman_support_expected) +{ + return test_lmv2_ntlmv2_broken(BREAK_LM); +} + +static bool test_ntlmv2_ntlmv2_broken(bool lanman_support_expected) +{ + return test_lmv2_ntlmv2_broken(BREAK_NT); +} + +static bool test_plaintext(enum ntlm_break break_which) +{ + NTSTATUS nt_status; + uint32_t flags = 0; + DATA_BLOB nt_response = data_blob_null; + DATA_BLOB lm_response = data_blob_null; + char *password; + smb_ucs2_t *nt_response_ucs2; + size_t converted_size; + uint8_t authoritative = 1; + uchar user_session_key[16]; + uchar lm_key[16]; + static const uchar zeros[8] = { 0, }; + DATA_BLOB chall = data_blob(zeros, sizeof(zeros)); + char *error_string; + + ZERO_STRUCT(user_session_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_USER_SESSION_KEY; + + if (!push_ucs2_talloc(talloc_tos(), &nt_response_ucs2, opt_password, + &converted_size)) + { + DEBUG(0, ("push_ucs2_talloc failed!\n")); + exit(1); + } + + nt_response.data = (unsigned char *)nt_response_ucs2; + nt_response.length = strlen_w(nt_response_ucs2)*sizeof(smb_ucs2_t); + + if ((password = strupper_talloc(talloc_tos(), opt_password)) == NULL) { + DEBUG(0, ("strupper_talloc() failed!\n")); + exit(1); + } + + if (!convert_string_talloc(talloc_tos(), CH_UNIX, + CH_DOS, password, + strlen(password)+1, + &lm_response.data, + &lm_response.length)) { + DEBUG(0, ("convert_string_talloc failed!\n")); + exit(1); + } + + TALLOC_FREE(password); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + TALLOC_FREE(lm_response.data); + lm_response.length = 0; + break; + case NO_NT: + TALLOC_FREE(nt_response.data); + nt_response.length = 0; + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, MSV1_0_CLEARTEXT_PASSWORD_ALLOWED, + lm_key, + user_session_key, + &authoritative, + &error_string, NULL); + + TALLOC_FREE(nt_response.data); + TALLOC_FREE(lm_response.data); + data_blob_free(&chall); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + return break_which != BREAK_NT; +} + +static bool test_plaintext_none_broken(bool lanman_support_expected) { + return test_plaintext(BREAK_NONE); +} + +static bool test_plaintext_lm_broken(bool lanman_support_expected) { + return test_plaintext(BREAK_LM); +} + +static bool test_plaintext_nt_broken(bool lanman_support_expected) { + return test_plaintext(BREAK_NT); +} + +static bool test_plaintext_nt_only(bool lanman_support_expected) { + return test_plaintext(NO_LM); +} + +static bool test_plaintext_lm_only(bool lanman_support_expected) { + return test_plaintext(NO_NT); +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - NT in LM field + - NT in both fields + - NTLMv2 + - NTLMv2 and LMv2 + - LMv2 + - plaintext tests (in challenge-response fields) + + check we get the correct session key in each case + check what values we get for the LM session key + +*/ + +static const struct ntlm_tests { + bool (*fn)(bool lanman_support_expected); + const char *name; + bool lanman; +} test_table[] = { + { + .fn = test_lm, + .name = "LM", + .lanman = true + }, + { + .fn = test_lm_ntlm, + .name = "LM and NTLM" + }, + { + .fn = test_ntlm, + .name = "NTLM" + }, + { + .fn = test_ntlm_in_lm, + .name = "NTLM in LM" + }, + { + .fn = test_ntlm_in_both, + .name = "NTLM in both" + }, + { + .fn = test_ntlmv2, + .name = "NTLMv2" + }, + { + .fn = test_lmv2_ntlmv2, + .name = "NTLMv2 and LMv2" + }, + { + .fn = test_lmv2, + .name = "LMv2" + }, + { + .fn = test_ntlmv2_lmv2_broken, + .name = "NTLMv2 and LMv2, LMv2 broken" + }, + { + .fn = test_ntlmv2_ntlmv2_broken, + .name = "NTLMv2 and LMv2, NTLMv2 broken" + }, + { + .fn = test_ntlm_lm_broken, + .name = "NTLM and LM, LM broken" + }, + { + .fn = test_ntlm_ntlm_broken, + .name = "NTLM and LM, NTLM broken" + }, + { + .fn = test_plaintext_none_broken, + .name = "Plaintext" + }, + { + .fn = test_plaintext_lm_broken, + .name = "Plaintext LM broken" + }, + { + .fn = test_plaintext_nt_broken, + .name = "Plaintext NT broken" + }, + { + .fn = test_plaintext_nt_only, + .name = "Plaintext NT only" + }, + { + .fn = test_plaintext_lm_only, + .name = "Plaintext LM only", + .lanman = true + }, + { + .fn = NULL + } +}; + +bool diagnose_ntlm_auth(bool lanman_support_expected) +{ + unsigned int i; + bool pass = True; + + for (i=0; test_table[i].fn; i++) { + bool test_pass = test_table[i].fn(lanman_support_expected); + if (!lanman_support_expected + && test_table[i].lanman) { + if (test_pass) { + DBG_ERR("Test %s unexpectedly passed " + "(server should have rejected LM)!\n", + test_table[i].name); + pass = false; + } + } else if (!test_pass) { + DBG_ERR("Test %s failed!\n", test_table[i].name); + pass = False; + } + } + + return pass; +} + diff --git a/source3/utils/ntlm_auth_proto.h b/source3/utils/ntlm_auth_proto.h new file mode 100644 index 0000000..ed6d5f4 --- /dev/null +++ b/source3/utils/ntlm_auth_proto.h @@ -0,0 +1,51 @@ +/* + * Unix SMB/CIFS implementation. + * collected prototypes header + * + * frozen from "make proto" in May 2008 + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _NTLM_AUTH_PROTO_H_ +#define _NTLM_AUTH_PROTO_H_ + + +/* The following definitions come from utils/ntlm_auth.c */ + +const char *get_winbind_domain(void); +const char *get_winbind_netbios_name(void); +DATA_BLOB get_challenge(void) ; +NTSTATUS contact_winbind_auth_crap(const char *username, + const char *domain, + const char *workstation, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + uint32_t flags, + uint32_t extra_logon_parameters, + uint8_t lm_key[8], + uint8_t user_session_key[16], + uint8_t *pauthoritative, + char **error_string, + char **unix_name); + +/* The following definitions come from utils/ntlm_auth_diagnostics.c */ + +bool diagnose_ntlm_auth(bool lanman_support_expected); +int get_pam_winbind_config(void); + +#endif /* _NTLM_AUTH_PROTO_H_ */ diff --git a/source3/utils/passwd_proto.h b/source3/utils/passwd_proto.h new file mode 100644 index 0000000..4099899 --- /dev/null +++ b/source3/utils/passwd_proto.h @@ -0,0 +1,31 @@ +/* + * Unix SMB/CIFS implementation. + * collected prototypes header + * + * frozen from "make proto" in May 2008 + * + * Copyright (C) Michael Adam 2008 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _PASSWD_PROTO_H_ +#define _PASSWD_PROTO_H_ + + +/* The following definitions come from utils/passwd_util.c */ + +char *get_pass( const char *prompt, bool stdin_get); + +#endif /* _PASSWD_PROTO_H_ */ diff --git a/source3/utils/passwd_util.c b/source3/utils/passwd_util.c new file mode 100644 index 0000000..edd2c52 --- /dev/null +++ b/source3/utils/passwd_util.c @@ -0,0 +1,80 @@ +/* + Unix SMB/CIFS implementation. + passdb editing frontend + + Copyright (C) Jeremy Allison 1998 + Copyright (C) Andrew Tridgell 1998 + Copyright (C) Tim Potter 2000 + Copyright (C) Simo Sorce 2000 + Copyright (C) Martin Pool 2001 + Copyright (C) Gerald Carter 2002 + Copyright (C) Andrew Bartlett 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "passwd_proto.h" + +/************************************************************* + Utility function to prompt for passwords from stdin. Each + password entered must end with a newline. +*************************************************************/ +static char *stdin_new_passwd( void) +{ + static fstring new_pw; + size_t len; + + ZERO_ARRAY(new_pw); + + /* + * if no error is reported from fgets() and string at least contains + * the newline that ends the password, then replace the newline with + * a null terminator. + */ + if ( fgets(new_pw, sizeof(new_pw), stdin) == NULL) { + return NULL; + } + if ((len = strlen(new_pw)) > 0) { + if(new_pw[len-1] == '\n') + new_pw[len - 1] = 0; + } + return(new_pw); +} + +/************************************************************* + Utility function to get passwords via tty or stdin + Used if the '-s' (smbpasswd) or '-t' (pdbedit) option is set + to silently get passwords to enable scripting. +*************************************************************/ +char *get_pass( const char *prompt, bool stdin_get) +{ + char pwd[256] = {0}; + char *p; + int rc; + + if (stdin_get) { + p = stdin_new_passwd(); + if (p == NULL) { + return NULL; + } + } else { + rc = samba_getpass(prompt, pwd, sizeof(pwd), false, false); + if (rc < 0) { + return NULL; + } + p = pwd; + } + return smb_xstrdup( p); +} diff --git a/source3/utils/pdbedit.c b/source3/utils/pdbedit.c new file mode 100644 index 0000000..eb6b982 --- /dev/null +++ b/source3/utils/pdbedit.c @@ -0,0 +1,1414 @@ +/* + Unix SMB/CIFS implementation. + passdb editing frontend + + Copyright (C) Simo Sorce 2000-2009 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jelmer Vernooij 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "../librpc/gen_ndr/samr.h" +#include "../libcli/security/security.h" +#include "passdb.h" +#include "cmdline_contexts.h" +#include "passwd_proto.h" +#include "lib/util/smb_strtox.h" +#include "lib/param/param.h" + +#define BIT_BACKEND 0x00000004 +#define BIT_VERBOSE 0x00000008 +#define BIT_SPSTYLE 0x00000010 +#define BIT_CAN_CHANGE 0x00000020 +#define BIT_MUST_CHANGE 0x00000040 +#define BIT_USERSIDS 0x00000080 +#define BIT_FULLNAME 0x00000100 +#define BIT_HOMEDIR 0x00000200 +#define BIT_HDIRDRIVE 0x00000400 +#define BIT_LOGSCRIPT 0x00000800 +#define BIT_PROFILE 0x00001000 +#define BIT_MACHINE 0x00002000 +#define BIT_USERDOMAIN 0x00004000 +#define BIT_USER 0x00008000 +#define BIT_LIST 0x00010000 +#define BIT_MODIFY 0x00020000 +#define BIT_CREATE 0x00040000 +#define BIT_DELETE 0x00080000 +#define BIT_ACCPOLICY 0x00100000 +#define BIT_ACCPOLVAL 0x00200000 +#define BIT_ACCTCTRL 0x00400000 +#define BIT_RESERV_7 0x00800000 +#define BIT_IMPORT 0x01000000 +#define BIT_EXPORT 0x02000000 +#define BIT_FIX_INIT 0x04000000 +#define BIT_BADPWRESET 0x08000000 +#define BIT_LOGONHOURS 0x10000000 +#define BIT_KICKOFFTIME 0x20000000 +#define BIT_DESCRIPTION 0x40000000 +#define BIT_PWSETNTHASH 0x80000000 + +#define MASK_ALWAYS_GOOD 0x0000001F +#define MASK_USER_GOOD 0xE0405FE0 + +static int get_sid_from_cli_string(struct dom_sid *sid, const char *str_sid) +{ + uint32_t rid; + + if (!string_to_sid(sid, str_sid)) { + /* not a complete sid, may be a RID, + * try building a SID */ + + if (sscanf(str_sid, "%u", &rid) != 1) { + fprintf(stderr, "Error passed string is not " + "a complete SID or RID!\n"); + return -1; + } + sid_compose(sid, get_global_sam_sid(), rid); + } + + return 0; +} + +/********************************************************* + Add all currently available users to another db + ********************************************************/ + +static int export_database (struct pdb_methods *in, + struct pdb_methods *out, + const char *username) +{ + NTSTATUS status; + struct pdb_search *u_search; + struct samr_displayentry userentry; + + DEBUG(3, ("export_database: username=\"%s\"\n", username ? username : "(NULL)")); + + u_search = pdb_search_init(talloc_tos(), PDB_USER_SEARCH); + if (u_search == NULL) { + DEBUG(0, ("pdb_search_init failed\n")); + return 1; + } + + if (!in->search_users(in, u_search, 0)) { + DEBUG(0, ("Could not start searching users\n")); + TALLOC_FREE(u_search); + return 1; + } + + while (u_search->next_entry(u_search, &userentry)) { + struct samu *user; + struct samu *account; + struct dom_sid user_sid; + + DEBUG(4, ("Processing account %s\n", userentry.account_name)); + + if ((username != NULL) + && (strcmp(username, userentry.account_name) != 0)) { + /* + * ignore unwanted users + */ + continue; + } + + user = samu_new(talloc_tos()); + if (user == NULL) { + DEBUG(0, ("talloc failed\n")); + break; + } + + sid_compose(&user_sid, get_global_sam_sid(), userentry.rid); + + status = in->getsampwsid(in, user, &user_sid); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("getsampwsid failed: %s\n", + nt_errstr(status))); + TALLOC_FREE(user); + continue; + } + + account = samu_new(NULL); + if (account == NULL) { + fprintf(stderr, "export_database: Memory allocation " + "failure!\n"); + TALLOC_FREE( user ); + TALLOC_FREE(u_search); + return 1; + } + + printf("Importing account for %s...", user->username); + status = out->getsampwnam(out, account, user->username); + + if (NT_STATUS_IS_OK(status)) { + status = out->update_sam_account( out, user ); + } else { + status = out->add_sam_account(out, user); + } + + if ( NT_STATUS_IS_OK(status) ) { + printf( "ok\n"); + } else { + printf( "failed\n"); + } + + TALLOC_FREE( account ); + TALLOC_FREE( user ); + } + + TALLOC_FREE(u_search); + + return 0; +} + +/********************************************************* + Add all currently available group mappings to another db + ********************************************************/ + +static int export_groups (struct pdb_methods *in, struct pdb_methods *out) +{ + GROUP_MAP **maps = NULL; + size_t i, entries = 0; + NTSTATUS status; + + status = in->enum_group_mapping(in, get_global_sam_sid(), + SID_NAME_DOM_GRP, &maps, &entries, False); + + if ( NT_STATUS_IS_ERR(status) ) { + fprintf(stderr, "Unable to enumerate group map entries.\n"); + return 1; + } + + for (i=0; i<entries; i++) { + out->add_group_mapping_entry(out, maps[i]); + } + + TALLOC_FREE(maps); + + return 0; +} + +/********************************************************* + Reset account policies to their default values and remove marker + ********************************************************/ + +static int reinit_account_policies (void) +{ + int i; + + for (i=1; decode_account_policy_name(i) != NULL; i++) { + uint32_t policy_value; + if (!account_policy_get_default(i, &policy_value)) { + fprintf(stderr, "Can't get default account policy\n"); + return -1; + } + if (!account_policy_set(i, policy_value)) { + fprintf(stderr, "Can't set account policy in tdb\n"); + return -1; + } + } + + return 0; +} + + +/********************************************************* + Add all currently available account policy from tdb to one backend + ********************************************************/ + +static int export_account_policies (struct pdb_methods *in, struct pdb_methods *out) +{ + int i; + + for ( i=1; decode_account_policy_name(i) != NULL; i++ ) { + uint32_t policy_value; + NTSTATUS status; + + status = in->get_account_policy(in, i, &policy_value); + + if ( NT_STATUS_IS_ERR(status) ) { + fprintf(stderr, "Unable to get account policy from %s\n", in->name); + return -1; + } + + status = out->set_account_policy(out, i, policy_value); + + if ( NT_STATUS_IS_ERR(status) ) { + fprintf(stderr, "Unable to migrate account policy to %s\n", out->name); + return -1; + } + } + + return 0; +} + + +/********************************************************* + Print info from sam structure +**********************************************************/ + +static int print_sam_info (struct samu *sam_pwent, bool verbosity, bool smbpwdstyle) +{ + uid_t uid; + time_t tmp; + + /* TODO: check if entry is a user or a workstation */ + if (!sam_pwent) return -1; + + if (verbosity) { + char temp[44]; + const uint8_t *hours; + struct dom_sid_buf buf; + + printf ("Unix username: %s\n", pdb_get_username(sam_pwent)); + printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent)); + printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN)); + printf ("User SID: %s\n", + dom_sid_str_buf(pdb_get_user_sid(sam_pwent), &buf)); + printf ("Primary Group SID: %s\n", + dom_sid_str_buf(pdb_get_group_sid(sam_pwent), &buf)); + printf ("Full Name: %s\n", pdb_get_fullname(sam_pwent)); + printf ("Home Directory: %s\n", pdb_get_homedir(sam_pwent)); + printf ("HomeDir Drive: %s\n", pdb_get_dir_drive(sam_pwent)); + printf ("Logon Script: %s\n", pdb_get_logon_script(sam_pwent)); + printf ("Profile Path: %s\n", pdb_get_profile_path(sam_pwent)); + printf ("Domain: %s\n", pdb_get_domain(sam_pwent)); + printf ("Account desc: %s\n", pdb_get_acct_desc(sam_pwent)); + printf ("Workstations: %s\n", pdb_get_workstations(sam_pwent)); + printf ("Munged dial: %s\n", pdb_get_munged_dial(sam_pwent)); + + tmp = pdb_get_logon_time(sam_pwent); + printf ("Logon time: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_logoff_time(sam_pwent); + printf ("Logoff time: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_kickoff_time(sam_pwent); + printf ("Kickoff time: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_pass_last_set_time(sam_pwent); + printf ("Password last set: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_pass_can_change_time(sam_pwent); + printf ("Password can change: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_pass_must_change_time(sam_pwent); + printf ("Password must change: %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + + tmp = pdb_get_bad_password_time(sam_pwent); + printf ("Last bad password : %s\n", + tmp ? http_timestring(talloc_tos(), tmp) : "0"); + printf ("Bad password count : %d\n", + pdb_get_bad_password_count(sam_pwent)); + + hours = pdb_get_hours(sam_pwent); + pdb_sethexhours(temp, hours); + printf ("Logon hours : %s\n", temp); + if (smbpwdstyle){ + pdb_sethexpwd(temp, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + printf ("LM hash : %s\n", temp); + pdb_sethexpwd(temp, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + printf ("NT hash : %s\n", temp); + } + + } else if (smbpwdstyle) { + char lm_passwd[33]; + char nt_passwd[33]; + + uid = nametouid(pdb_get_username(sam_pwent)); + pdb_sethexpwd(lm_passwd, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + pdb_sethexpwd(nt_passwd, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + + printf("%s:%lu:%s:%s:%s:LCT-%08X:\n", + pdb_get_username(sam_pwent), + (unsigned long)uid, + lm_passwd, + nt_passwd, + pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32_t)convert_time_t_to_uint32_t(pdb_get_pass_last_set_time(sam_pwent))); + } else { + uid = nametouid(pdb_get_username(sam_pwent)); + printf ("%s:%lu:%s\n", pdb_get_username(sam_pwent), (unsigned long)uid, + pdb_get_fullname(sam_pwent)); + } + + return 0; +} + +/********************************************************* + Get an Print User Info +**********************************************************/ + +static int print_user_info(const char *username, + bool verbosity, bool smbpwdstyle) +{ + struct samu *sam_pwent = NULL; + bool bret; + int ret; + + sam_pwent = samu_new(NULL); + if (!sam_pwent) { + return -1; + } + + bret = pdb_getsampwnam(sam_pwent, username); + if (!bret) { + fprintf (stderr, "Username not found!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + ret = print_sam_info(sam_pwent, verbosity, smbpwdstyle); + + TALLOC_FREE(sam_pwent); + return ret; +} + +/********************************************************* + List Users +**********************************************************/ +static int print_users_list(bool verbosity, bool smbpwdstyle) +{ + struct pdb_search *u_search; + struct samr_displayentry userentry; + struct samu *sam_pwent; + TALLOC_CTX *tosctx; + struct dom_sid user_sid; + bool bret; + int ret; + + tosctx = talloc_tos(); + if (!tosctx) { + DEBUG(0, ("talloc failed\n")); + return 1; + } + + u_search = pdb_search_users(tosctx, 0); + if (!u_search) { + DEBUG(0, ("User Search failed!\n")); + ret = 1; + goto done; + } + + while (u_search->next_entry(u_search, &userentry)) { + + sam_pwent = samu_new(tosctx); + if (sam_pwent == NULL) { + DEBUG(0, ("talloc failed\n")); + ret = 1; + goto done; + } + + sid_compose(&user_sid, get_global_sam_sid(), userentry.rid); + + bret = pdb_getsampwsid(sam_pwent, &user_sid); + if (!bret) { + DEBUG(2, ("getsampwsid failed\n")); + TALLOC_FREE(sam_pwent); + continue; + } + + if (verbosity) { + printf ("---------------\n"); + } + print_sam_info(sam_pwent, verbosity, smbpwdstyle); + TALLOC_FREE(sam_pwent); + } + + ret = 0; + +done: + TALLOC_FREE(tosctx); + return ret; +} + +/********************************************************* + Fix a list of Users for uninitialised passwords +**********************************************************/ +static int fix_users_list(void) +{ + struct pdb_search *u_search; + struct samr_displayentry userentry; + struct samu *sam_pwent; + TALLOC_CTX *tosctx; + struct dom_sid user_sid; + NTSTATUS status; + bool bret; + int ret; + + tosctx = talloc_tos(); + if (!tosctx) { + fprintf(stderr, "Out of memory!\n"); + return 1; + } + + u_search = pdb_search_users(tosctx, 0); + if (!u_search) { + fprintf(stderr, "User Search failed!\n"); + ret = 1; + goto done; + } + + while (u_search->next_entry(u_search, &userentry)) { + + sam_pwent = samu_new(tosctx); + if (sam_pwent == NULL) { + fprintf(stderr, "Out of memory!\n"); + ret = 1; + goto done; + } + + sid_compose(&user_sid, get_global_sam_sid(), userentry.rid); + + bret = pdb_getsampwsid(sam_pwent, &user_sid); + if (!bret) { + DEBUG(2, ("getsampwsid failed\n")); + TALLOC_FREE(sam_pwent); + continue; + } + + status = pdb_update_sam_account(sam_pwent); + if (!NT_STATUS_IS_OK(status)) { + printf("Update of user %s failed!\n", + pdb_get_username(sam_pwent)); + } + TALLOC_FREE(sam_pwent); + } + + ret = 0; + +done: + TALLOC_FREE(tosctx); + return ret; +} + +/********************************************************* + Set User Info +**********************************************************/ + +static int set_user_info(const char *username, const char *fullname, + const char *homedir, const char *acct_desc, + const char *drive, const char *script, + const char *profile, const char *account_control, + const char *user_sid, const char *user_domain, + const bool badpw, const bool hours, + const char *kickoff_time, const char *str_hex_pwd) +{ + bool updated_autolock = False, updated_badpw = False; + struct samu *sam_pwent; + uint8_t hours_array[MAX_HOURS_LEN]; + uint32_t hours_len; + uint32_t acb_flags; + uint32_t not_settable; + uint32_t new_flags; + struct dom_sid u_sid; + bool ret; + + sam_pwent = samu_new(NULL); + if (!sam_pwent) { + return 1; + } + + ret = pdb_getsampwnam(sam_pwent, username); + if (!ret) { + fprintf (stderr, "Username not found!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + if (hours) { + hours_len = pdb_get_hours_len(sam_pwent); + memset(hours_array, 0xff, hours_len); + + pdb_set_hours(sam_pwent, hours_array, hours_len, PDB_CHANGED); + } + + if (!pdb_update_autolock_flag(sam_pwent, &updated_autolock)) { + DEBUG(2,("pdb_update_autolock_flag failed.\n")); + } + + if (!pdb_update_bad_password_count(sam_pwent, &updated_badpw)) { + DEBUG(2,("pdb_update_bad_password_count failed.\n")); + } + + if (fullname) + pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED); + if (acct_desc) + pdb_set_acct_desc(sam_pwent, acct_desc, PDB_CHANGED); + if (homedir) + pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED); + if (drive) + pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED); + if (script) + pdb_set_logon_script(sam_pwent, script, PDB_CHANGED); + if (profile) + pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED); + if (user_domain) + pdb_set_domain(sam_pwent, user_domain, PDB_CHANGED); + + if (account_control) { + not_settable = ~(ACB_DISABLED | ACB_HOMDIRREQ | + ACB_PWNOTREQ | ACB_PWNOEXP | ACB_AUTOLOCK); + + new_flags = pdb_decode_acct_ctrl(account_control); + + if (new_flags & not_settable) { + fprintf(stderr, "Can only set [NDHLX] flags\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + acb_flags = pdb_get_acct_ctrl(sam_pwent); + + pdb_set_acct_ctrl(sam_pwent, + (acb_flags & not_settable) | new_flags, + PDB_CHANGED); + } + if (user_sid) { + if (get_sid_from_cli_string(&u_sid, user_sid)) { + fprintf(stderr, "Failed to parse SID\n"); + return -1; + } + pdb_set_user_sid(sam_pwent, &u_sid, PDB_CHANGED); + } + + if (badpw) { + pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED); + pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED); + } + + if (kickoff_time) { + time_t value = get_time_t_max(); + + if (strcmp(kickoff_time, "never") != 0) { + int error = 0; + uint32_t num; + + num = smb_strtoul(kickoff_time, + NULL, + 10, + &error, + SMB_STR_FULL_STR_CONV); + if (error != 0) { + fprintf(stderr, "Failed to parse kickoff time\n"); + return -1; + } + + value = convert_uint32_t_to_time_t(num); + } + + pdb_set_kickoff_time(sam_pwent, value, PDB_CHANGED); + } + if (str_hex_pwd) { + unsigned char new_nt_p16[NT_HASH_LEN]; + if(strlen(str_hex_pwd) != (NT_HASH_LEN *2)){ + fprintf(stderr, "Invalid hash\n"); + return -1; + } + + pdb_gethexpwd(str_hex_pwd, new_nt_p16); + + if (!pdb_set_nt_passwd (sam_pwent, new_nt_p16 , PDB_CHANGED)) { + fprintf(stderr, "Failed to set password from nt-hash\n"); + return -1; + } + + if (!pdb_set_pass_last_set_time (sam_pwent, time(NULL), PDB_CHANGED)){ + fprintf(stderr, "Failed to set last password set time\n"); + return -1; + } + if (!pdb_update_history(sam_pwent, new_nt_p16)){ + fprintf(stderr, "Failed to update password history\n"); + return -1; + } + } + + if (NT_STATUS_IS_OK(pdb_update_sam_account(sam_pwent))) { + + print_user_info(username, True, (str_hex_pwd != NULL )); + } else { + fprintf (stderr, "Unable to modify entry!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + TALLOC_FREE(sam_pwent); + return 0; +} + +static int set_machine_info(const char *machinename, + const char *account_control, + const char *machine_sid) +{ + struct samu *sam_pwent = NULL; + TALLOC_CTX *tosctx; + uint32_t acb_flags; + uint32_t not_settable; + uint32_t new_flags; + struct dom_sid m_sid; + char *name; + int len; + bool ret; + + len = strlen(machinename); + if (len == 0) { + fprintf(stderr, "No machine name given\n"); + return -1; + } + + tosctx = talloc_tos(); + if (!tosctx) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + sam_pwent = samu_new(tosctx); + if (!sam_pwent) { + return 1; + } + + if (machinename[len-1] == '$') { + name = talloc_strdup(sam_pwent, machinename); + } else { + name = talloc_asprintf(sam_pwent, "%s$", machinename); + } + if (!name) { + fprintf(stderr, "Out of memory!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + if (!strlower_m(name)) { + fprintf(stderr, "strlower_m %s failed\n", name); + TALLOC_FREE(sam_pwent); + return -1; + } + + ret = pdb_getsampwnam(sam_pwent, name); + if (!ret) { + fprintf (stderr, "Username not found!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + if (account_control) { + not_settable = ~(ACB_DISABLED); + + new_flags = pdb_decode_acct_ctrl(account_control); + + if (new_flags & not_settable) { + fprintf(stderr, "Can only set [D] flags\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + + acb_flags = pdb_get_acct_ctrl(sam_pwent); + + pdb_set_acct_ctrl(sam_pwent, + (acb_flags & not_settable) | new_flags, + PDB_CHANGED); + } + if (machine_sid) { + if (get_sid_from_cli_string(&m_sid, machine_sid)) { + fprintf(stderr, "Failed to parse SID\n"); + return -1; + } + pdb_set_user_sid(sam_pwent, &m_sid, PDB_CHANGED); + } + + if (NT_STATUS_IS_OK(pdb_update_sam_account(sam_pwent))) { + print_user_info(name, True, False); + } else { + fprintf (stderr, "Unable to modify entry!\n"); + TALLOC_FREE(sam_pwent); + return -1; + } + TALLOC_FREE(sam_pwent); + return 0; +} + +/********************************************************* + Add New User +**********************************************************/ +static int new_user(const char *username, const char *fullname, + const char *homedir, const char *drive, + const char *script, const char *profile, + char *user_sid, bool stdin_get) +{ + char *pwd1 = NULL, *pwd2 = NULL; + char *err = NULL, *msg = NULL; + struct samu *sam_pwent = NULL; + TALLOC_CTX *tosctx; + NTSTATUS status; + struct dom_sid u_sid; + int flags; + int ret = -1; + + tosctx = talloc_tos(); + if (!tosctx) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (user_sid) { + if (get_sid_from_cli_string(&u_sid, user_sid)) { + fprintf(stderr, "Failed to parse SID\n"); + return -1; + } + } + + pwd1 = get_pass( "new password:", stdin_get); + if (pwd1 == NULL) { + fprintf(stderr, "Failed to read passwords.\n"); + goto done; + } + pwd2 = get_pass( "retype new password:", stdin_get); + if (pwd2 == NULL) { + fprintf(stderr, "Failed to read passwords.\n"); + goto done; + } + ret = strcmp(pwd1, pwd2); + if (ret != 0) { + fprintf (stderr, "Passwords do not match!\n"); + goto done; + } + + flags = LOCAL_ADD_USER | LOCAL_SET_PASSWORD; + + status = local_password_change(username, flags, pwd1, &err, &msg); + if (!NT_STATUS_IS_OK(status)) { + if (err) fprintf(stderr, "%s", err); + ret = -1; + goto done; + } + + sam_pwent = samu_new(tosctx); + if (!sam_pwent) { + fprintf(stderr, "Out of memory!\n"); + ret = -1; + goto done; + } + + if (!pdb_getsampwnam(sam_pwent, username)) { + fprintf(stderr, "User %s not found!\n", username); + ret = -1; + goto done; + } + + if (fullname) + pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED); + if (homedir) + pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED); + if (drive) + pdb_set_dir_drive(sam_pwent, drive, PDB_CHANGED); + if (script) + pdb_set_logon_script(sam_pwent, script, PDB_CHANGED); + if (profile) + pdb_set_profile_path(sam_pwent, profile, PDB_CHANGED); + if (user_sid) + pdb_set_user_sid(sam_pwent, &u_sid, PDB_CHANGED); + + status = pdb_update_sam_account(sam_pwent); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "Failed to modify entry for user %s.!\n", + username); + ret = -1; + goto done; + } + + print_user_info(username, True, False); + ret = 0; + +done: + if (pwd1) memset(pwd1, 0, strlen(pwd1)); + if (pwd2) memset(pwd2, 0, strlen(pwd2)); + SAFE_FREE(pwd1); + SAFE_FREE(pwd2); + SAFE_FREE(err); + SAFE_FREE(msg); + TALLOC_FREE(sam_pwent); + return ret; +} + +/********************************************************* + Add New Machine +**********************************************************/ + +static int new_machine(const char *machinename, char *machine_sid) +{ + char *err = NULL, *msg = NULL; + struct samu *sam_pwent = NULL; + TALLOC_CTX *tosctx; + NTSTATUS status; + struct dom_sid m_sid; + char *compatpwd; + char *name; + int flags; + int len; + int ret; + + len = strlen(machinename); + if (len == 0) { + fprintf(stderr, "No machine name given\n"); + return -1; + } + + tosctx = talloc_tos(); + if (!tosctx) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (machine_sid) { + if (get_sid_from_cli_string(&m_sid, machine_sid)) { + fprintf(stderr, "Failed to parse SID\n"); + return -1; + } + } + + compatpwd = talloc_strdup(tosctx, machinename); + if (!compatpwd) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (machinename[len-1] == '$') { + name = talloc_strdup(tosctx, machinename); + compatpwd[len-1] = '\0'; + } else { + name = talloc_asprintf(tosctx, "%s$", machinename); + } + if (!name) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (!strlower_m(name)) { + fprintf(stderr, "strlower_m %s failed\n", name); + return -1; + } + + flags = LOCAL_ADD_USER | LOCAL_TRUST_ACCOUNT | LOCAL_SET_PASSWORD; + + status = local_password_change(name, flags, compatpwd, &err, &msg); + + if (!NT_STATUS_IS_OK(status)) { + if (err) fprintf(stderr, "%s", err); + ret = -1; + } + + sam_pwent = samu_new(tosctx); + if (!sam_pwent) { + fprintf(stderr, "Out of memory!\n"); + ret = -1; + goto done; + } + + if (!pdb_getsampwnam(sam_pwent, name)) { + fprintf(stderr, "Machine %s not found!\n", name); + ret = -1; + goto done; + } + + if (machine_sid) + pdb_set_user_sid(sam_pwent, &m_sid, PDB_CHANGED); + + status = pdb_update_sam_account(sam_pwent); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, + "Failed to modify entry for %s.!\n", name); + ret = -1; + goto done; + } + + print_user_info(name, True, False); + ret = 0; + +done: + SAFE_FREE(err); + SAFE_FREE(msg); + TALLOC_FREE(sam_pwent); + return ret; +} + +/********************************************************* + Delete user entry +**********************************************************/ + +static int delete_user_entry(const char *username) +{ + struct samu *samaccount; + + samaccount = samu_new(NULL); + if (!samaccount) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (!pdb_getsampwnam(samaccount, username)) { + fprintf (stderr, + "user %s does not exist in the passdb\n", username); + TALLOC_FREE(samaccount); + return -1; + } + + if (!NT_STATUS_IS_OK(pdb_delete_sam_account(samaccount))) { + fprintf (stderr, "Unable to delete user %s\n", username); + TALLOC_FREE(samaccount); + return -1; + } + + TALLOC_FREE(samaccount); + return 0; +} + +/********************************************************* + Delete machine entry +**********************************************************/ + +static int delete_machine_entry(const char *machinename) +{ + struct samu *samaccount = NULL; + const char *name; + + if (strlen(machinename) == 0) { + fprintf(stderr, "No machine name given\n"); + return -1; + } + + samaccount = samu_new(NULL); + if (!samaccount) { + fprintf(stderr, "Out of memory!\n"); + return -1; + } + + if (machinename[strlen(machinename)-1] != '$') { + name = talloc_asprintf(samaccount, "%s$", machinename); + } else { + name = machinename; + } + + if (!pdb_getsampwnam(samaccount, name)) { + fprintf (stderr, + "machine %s does not exist in the passdb\n", name); + TALLOC_FREE(samaccount); + return -1; + } + + if (!NT_STATUS_IS_OK(pdb_delete_sam_account(samaccount))) { + fprintf (stderr, "Unable to delete machine %s\n", name); + TALLOC_FREE(samaccount); + return -1; + } + + TALLOC_FREE(samaccount); + return 0; +} + +/********************************************************* + Start here. +**********************************************************/ + +int main(int argc, const char **argv) +{ + static int list_users = False; + static int verbose = False; + static int spstyle = False; + static int machine = False; + static int add_user = False; + static int delete_user = False; + static int modify_user = False; + uint32_t setparms, checkparms; + int opt; + static char *full_name = NULL; + static char *acct_desc = NULL; + static const char *user_name = NULL; + static char *home_dir = NULL; + static char *home_drive = NULL; + static const char *backend = NULL; + static char *backend_in = NULL; + static char *backend_out = NULL; + static int transfer_groups = False; + static int transfer_account_policies = False; + static int reset_account_policies = False; + static int force_initialised_password = False; + static char *logon_script = NULL; + static char *profile_path = NULL; + static char *user_domain = NULL; + static char *account_control = NULL; + static char *account_policy = NULL; + static char *user_sid = NULL; + static char *machine_sid = NULL; + static long int account_policy_value = 0; + bool account_policy_value_set = False; + static int badpw_reset = False; + static int hours_reset = False; + static char *pwd_time_format = NULL; + static int pw_from_stdin = False; + struct pdb_methods *bin, *bout; + static char *kickoff_time = NULL; + static char *str_hex_pwd = NULL; + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + NTSTATUS status; + poptContext pc; + bool ok; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"list", 'L', POPT_ARG_NONE, &list_users, 0, "list all users", NULL}, + {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL }, + {"smbpasswd-style", 'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL}, + {"user", 'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" }, + {"account-desc", 'N', POPT_ARG_STRING, &acct_desc, 0, "set account description", NULL}, + {"fullname", 'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL}, + {"homedir", 'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL}, + {"drive", 'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL}, + {"script", 'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL}, + {"profile", 'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL}, + {"domain", 'I', POPT_ARG_STRING, &user_domain, 0, "set a users' domain", NULL}, + {"user SID", 'U', POPT_ARG_STRING, &user_sid, 0, "set user SID or RID", NULL}, + {"machine SID", 'M', POPT_ARG_STRING, &machine_sid, 0, "set machine SID or RID", NULL}, + {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL}, + {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL}, + {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL}, + {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL}, + {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL}, + {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL}, + {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL}, + {"group", 'g', POPT_ARG_NONE, &transfer_groups, 0, "use -i and -e for groups", NULL}, + {"policies", 'y', POPT_ARG_NONE, &transfer_account_policies, 0, "use -i and -e to move account policies between backends", NULL}, + {"policies-reset", 0, POPT_ARG_NONE, &reset_account_policies, 0, "restore default policies", NULL}, + {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL}, + {"value", 'C', POPT_ARG_LONG, &account_policy_value, 'C',"set the account policy to this value", NULL}, + {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL}, + {"force-initialized-passwords", 0, POPT_ARG_NONE, &force_initialised_password, 0, "Force initialization of corrupt password strings in a passdb backend", NULL}, + {"bad-password-count-reset", 'z', POPT_ARG_NONE, &badpw_reset, 0, "reset bad password count", NULL}, + {"logon-hours-reset", 'Z', POPT_ARG_NONE, &hours_reset, 0, "reset logon hours", NULL}, + {"time-format", 0, POPT_ARG_STRING, &pwd_time_format, 0, "The time format for time parameters", NULL }, + {"password-from-stdin", 't', POPT_ARG_NONE, &pw_from_stdin, 0, "get password from standard in", NULL}, + {"kickoff-time", 'K', POPT_ARG_STRING, &kickoff_time, 0, "set the kickoff time", NULL}, + {"set-nt-hash", 0, POPT_ARG_STRING, &str_hex_pwd, 0, "set password from nt-hash", NULL}, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + bin = bout = NULL; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'C': + account_policy_value_set = True; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptGetArg(pc); /* Drop argv[0], the program name */ + + if (user_name == NULL) { + if (poptPeekArg(pc)) { + user_name = talloc_strdup(frame, poptGetArg(pc)); + if (user_name == NULL) { + fprintf(stderr, "out of memory\n"); + TALLOC_FREE(frame); + exit(1); + } + } + } + + setparms = (backend ? BIT_BACKEND : 0) + + (verbose ? BIT_VERBOSE : 0) + + (spstyle ? BIT_SPSTYLE : 0) + + (full_name ? BIT_FULLNAME : 0) + + (home_dir ? BIT_HOMEDIR : 0) + + (home_drive ? BIT_HDIRDRIVE : 0) + + (logon_script ? BIT_LOGSCRIPT : 0) + + (profile_path ? BIT_PROFILE : 0) + + (user_domain ? BIT_USERDOMAIN : 0) + + (machine ? BIT_MACHINE : 0) + + (user_name ? BIT_USER : 0) + + (list_users ? BIT_LIST : 0) + + (force_initialised_password ? BIT_FIX_INIT : 0) + + (user_sid ? BIT_USERSIDS : 0) + + (machine_sid ? BIT_USERSIDS : 0) + + (modify_user ? BIT_MODIFY : 0) + + (add_user ? BIT_CREATE : 0) + + (delete_user ? BIT_DELETE : 0) + + (account_control ? BIT_ACCTCTRL : 0) + + (account_policy ? BIT_ACCPOLICY : 0) + + (account_policy_value_set ? BIT_ACCPOLVAL : 0) + + (backend_in ? BIT_IMPORT : 0) + + (backend_out ? BIT_EXPORT : 0) + + (badpw_reset ? BIT_BADPWRESET : 0) + + (hours_reset ? BIT_LOGONHOURS : 0) + + (kickoff_time ? BIT_KICKOFFTIME : 0) + + (str_hex_pwd ? BIT_PWSETNTHASH : 0 ) + + (acct_desc ? BIT_DESCRIPTION : 0); + + + if (setparms & BIT_BACKEND) { + /* HACK: set the global passdb backend by overwriting globals. + * This way we can use regular pdb functions for default + * operations that do not involve passdb migrations */ + lpcfg_set_cmdline(lp_ctx, "passdb backend", backend); + } else { + backend = lp_passdb_backend(); + } + + if (!initialize_password_db(False, NULL)) { + fprintf(stderr, "Can't initialize passdb backend.\n"); + exit(1); + } + + /* the lowest bit options are always accepted */ + checkparms = setparms & ~MASK_ALWAYS_GOOD; + + if (checkparms & BIT_FIX_INIT) { + poptFreeContext(pc); + return fix_users_list(); + } + + /* account policy operations */ + if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) { + uint32_t value; + enum pdb_policy_type field = account_policy_name_to_typenum(account_policy); + if (field == 0) { + const char **names; + int count; + int i; + account_policy_names_list(talloc_tos(), &names, &count); + fprintf(stderr, "No account policy by that name!\n"); + if (count !=0) { + fprintf(stderr, "Account policy names are:\n"); + for (i = 0; i < count ; i++) { + d_fprintf(stderr, "%s\n", names[i]); + } + } + TALLOC_FREE(names); + exit(1); + } + if (!pdb_get_account_policy(field, &value)) { + fprintf(stderr, "valid account policy, but unable to fetch value!\n"); + if (!account_policy_value_set) + exit(1); + } + printf("account policy \"%s\" description: %s\n", account_policy, account_policy_get_desc(field)); + if (account_policy_value_set) { + printf("account policy \"%s\" value was: %u\n", account_policy, value); + if (!pdb_set_account_policy(field, account_policy_value)) { + fprintf(stderr, "valid account policy, but unable to set value!\n"); + exit(1); + } + printf("account policy \"%s\" value is now: %lu\n", account_policy, account_policy_value); + exit(0); + } else { + printf("account policy \"%s\" value is: %u\n", account_policy, value); + exit(0); + } + } + + if (reset_account_policies) { + if (reinit_account_policies()) { + exit(1); + } + + exit(0); + } + + /* import and export operations */ + + if (((checkparms & BIT_IMPORT) || + (checkparms & BIT_EXPORT)) && + !(checkparms & ~(BIT_IMPORT +BIT_EXPORT +BIT_USER))) { + + poptFreeContext(pc); + + if (backend_in) { + status = make_pdb_method_name(&bin, backend_in); + } else { + status = make_pdb_method_name(&bin, backend); + } + + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "Unable to initialize %s.\n", + backend_in ? backend_in : backend); + return 1; + } + + if (backend_out) { + status = make_pdb_method_name(&bout, backend_out); + } else { + status = make_pdb_method_name(&bout, backend); + } + + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "Unable to initialize %s.\n", + backend_out ? backend_out : backend); + return 1; + } + + if (transfer_account_policies) { + + if (!(checkparms & BIT_USER)) { + return export_account_policies(bin, bout); + } + + } else if (transfer_groups) { + + if (!(checkparms & BIT_USER)) { + return export_groups(bin, bout); + } + + } else { + return export_database(bin, bout, + (checkparms & BIT_USER) ? user_name : NULL); + } + } + + /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */ + /* fake up BIT_LIST if only BIT_USER is defined */ + if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) { + checkparms += BIT_LIST; + } + + /* modify flag is optional to maintain backwards compatibility */ + /* fake up BIT_MODIFY if BIT_USER and at least one of MASK_USER_GOOD is defined */ + if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) { + checkparms += BIT_MODIFY; + } + + /* list users operations */ + if (checkparms & BIT_LIST) { + if (!(checkparms & ~BIT_LIST)) { + poptFreeContext(pc); + return print_users_list(verbose, spstyle); + } + if (!(checkparms & ~(BIT_USER + BIT_LIST))) { + poptFreeContext(pc); + return print_user_info(user_name, verbose, spstyle); + } + } + + /* mask out users options */ + checkparms &= ~MASK_USER_GOOD; + + /* if bad password count is reset, we must be modifying */ + if (checkparms & BIT_BADPWRESET) { + checkparms |= BIT_MODIFY; + checkparms &= ~BIT_BADPWRESET; + } + + /* if logon hours is reset, must modify */ + if (checkparms & BIT_LOGONHOURS) { + checkparms |= BIT_MODIFY; + checkparms &= ~BIT_LOGONHOURS; + } + + /* account operation */ + if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) { + /* check use of -u option */ + if (!(checkparms & BIT_USER)) { + fprintf (stderr, "Username not specified! (use -u option)\n"); + poptFreeContext(pc); + return -1; + } + + /* account creation operations */ + if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE))) { + poptFreeContext(pc); + if (checkparms & BIT_MACHINE) { + return new_machine(user_name, machine_sid); + } else { + return new_user(user_name, full_name, + home_dir, home_drive, + logon_script, profile_path, + user_sid, pw_from_stdin); + } + } + + /* account deletion operations */ + if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) { + poptFreeContext(pc); + if (checkparms & BIT_MACHINE) { + return delete_machine_entry(user_name); + } else { + return delete_user_entry(user_name); + } + } + + /* account modification operations */ + if (!(checkparms & ~(BIT_MODIFY + BIT_USER + BIT_MACHINE))) { + poptFreeContext(pc); + if (checkparms & BIT_MACHINE) { + return set_machine_info(user_name, + account_control, + machine_sid); + } else { + return set_user_info(user_name, full_name, + home_dir, acct_desc, + home_drive, logon_script, + profile_path, account_control, + user_sid, user_domain, + badpw_reset, hours_reset, + kickoff_time, str_hex_pwd); + } + } + } + + if (setparms >= 0x20) { + fprintf (stderr, "Incompatible or insufficient options on command line!\n"); + } + poptPrintHelp(pc, stderr, 0); + + gfree_all(); + poptFreeContext(pc); + TALLOC_FREE(frame); + return 1; +} diff --git a/source3/utils/profiles.c b/source3/utils/profiles.c new file mode 100644 index 0000000..ab1eb26 --- /dev/null +++ b/source3/utils/profiles.c @@ -0,0 +1,365 @@ +/* + Samba Unix/Linux SMB client utility profiles.c + + Copyright (C) Richard Sharpe, <rsharpe@richardsharpe.com> 2002 + Copyright (C) Jelmer Vernooij (conversion to popt) 2003 + Copyright (C) Gerald (Jerry) Carter 2005 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "registry/reg_objects.h" +#include "registry/regfio.h" +#include "../libcli/security/security.h" + +/* GLOBAL VARIABLES */ + +struct dom_sid old_sid, new_sid; +int change = 0, new_val = 0; +int opt_verbose = False; + +/******************************************************************** +********************************************************************/ + +static void verbose_output(const char *format, ...) PRINTF_ATTRIBUTE(1,2); +static void verbose_output(const char *format, ...) +{ + va_list args; + char *var = NULL; + + if (!opt_verbose) { + return; + } + + va_start(args, format); + if ((vasprintf(&var, format, args)) == -1) { + va_end(args); + return; + } + + fprintf(stdout, "%s", var); + va_end(args); + SAFE_FREE(var); +} + +/******************************************************************** +********************************************************************/ + +static bool swap_sid_in_acl( struct security_descriptor *sd, struct dom_sid *s1, struct dom_sid *s2 ) +{ + struct security_acl *theacl; + int i; + bool update = False; + struct dom_sid_buf buf; + + verbose_output(" Owner SID: %s\n", + dom_sid_str_buf(sd->owner_sid, &buf)); + if ( dom_sid_equal( sd->owner_sid, s1 ) ) { + sid_copy( sd->owner_sid, s2 ); + update = True; + verbose_output(" New Owner SID: %s\n", + dom_sid_str_buf(sd->owner_sid, &buf)); + + } + + verbose_output(" Group SID: %s\n", + dom_sid_str_buf(sd->group_sid, &buf)); + if ( dom_sid_equal( sd->group_sid, s1 ) ) { + sid_copy( sd->group_sid, s2 ); + update = True; + verbose_output(" New Group SID: %s\n", + dom_sid_str_buf(sd->group_sid, &buf)); + } + + theacl = sd->dacl; + verbose_output(" DACL: %d entries:\n", theacl->num_aces); + for ( i=0; i<theacl->num_aces; i++ ) { + verbose_output(" Trustee SID: %s\n", + dom_sid_str_buf(&theacl->aces[i].trustee, + &buf)); + if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) { + sid_copy( &theacl->aces[i].trustee, s2 ); + update = True; + verbose_output( + " New Trustee SID: %s\n", + dom_sid_str_buf(&theacl->aces[i].trustee, + &buf)); + } + } + +#if 0 + theacl = sd->sacl; + verbose_output(" SACL: %d entries: \n", theacl->num_aces); + for ( i=0; i<theacl->num_aces; i++ ) { + verbose_output(" Trustee SID: %s\n", + dom_sid_str_buf(&theacl->aces[i].trustee, + &buf)); + if ( dom_sid_equal( &theacl->aces[i].trustee, s1 ) ) { + sid_copy( &theacl->aces[i].trustee, s2 ); + update = True; + verbose_output( + " New Trustee SID: %s\n", + dom_sid_str_buf(&theacl->aces[i].trustee, + &buf)); + } + } +#endif + return update; +} + +/******************************************************************** +********************************************************************/ + +static bool copy_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk, + REGF_NK_REC *parent, REGF_FILE *outfile, + const char *parentpath ) +{ + REGF_NK_REC *key, *subkey; + struct security_descriptor *new_sd; + struct regval_ctr *values; + struct regsubkey_ctr *subkeys; + int i; + char *path; + WERROR werr; + + /* swap out the SIDs in the security descriptor */ + + if (nk->sec_desc->sec_desc == NULL) { + fprintf(stderr, "Invalid (NULL) security descriptor!\n"); + return false; + } + + new_sd = security_descriptor_copy(outfile->mem_ctx, + nk->sec_desc->sec_desc); + if (new_sd == NULL) { + fprintf(stderr, "Failed to copy security descriptor!\n"); + return False; + } + + verbose_output("ACL for %s%s%s\n", parentpath, parent ? "\\" : "", nk->keyname); + swap_sid_in_acl( new_sd, &old_sid, &new_sid ); + + werr = regsubkey_ctr_init(NULL, &subkeys); + if (!W_ERROR_IS_OK(werr)) { + DEBUG(0,("copy_registry_tree: talloc() failure!\n")); + return False; + } + + werr = regval_ctr_init(subkeys, &values); + if (!W_ERROR_IS_OK(werr)) { + TALLOC_FREE( subkeys ); + DEBUG(0,("copy_registry_tree: talloc() failure!\n")); + return False; + } + + /* copy values into the struct regval_ctr */ + + for ( i=0; i<nk->num_values; i++ ) { + regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type, + nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) ); + } + + /* copy subkeys into the struct regsubkey_ctr */ + + while ( (subkey = regfio_fetch_subkey( infile, nk )) ) { + regsubkey_ctr_addkey( subkeys, subkey->keyname ); + } + + key = regfio_write_key( outfile, nk->keyname, values, subkeys, new_sd, parent ); + + /* write each one of the subkeys out */ + + path = talloc_asprintf(subkeys, "%s%s%s", + parentpath, parent ? "\\" : "",nk->keyname); + if (!path) { + TALLOC_FREE( subkeys ); + return false; + } + + nk->subkey_index = 0; + while ((subkey = regfio_fetch_subkey(infile, nk))) { + if (!copy_registry_tree( infile, subkey, key, outfile, path)) { + TALLOC_FREE(subkeys); + return false; + } + } + + + verbose_output("[%s]\n", path); + + /* values is a talloc()'d child of subkeys here so just throw it all away */ + TALLOC_FREE(subkeys); + + return True; +} + +/********************************************************************* +*********************************************************************/ + +int main( int argc, const char *argv[] ) +{ + TALLOC_CTX *frame = talloc_stackframe(); + int opt; + REGF_FILE *infile, *outfile; + REGF_NK_REC *nk; + char *orig_filename, *new_filename; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "change-sid", + .shortName = 'c', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'c', + .descrip = "Provides SID to change", + }, + { + .longName = "new-sid", + .shortName = 'n', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'n', + .descrip = "Provides SID to change to", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &opt_verbose, + .val = 'v', + .descrip = "Verbose output", + }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + poptContext pc; + bool ok; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "<profilefile>"); + + /* Now, process the arguments */ + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'c': + change = 1; + if (!string_to_sid(&old_sid, poptGetOptArg(pc))) { + fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n"); + poptPrintUsage(pc, stderr, 0); + exit(254); + } + break; + + case 'n': + new_val = 1; + if (!string_to_sid(&new_sid, poptGetOptArg(pc))) { + fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n"); + poptPrintUsage(pc, stderr, 0); + exit(253); + } + break; + + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptGetArg(pc); + + if (!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + if ((!change && new_val) || (change && !new_val)) { + fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n"); + poptPrintUsage(pc, stderr, 0); + exit(252); + } + + orig_filename = talloc_strdup(frame, poptPeekArg(pc)); + if (!orig_filename) { + exit(ENOMEM); + } + new_filename = talloc_asprintf(frame, + "%s.new", + orig_filename); + if (!new_filename) { + exit(ENOMEM); + } + + if (!(infile = regfio_open( orig_filename, O_RDONLY, 0))) { + fprintf( stderr, "Failed to open %s!\n", orig_filename ); + fprintf( stderr, "Error was (%s)\n", strerror(errno) ); + exit (1); + } + + if ( !(outfile = regfio_open( new_filename, (O_RDWR|O_CREAT|O_TRUNC), + (S_IRUSR|S_IWUSR) )) ) { + fprintf( stderr, "Failed to open new file %s!\n", new_filename ); + fprintf( stderr, "Error was (%s)\n", strerror(errno) ); + exit (1); + } + + /* actually do the update now */ + + if ((nk = regfio_rootkey( infile )) == NULL) { + fprintf(stderr, "Could not get rootkey\n"); + exit(3); + } + + if (!copy_registry_tree( infile, nk, NULL, outfile, "")) { + fprintf(stderr, "Failed to write updated registry file!\n"); + exit(2); + } + + /* cleanup */ + + regfio_close(infile); + regfio_close(outfile); + + poptFreeContext(pc); + + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/utils/py_net.c b/source3/utils/py_net.c new file mode 100644 index 0000000..5e81b8f --- /dev/null +++ b/source3/utils/py_net.c @@ -0,0 +1,369 @@ +/* + Unix SMB/CIFS implementation. + Samba python bindings to s3 libnet library + + Copyright (C) David Mulder <dmulder@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "lib/replace/system/python.h" +#include "includes.h" +#include <pytalloc.h> +#include "python/modules.h" +#include "python/py3compat.h" +#include "rpc_client/rpc_client.h" +#include <sys/socket.h> +#include "net.h" +#include "auth/credentials/credentials.h" +#include "auth/credentials/pycredentials.h" +#include "lib/cmdline_contexts.h" +#include "param/loadparm.h" +#include "param/s3_param.h" +#include "param/pyparam.h" +#include "py_net.h" +#include "librpc/gen_ndr/libnet_join.h" +#include "libnet/libnet_join.h" +#include "libcli/security/dom_sid.h" +#include "dynconfig/dynconfig.h" + +static WERROR check_ads_config(struct loadparm_context *lp_ctx) +{ + if (lpcfg_server_role(lp_ctx) != ROLE_DOMAIN_MEMBER ) { + d_printf(_("Host is not configured as a member server.\n")); + return WERR_INVALID_DOMAIN_ROLE; + } + + if (strlen(lpcfg_netbios_name(lp_ctx)) > 15) { + d_printf(_("Our netbios name can be at most 15 chars long, " + "\"%s\" is %u chars long\n"), lpcfg_netbios_name(lp_ctx), + (unsigned int)strlen(lpcfg_netbios_name(lp_ctx))); + return WERR_INVALID_COMPUTERNAME; + } + + if ( lpcfg_security(lp_ctx) == SEC_ADS && !*lpcfg_realm(lp_ctx)) { + d_fprintf(stderr, _("realm must be set in %s for ADS " + "join to succeed.\n"), get_dyn_CONFIGFILE()); + return WERR_INVALID_PARAMETER; + } + + return WERR_OK; +} + +static PyObject *py_net_join_member(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + struct libnet_JoinCtx *r = NULL; + struct net_context *c; + WERROR werr; + PyObject *result; + TALLOC_CTX *mem_ctx; + int no_dns_updates = false, debug = false; + bool modify_config = lp_config_backend_is_registry(); + const char *kwnames[] = { "dnshostname", "createupn", "createcomputer", + "osName", "osVer", "osServicePack", + "machinepass", "debug", "noDnsUpdates", NULL }; + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + c = talloc_zero(mem_ctx, struct net_context); + c->msg_ctx = mem_ctx; + + werr = libnet_init_JoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_NoMemory(); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|sssssszpp:Join", + discard_const_p(char *, kwnames), + &r->in.dnshostname, + &r->in.upn, + &r->in.account_ou, + &r->in.os_name, + &r->in.os_version, + &r->in.os_servicepack, + &r->in.machine_password, + &debug, + &no_dns_updates)) { + talloc_free(mem_ctx); + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + if (!modify_config) { + werr = check_ads_config(self->lp_ctx); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + _("Invalid configuration. Exiting....\n")); + talloc_free(mem_ctx); + return NULL; + } + } + + r->in.domain_name = lpcfg_realm(self->lp_ctx); + r->in.domain_name_type = JoinDomNameTypeDNS; + r->in.create_upn = r->in.upn != NULL ? true : false; + r->in.dc_name = self->server_address; + r->in.admin_account = cli_credentials_get_username(self->creds); + r->in.admin_password = cli_credentials_get_password(self->creds); + r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds); + r->in.modify_config = modify_config; + r->in.join_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_CREATE | + WKSSVC_JOIN_FLAGS_DOMAIN_JOIN_IF_JOINED; + r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + r->in.debug = debug; + c->opt_user_name = r->in.admin_account; + c->opt_password = r->in.admin_password; + c->opt_kerberos = r->in.use_kerberos; + + werr = libnet_Join(mem_ctx, r); + if (W_ERROR_EQUAL(werr, WERR_NERR_DCNOTFOUND)) { + r->in.domain_name = lpcfg_workgroup(self->lp_ctx); + r->in.domain_name_type = JoinDomNameTypeNBT; + werr = libnet_Join(mem_ctx, r); + } + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + r->out.error_string + ? r->out.error_string + : get_friendly_werror_msg(werr)); + talloc_free(mem_ctx); + return NULL; + } + + /* + * Check the short name of the domain + */ + + if (!modify_config && !strequal(lpcfg_workgroup(self->lp_ctx), r->out.netbios_domain_name)) { + d_printf(_("The workgroup in %s does not match the short\n" + "domain name obtained from the server.\n" + "Using the name [%s] from the server.\n" + "You should set \"workgroup = %s\" in %s.\n"), + get_dyn_CONFIGFILE(), r->out.netbios_domain_name, + r->out.netbios_domain_name, get_dyn_CONFIGFILE()); + } + + /* + * We try doing the dns update (if it was compiled in + * and if it was not disabled on the command line). + * If the dns update fails, we still consider the join + * operation as succeeded if we came this far. + */ + if (!no_dns_updates) { + net_ads_join_dns_updates(c, mem_ctx, r); + } + + result = Py_BuildValue("ss", dom_sid_string(mem_ctx, r->out.domain_sid), + r->out.dns_domain_name); + + talloc_free(mem_ctx); + + return result; +} + +static const char py_net_join_member_doc[] = "join_member(dnshostname, createupn, createcomputer, osName, osVer, osServicePack, machinepass) -> (domain_sid, domain_name)\n\n" \ +"Join the domain with the specified name."; + +static PyObject *py_net_leave(py_net_Object *self, PyObject *args, PyObject *kwargs) +{ + struct libnet_UnjoinCtx *r = NULL; + WERROR werr; + TALLOC_CTX *mem_ctx; + int keep_account = false, debug = false; + const char *kwnames[] = { "keepAccount", "debug", NULL }; + + mem_ctx = talloc_new(self->mem_ctx); + if (mem_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + + if (!*lpcfg_realm(self->lp_ctx)) { + PyErr_FromString(_("No realm set, are we joined ?\n")); + return NULL; + } + + werr = libnet_init_UnjoinCtx(mem_ctx, &r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + _("Could not initialise unjoin context.\n")); + return NULL; + } + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|pp:Leave", + discard_const_p(char *, kwnames), + &keep_account, &debug)) { + talloc_free(mem_ctx); + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + r->in.use_kerberos = cli_credentials_get_kerberos_state(self->creds); + r->in.dc_name = self->server_address; + r->in.domain_name = lpcfg_realm(self->lp_ctx); + r->in.admin_account = cli_credentials_get_username(self->creds); + r->in.admin_password = cli_credentials_get_password(self->creds); + r->in.modify_config = lp_config_backend_is_registry(); + r->in.debug = debug; + + /* + * Try to delete it, but if that fails, disable it. The + * WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE really means "disable" + */ + r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE | + WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE; + if (keep_account) { + r->in.delete_machine_account = false; + } else { + r->in.delete_machine_account = true; + } + + r->in.msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + + werr = libnet_Unjoin(mem_ctx, r); + if (!W_ERROR_IS_OK(werr)) { + PyErr_SetWERROR_and_string(werr, + r->out.error_string + ? r->out.error_string + : get_friendly_werror_msg(werr)); + Py_RETURN_FALSE; + } + + if (r->out.deleted_machine_account) { + d_printf(_("Deleted account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + Py_RETURN_TRUE; + } + + if (r->out.disabled_machine_account) { + d_printf(_("Disabled account for '%s' in realm '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + werr = WERR_OK; + Py_RETURN_TRUE; + } + + /* + * Based on what we requested, we shouldn't get here, but if + * we did, it means the secrets were removed, and therefore + * we have left the domain. + */ + d_fprintf(stderr, _("Machine '%s' Left domain '%s'\n"), + r->in.machine_name, r->out.dns_domain_name); + Py_RETURN_TRUE; +} + +static const char py_net_leave_doc[] = "leave(keepAccount) -> success\n\n" \ +"Leave the joined domain."; + +static PyMethodDef net_obj_methods[] = { + { + .ml_name = "join_member", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_join_member), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_join_member_doc + }, + { + .ml_name = "leave", + .ml_meth = PY_DISCARD_FUNC_SIG(PyCFunction, + py_net_leave), + .ml_flags = METH_VARARGS|METH_KEYWORDS, + .ml_doc = py_net_leave_doc + }, + { .ml_name = NULL } +}; + +static void py_net_dealloc(py_net_Object *self) +{ + talloc_free(self->mem_ctx); + PyObject_Del(self); +} + +static PyObject *net_obj_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *py_creds, *py_lp = Py_None; + const char *kwnames[] = { "creds", "lp", "server", NULL }; + py_net_Object *ret; + const char *server_address = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|Oz", + discard_const_p(char *, kwnames), &py_creds, &py_lp, + &server_address)) { + PyErr_FromString(_("Invalid arguments\n")); + return NULL; + } + + ret = PyObject_New(py_net_Object, type); + if (ret == NULL) { + return NULL; + } + + ret->ev = samba_tevent_context_init(NULL); + ret->mem_ctx = talloc_stackframe(); + + ret->lp_ctx = lpcfg_from_py_object(ret->mem_ctx, py_lp); + if (ret->lp_ctx == NULL) { + Py_DECREF(ret); + return NULL; + } + + ret->server_address = server_address; + + ret->creds = cli_credentials_from_py_object(py_creds); + if (ret->creds == NULL) { + PyErr_SetString(PyExc_TypeError, "Expected credentials object"); + Py_DECREF(ret); + return NULL; + } + + return (PyObject *)ret; +} + + +PyTypeObject py_net_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + .tp_name = "net_s3.Net", + .tp_basicsize = sizeof(py_net_Object), + .tp_dealloc = (destructor)py_net_dealloc, + .tp_methods = net_obj_methods, + .tp_new = net_obj_new, +}; + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + .m_name = "net", + .m_size = -1, +}; + +MODULE_INIT_FUNC(net_s3) +{ + PyObject *m; + + if (PyType_Ready(&py_net_Type) < 0) + return NULL; + + m = PyModule_Create(&moduledef); + if (m == NULL) + return NULL; + + Py_INCREF(&py_net_Type); + PyModule_AddObject(m, "Net", (PyObject *)&py_net_Type); + + return m; +} diff --git a/source3/utils/py_net.h b/source3/utils/py_net.h new file mode 100644 index 0000000..9ace71b --- /dev/null +++ b/source3/utils/py_net.h @@ -0,0 +1,26 @@ +/* + Unix SMB/CIFS implementation. + Samba python bindings to s3 libnet library + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +typedef struct { + PyObject_HEAD + TALLOC_CTX *mem_ctx; + struct cli_credentials *creds; + struct loadparm_context *lp_ctx; + const char *server_address; + struct tevent_context *ev; +} py_net_Object; diff --git a/source3/utils/regedit.c b/source3/utils/regedit.c new file mode 100644 index 0000000..3259076 --- /dev/null +++ b/source3/utils/regedit.c @@ -0,0 +1,835 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "lib/param/param.h" +#include "lib/util/data_blob.h" +#include "lib/registry/registry.h" +#include "regedit.h" +#include "regedit_treeview.h" +#include "regedit_valuelist.h" +#include "regedit_dialog.h" +#include "regedit_list.h" +#include <ncurses.h> +#include <menu.h> +#include <panel.h> + +#define KEY_START_X 0 +#define KEY_START_Y 1 +#define KEY_WIDTH (COLS / 4) +#define KEY_HEIGHT (LINES - KEY_START_Y - 2) +#define VAL_START_X KEY_WIDTH +#define VAL_START_Y 1 +#define VAL_WIDTH (COLS - KEY_WIDTH) +#define VAL_HEIGHT (LINES - VAL_START_Y - 2) + +#define HELP1_START_Y (LINES - 2) +#define HELP1_START_X 0 +#define HELP1_WIDTH (LINES) +#define HELP2_START_Y (LINES - 1) +#define HELP2_START_X 0 +#define HELP2_WIDTH (LINES) +#define PATH_START_Y 0 +#define PATH_START_X 6 +#define PATH_MAX_Y (COLS - 1) +#define PATH_WIDTH (COLS - 6) +#define PATH_WIDTH_MAX 1024 + +struct regedit { + struct registry_context *registry_context; + WINDOW *main_window; + WINDOW *path_label; + size_t path_len; + struct value_list *vl; + struct tree_view *keys; + bool tree_input; + struct regedit_search_opts active_search; +}; + +static struct regedit *regedit_main = NULL; + +static void show_path(struct regedit *regedit) +{ + int start_pad = 0; + int start_win = PATH_START_X; + + if (PATH_START_X + regedit->path_len > COLS) { + start_pad = 3 + PATH_START_X + regedit->path_len - COLS; + mvprintw(PATH_START_Y, start_win, "..."); + start_win += 3; + } + copywin(regedit->path_label, regedit->main_window, 0, start_pad, + PATH_START_Y, start_win, PATH_START_Y, PATH_MAX_Y, false); + + mvchgat(0, 0, COLS, A_BOLD, PAIR_YELLOW_CYAN, NULL); +} + +static void print_path(struct regedit *regedit, struct tree_node *node) +{ + regedit->path_len = tree_node_print_path(regedit->path_label, node); + show_path(regedit); +} + +static void print_help(struct regedit *regedit) +{ + const char *khelp = "[n] New Key [s] New Subkey [d] Del Key " + "[LEFT] Ascend [RIGHT] Descend"; + const char *vhelp = "[n] New Value [d] Del Value [ENTER] Edit " + "[b] Edit binary"; + const char *msg = "KEYS"; + const char *help = khelp; + const char *genhelp = "[TAB] Switch sections [q] Quit " + "[UP] List up [DOWN] List down " + "[/] Search [x] Next"; + int i, pad; + + if (!regedit->tree_input) { + msg = "VALUES"; + help = vhelp; + } + + move(HELP1_START_Y, HELP1_START_X); + clrtoeol(); + attron(COLOR_PAIR(PAIR_BLACK_CYAN)); + mvaddstr(HELP1_START_Y, HELP1_START_X, help); + pad = COLS - strlen(msg) - strlen(help); + for (i = 0; i < pad; ++i) { + addch(' '); + } + attroff(COLOR_PAIR(PAIR_BLACK_CYAN)); + attron(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD); + addstr(msg); + attroff(COLOR_PAIR(PAIR_YELLOW_CYAN) | A_BOLD); + + move(HELP2_START_Y, HELP2_START_X); + clrtoeol(); + mvaddstr(HELP2_START_Y, HELP2_START_X, genhelp); +} + +static void print_heading(struct regedit *regedit) +{ + if (regedit->tree_input) { + tree_view_set_selected(regedit->keys, true); + value_list_set_selected(regedit->vl, false); + } else { + tree_view_set_selected(regedit->keys, false); + value_list_set_selected(regedit->vl, true); + } + + print_help(regedit); +} + +static void load_values(struct regedit *regedit) +{ + struct tree_node *node; + + node = tree_view_get_current_node(regedit->keys); + value_list_load(regedit->vl, node->key); +} + +static void add_reg_key(struct regedit *regedit, struct tree_node *node, + bool subkey) +{ + const char *name; + const char *msg; + + if (!subkey && tree_node_is_top_level(node)) { + return; + } + + msg = "Enter name of new key"; + if (subkey) { + msg = "Enter name of new subkey"; + } + dialog_input(regedit, &name, "New Key", "%s", msg); + if (name) { + WERROR rv; + struct registry_key *new_key; + struct tree_node *new_node = NULL; + struct tree_node *list; + struct tree_node *parent; + + if (subkey) { + parent = node; + list = node->child_head; + } else { + parent = node->parent; + list = tree_node_first(node); + SMB_ASSERT(list != NULL); + } + rv = reg_key_add_name(regedit, parent->key, name, + NULL, NULL, &new_key); + if (W_ERROR_IS_OK(rv)) { + /* The list of subkeys may not be present in + cache yet, so if not, don't bother allocating + a new node for the key. */ + if (list) { + new_node = tree_node_new(parent, parent, + name, new_key); + SMB_ASSERT(new_node); + tree_node_insert_sorted(list, new_node); + } else { + /* Reopen the parent key to make sure the + new subkey will be noticed. */ + tree_node_reopen_key(regedit->registry_context, + parent); + } + + list = tree_node_first(node); + tree_view_clear(regedit->keys); + tree_view_update(regedit->keys, list); + if (!subkey) { + node = new_node; + } + tree_view_set_current_node(regedit->keys, node); + load_values(regedit); + } else { + msg = get_friendly_werror_msg(rv); + dialog_notice(regedit, DIA_ALERT, "New Key", + "Failed to create key: %s", msg); + } + talloc_free(discard_const(name)); + } +} + +enum search_flags { + SEARCH_NEXT = (1<<0), + SEARCH_PREV = (1<<1), + SEARCH_REPEAT = (1<<2) +}; +static WERROR regedit_search(struct regedit *regedit, struct tree_node *node, + struct value_item *vitem, unsigned flags) +{ + struct regedit_search_opts *opts; + struct tree_node *found; + struct value_item *found_value; + bool search_key, need_sync; + char *save_value_name; + WERROR rv; + bool (*iterate)(struct tree_node **, bool, WERROR *); + struct value_item *(*find_item)(struct value_list *, + struct value_item *, + const char *, + regedit_search_match_fn_t); + + opts = ®edit->active_search; + + if (!opts->query || !opts->match) { + return WERR_OK; + } + + SMB_ASSERT(opts->search_key || opts->search_value); + + rv = WERR_OK; + found = NULL; + found_value = NULL; + save_value_name = NULL; + search_key = opts->search_key; + need_sync = false; + iterate = tree_node_next; + find_item = value_list_find_next_item; + + if (flags & SEARCH_PREV) { + iterate = tree_node_prev; + find_item = value_list_find_prev_item; + } + + if (opts->search_value) { + struct value_item *it; + + it = value_list_get_current_item(regedit->vl); + if (it) { + save_value_name = talloc_strdup(regedit, + it->value_name); + if (save_value_name == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + + if (vitem) { + search_key = false; + } + } + + if (!vitem && (flags & SEARCH_REPEAT)) { + if (opts->search_value) { + search_key = false; + } else if (!iterate(&node, opts->search_recursive, &rv)) { + beep(); + return rv; + } + } + + do { + if (search_key) { + SMB_ASSERT(opts->search_key == true); + if (opts->match(node->name, opts->query)) { + found = node; + } else if (opts->search_value) { + search_key = false; + } + } + if (!search_key) { + SMB_ASSERT(opts->search_value == true); + if (!vitem) { + rv = value_list_load_quick(regedit->vl, + node->key); + if (!W_ERROR_IS_OK(rv)) { + goto out; + } + need_sync = true; + } + found_value = find_item(regedit->vl, vitem, opts->query, + opts->match); + if (found_value) { + found = node; + } else { + vitem = NULL; + search_key = opts->search_key; + } + } + } while (!found && iterate(&node, opts->search_recursive, &rv)); + + if (!W_ERROR_IS_OK(rv)) { + goto out; + } + + if (found) { + /* Put the cursor on the node that was found */ + if (!tree_view_is_node_visible(regedit->keys, found)) { + tree_view_update(regedit->keys, + tree_node_first(found)); + print_path(regedit, found); + } + tree_view_set_current_node(regedit->keys, found); + if (found_value) { + if (need_sync) { + value_list_sync(regedit->vl); + } + value_list_set_current_item(regedit->vl, found_value); + regedit->tree_input = false; + } else { + load_values(regedit); + regedit->tree_input = true; + } + tree_view_show(regedit->keys); + value_list_show(regedit->vl); + print_heading(regedit); + } else { + if (need_sync) { + load_values(regedit); + value_list_set_current_item_by_name(regedit->vl, + save_value_name); + } + beep(); + } + +out: + talloc_free(save_value_name); + + return rv; +} + +static void regedit_search_repeat(struct regedit *regedit, unsigned flags) +{ + struct tree_node *node; + struct value_item *vitem; + struct regedit_search_opts *opts; + + opts = ®edit->active_search; + if (opts->query == NULL) { + return; + } + + node = tree_view_get_current_node(regedit->keys); + vitem = NULL; + if (opts->search_value && !regedit->tree_input) { + vitem = value_list_get_current_item(regedit->vl); + } + regedit_search(regedit, node, vitem, flags | SEARCH_REPEAT); +} + +static void handle_tree_input(struct regedit *regedit, int c) +{ + struct tree_node *node; + + switch (c) { + case KEY_DOWN: + tree_view_driver(regedit->keys, ML_CURSOR_DOWN); + load_values(regedit); + break; + case KEY_UP: + tree_view_driver(regedit->keys, ML_CURSOR_UP); + load_values(regedit); + break; + case KEY_NPAGE: + tree_view_driver(regedit->keys, ML_CURSOR_PGDN); + load_values(regedit); + break; + case KEY_PPAGE: + tree_view_driver(regedit->keys, ML_CURSOR_PGUP); + load_values(regedit); + break; + case KEY_HOME: + tree_view_driver(regedit->keys, ML_CURSOR_HOME); + load_values(regedit); + break; + case KEY_END: + tree_view_driver(regedit->keys, ML_CURSOR_END); + load_values(regedit); + break; + case '\n': + case KEY_ENTER: + case KEY_RIGHT: + node = tree_view_get_current_node(regedit->keys); + if (node && tree_node_has_children(node)) { + WERROR rv; + + rv = tree_node_load_children(node); + if (W_ERROR_IS_OK(rv)) { + print_path(regedit, node->child_head); + tree_view_update(regedit->keys, node->child_head); + value_list_load(regedit->vl, node->child_head->key); + } else { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(regedit, DIA_ALERT, "Loading Subkeys", + "Failed to load subkeys: %s", msg); + } + } + break; + case KEY_LEFT: + node = tree_view_get_current_node(regedit->keys); + if (node && !tree_node_is_top_level(node)) { + print_path(regedit, node->parent); + node = node->parent; + tree_view_update(regedit->keys, tree_node_first(node)); + tree_view_set_current_node(regedit->keys, node); + value_list_load(regedit->vl, node->key); + } + break; + case 'n': + case 'N': + node = tree_view_get_current_node(regedit->keys); + add_reg_key(regedit, node, false); + break; + case 's': + case 'S': + node = tree_view_get_current_node(regedit->keys); + add_reg_key(regedit, node, true); + break; + case 'd': + case 'D': { + int sel; + + node = tree_view_get_current_node(regedit->keys); + if (tree_node_is_top_level(node)) { + break; + } + sel = dialog_notice(regedit, DIA_CONFIRM, + "Delete Key", + "Really delete key \"%s\"?", + node->name); + if (sel == DIALOG_OK) { + WERROR rv; + struct tree_node *pop; + struct tree_node *parent = node->parent; + + rv = reg_key_del(node, parent->key, node->name); + if (W_ERROR_IS_OK(rv)) { + tree_node_reopen_key(regedit->registry_context, + parent); + tree_view_clear(regedit->keys); + pop = tree_node_pop(&node); + talloc_free(pop); + node = parent->child_head; + if (node == NULL) { + node = tree_node_first(parent); + print_path(regedit, node); + } + tree_view_update(regedit->keys, node); + value_list_load(regedit->vl, node->key); + } else { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(regedit, DIA_ALERT, "Delete Key", + "Failed to delete key: %s", msg); + } + } + break; + } + } + + tree_view_show(regedit->keys); + value_list_show(regedit->vl); +} + +static void handle_value_input(struct regedit *regedit, int c) +{ + struct value_item *vitem; + bool binmode = false; + WERROR err; + int sel; + + switch (c) { + case KEY_DOWN: + value_list_driver(regedit->vl, ML_CURSOR_DOWN); + break; + case KEY_UP: + value_list_driver(regedit->vl, ML_CURSOR_UP); + break; + case KEY_NPAGE: + value_list_driver(regedit->vl, ML_CURSOR_PGDN); + break; + case KEY_PPAGE: + value_list_driver(regedit->vl, ML_CURSOR_PGUP); + break; + case KEY_HOME: + value_list_driver(regedit->vl, ML_CURSOR_HOME); + break; + case KEY_END: + value_list_driver(regedit->vl, ML_CURSOR_END); + break; + case 'b': + case 'B': + binmode = true; + + FALL_THROUGH; + case '\n': + case KEY_ENTER: + vitem = value_list_get_current_item(regedit->vl); + if (vitem) { + struct tree_node *node; + const char *name = NULL; + node = tree_view_get_current_node(regedit->keys); + sel = dialog_edit_value(regedit, node->key, vitem->type, + vitem, binmode, &err, &name); + if (!W_ERROR_IS_OK(err)) { + const char *msg = get_friendly_werror_msg(err); + dialog_notice(regedit, DIA_ALERT, "Error", + "Error editing value:\n%s", msg); + } else if (sel == DIALOG_OK) { + tree_node_reopen_key(regedit->registry_context, + node); + value_list_load(regedit->vl, node->key); + value_list_set_current_item_by_name(regedit->vl, + name); + talloc_free(discard_const(name)); + } + } + break; + case 'n': + case 'N': { + int new_type; + + sel = dialog_select_type(regedit, &new_type); + if (sel == DIALOG_OK) { + struct tree_node *node; + const char *name = NULL; + node = tree_view_get_current_node(regedit->keys); + sel = dialog_edit_value(regedit, node->key, new_type, + NULL, false, &err, &name); + if (!W_ERROR_IS_OK(err)) { + const char *msg = get_friendly_werror_msg(err); + dialog_notice(regedit, DIA_ALERT, "Error", + "Error creating value:\n%s", msg); + } else if (sel == DIALOG_OK) { + tree_node_reopen_key(regedit->registry_context, + node); + value_list_load(regedit->vl, node->key); + value_list_set_current_item_by_name(regedit->vl, + name); + talloc_free(discard_const(name)); + } + } + break; + } + case 'd': + case 'D': + vitem = value_list_get_current_item(regedit->vl); + if (vitem) { + sel = dialog_notice(regedit, DIA_CONFIRM, + "Delete Value", + "Really delete value \"%s\"?", + vitem->value_name); + if (sel == DIALOG_OK) { + struct tree_node *node; + node = tree_view_get_current_node(regedit->keys); + reg_del_value(regedit, node->key, + vitem->value_name); + tree_node_reopen_key(regedit->registry_context, + node); + value_list_load(regedit->vl, node->key); + } + } + break; + } + + value_list_show(regedit->vl); +} + +static bool find_substring(const char *haystack, const char *needle) +{ + return strstr(haystack, needle) != NULL; +} + +static bool find_substring_nocase(const char *haystack, const char *needle) +{ + return strcasestr(haystack, needle) != NULL; +} + +static void handle_main_input(struct regedit *regedit, int c) +{ + switch (c) { + case 18: { /* CTRL-R */ + struct tree_node *root, *node; + const char **path; + + node = tree_view_get_current_node(regedit->keys); + path = tree_node_get_path(regedit, node); + SMB_ASSERT(path != NULL); + + root = tree_node_new_root(regedit, regedit->registry_context); + SMB_ASSERT(root != NULL); + + tree_view_set_root(regedit->keys, root); + tree_view_set_path(regedit->keys, path); + node = tree_view_get_current_node(regedit->keys); + value_list_load(regedit->vl, node->key); + tree_view_show(regedit->keys); + value_list_show(regedit->vl); + print_path(regedit, node); + talloc_free(discard_const(path)); + break; + } + case 'f': + case 'F': + case '/': { + int rv; + struct regedit_search_opts *opts; + struct tree_node *node; + + opts = ®edit->active_search; + rv = dialog_search_input(regedit, opts); + if (rv == DIALOG_OK) { + SMB_ASSERT(opts->query != NULL); + opts->match = find_substring_nocase; + node = regedit->keys->root->child_head; + if (opts->search_case) { + opts->match = find_substring; + } + if (!opts->search_recursive) { + node = tree_view_get_current_node(regedit->keys); + node = tree_node_first(node); + } + regedit_search(regedit, node, NULL, SEARCH_NEXT); + } + break; + } + case 'x': + regedit_search_repeat(regedit, SEARCH_NEXT); + break; + case 'X': + regedit_search_repeat(regedit, SEARCH_PREV); + break; + case '\t': + regedit->tree_input = !regedit->tree_input; + print_heading(regedit); + break; + default: + if (regedit->tree_input) { + handle_tree_input(regedit, c); + } else { + handle_value_input(regedit, c); + } + } +} + +int regedit_getch(void) +{ + int c; + + SMB_ASSERT(regedit_main); + + c = getch(); + if (c == KEY_RESIZE) { + tree_view_resize(regedit_main->keys, KEY_HEIGHT, KEY_WIDTH, + KEY_START_Y, KEY_START_X); + value_list_resize(regedit_main->vl, VAL_HEIGHT, VAL_WIDTH, + VAL_START_Y, VAL_START_X); + print_heading(regedit_main); + show_path(regedit_main); + } + + return c; +} + +static void regedit_panic_handler(const char *msg) +{ + endwin(); + smb_panic_log(msg); + smb_panic_s3(msg); +} + +static void display_window(TALLOC_CTX *mem_ctx, struct registry_context *ctx) +{ + struct regedit *regedit; + struct tree_node *root; + bool colors; + int key; + + initscr(); + + cbreak(); + noecho(); + + fault_configure(regedit_panic_handler); + + colors = has_colors(); + if (colors) { + start_color(); + use_default_colors(); + assume_default_colors(COLOR_WHITE, COLOR_BLUE); + init_pair(PAIR_YELLOW_CYAN, COLOR_YELLOW, COLOR_CYAN); + init_pair(PAIR_BLACK_CYAN, COLOR_BLACK, COLOR_CYAN); + init_pair(PAIR_YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE); + } + + regedit = talloc_zero(mem_ctx, struct regedit); + SMB_ASSERT(regedit != NULL); + regedit_main = regedit; + + regedit->registry_context = ctx; + regedit->main_window = stdscr; + keypad(regedit->main_window, TRUE); + + mvwprintw(regedit->main_window, 0, 0, "Path: "); + regedit->path_label = newpad(1, PATH_WIDTH_MAX); + SMB_ASSERT(regedit->path_label); + wprintw(regedit->path_label, "/"); + show_path(regedit_main); + + root = tree_node_new_root(regedit, ctx); + SMB_ASSERT(root != NULL); + + regedit->keys = tree_view_new(regedit, root, KEY_HEIGHT, KEY_WIDTH, + KEY_START_Y, KEY_START_X); + SMB_ASSERT(regedit->keys != NULL); + + regedit->vl = value_list_new(regedit, VAL_HEIGHT, VAL_WIDTH, + VAL_START_Y, VAL_START_X); + SMB_ASSERT(regedit->vl != NULL); + + regedit->tree_input = true; + print_heading(regedit); + + tree_view_show(regedit->keys); + load_values(regedit); + value_list_show(regedit->vl); + + update_panels(); + doupdate(); + + do { + key = regedit_getch(); + + handle_main_input(regedit, key); + update_panels(); + doupdate(); + } while (key != 'q' && key != 'Q'); + + endwin(); +} + +int main(int argc, char **argv) +{ + const char **argv_const = discard_const_p(const char *, argv); + struct poptOption long_options[] = { + POPT_AUTOHELP + /* ... */ + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_TABLEEND + }; + int opt; + poptContext pc; + TALLOC_CTX *frame; + struct registry_context *ctx; + WERROR rv; + bool ok; + struct loadparm_context *lp_ctx = NULL; + + frame = talloc_stackframe(); + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + + /* process options */ + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + poptFreeContext(pc); + samba_cmdline_burn(argc, argv); + + rv = reg_open_samba3(frame, &ctx); + if (!W_ERROR_IS_OK(rv)) { + fprintf(stderr, "Unable to open registry: %s\n", + win_errstr(rv)); + TALLOC_FREE(frame); + + return 1; + } + + display_window(frame, ctx); + + gfree_all(); + + TALLOC_FREE(frame); + + return 0; +} diff --git a/source3/utils/regedit.h b/source3/utils/regedit.h new file mode 100644 index 0000000..55a25c9 --- /dev/null +++ b/source3/utils/regedit.h @@ -0,0 +1,77 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _REGEDIT_H_ +#define _REGEDIT_H_ + +struct registry_context; +struct security_token; +struct registry_key; + +struct samba3_registry_key { + struct registry_key *key; +}; + +WERROR reg_openhive_wrap(TALLOC_CTX *ctx, const char *hive, + struct samba3_registry_key *key); +WERROR reg_openkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent, + const char *name, struct samba3_registry_key *key); +WERROR reg_enumvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + uint32_t idx, char **name, uint32_t *type, + DATA_BLOB *data); +WERROR reg_queryvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + const char *name, uint32_t *type, DATA_BLOB *data); +WERROR reg_enumkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + uint32_t idx, char **name, NTTIME *last_write_time); +WERROR reg_createkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent, + const char *subkeypath, + struct samba3_registry_key *pkey); +WERROR reg_deletekey_wrap(struct samba3_registry_key *parent, + const char *path); +WERROR reg_deletevalue_wrap(struct samba3_registry_key *key, const char *name); +WERROR reg_queryinfokey_wrap(struct samba3_registry_key *key, + uint32_t *num_subkeys, uint32_t *max_subkeylen, + uint32_t *max_subkeysize, uint32_t *num_values, + uint32_t *max_valnamelen, + uint32_t *max_valbufsize, uint32_t *secdescsize, + NTTIME *last_changed_time); +WERROR reg_setvalue_wrap(struct samba3_registry_key *key, const char *name, + uint32_t type, const DATA_BLOB data); +WERROR reg_init_wrap(void); + +WERROR reg_open_samba3(TALLOC_CTX *mem_ctx, struct registry_context **ctx); + +int regedit_getch(void); + +typedef bool (*regedit_search_match_fn_t)(const char *, const char *); + +struct regedit_search_opts { + const char *query; + regedit_search_match_fn_t match; + bool search_key; + bool search_value; + bool search_recursive; + bool search_case; +}; + +#define PAIR_YELLOW_CYAN 1 +#define PAIR_BLACK_CYAN 2 +#define PAIR_YELLOW_BLUE 3 + +#endif diff --git a/source3/utils/regedit_dialog.c b/source3/utils/regedit_dialog.c new file mode 100644 index 0000000..d1cb45f --- /dev/null +++ b/source3/utils/regedit_dialog.c @@ -0,0 +1,2328 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "regedit.h" +#include "regedit_dialog.h" +#include "regedit_valuelist.h" +#include "regedit_hexedit.h" +#include "util_reg.h" +#include "lib/registry/registry.h" +#include "lib/util/smb_strtox.h" +#include <stdarg.h> +#include <form.h> + +static char *string_trim_n(TALLOC_CTX *ctx, const char *buf, size_t n) +{ + char *str; + + str = talloc_strndup(ctx, buf, n); + + if (str) { + trim_string(str, " ", " "); + } + + return str; +} + +static char *string_trim(TALLOC_CTX *ctx, const char *buf) +{ + char *str; + + str = talloc_strdup(ctx, buf); + + if (str) { + trim_string(str, " ", " "); + } + + return str; +} + +static int dialog_free(struct dialog *dia) +{ + dialog_destroy(dia); + + return 0; +} + +static bool default_validator(struct dialog *dia, struct dialog_section *sect, + void *arg) +{ + return true; +} + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, const char *title, + int y, int x) +{ + struct dialog *dia; + + dia = talloc_zero(ctx, struct dialog); + if (dia == NULL) { + return NULL; + } + + talloc_set_destructor(dia, dialog_free); + + dia->title = talloc_strdup(dia, title); + if (dia->title == NULL) { + goto fail; + } + dia->x = x; + dia->y = y; + dia->color = color; + dia->submit = default_validator; + + return dia; + +fail: + talloc_free(dia); + + return NULL; + +} + +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg) +{ + dia->submit = cb; + dia->submit_arg = arg; +} + +static void center_above_window(int *nlines, int *ncols, int *y, int *x) +{ + int centery, centerx; + + centery = LINES / 2; + centerx = COLS / 2; + *y = 0; + *x = 0; + + if (*nlines > LINES) { + *nlines = LINES; + } + if (*ncols > COLS) { + *ncols = COLS; + } + + if (*nlines/2 < centery) { + *y = centery - *nlines / 2; + } + if (*ncols/2 < centerx) { + *x = centerx - *ncols / 2; + } +} + +void dialog_section_destroy(struct dialog_section *section) +{ + if (section->ops->destroy) { + section->ops->destroy(section); + } + if (section->window) { + delwin(section->window); + section->window = NULL; + } +} + +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols) +{ + section->ops = ops; + section->nlines = nlines; + section->ncols = ncols; +} + +const char *dialog_section_get_name(struct dialog_section *section) +{ + return section->name; +} + +void dialog_section_set_name(struct dialog_section *section, const char *name) +{ + TALLOC_FREE(section->name); + section->name = talloc_strdup(section, name); +} + +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify) +{ + section->justify = justify; +} + +/* append a section to the dialog's circular list */ +void dialog_append_section(struct dialog *dia, + struct dialog_section *section) +{ + SMB_ASSERT(section != NULL); + + if (!dia->head_section) { + dia->head_section = section; + } + if (dia->tail_section) { + dia->tail_section->next = section; + } + section->prev = dia->tail_section; + section->next = dia->head_section; + dia->head_section->prev = section; + dia->tail_section = section; +} + +struct dialog_section *dialog_find_section(struct dialog *dia, const char *name) +{ + struct dialog_section *section = dia->head_section; + + do { + if (section->name && strequal(section->name, name)) { + return section; + } + section = section->next; + } while (section != dia->head_section); + + return NULL; +} + +static void section_on_input(struct dialog *dia, int c) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_input) { + return; + } + section->ops->on_input(dia, section, c); +} + +static bool section_on_tab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_tab) { + return false; + } + return section->ops->on_tab(dia, section); +} + +static bool section_on_btab(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_btab) { + return false; + } + return section->ops->on_btab(dia, section); +} + +static bool section_on_up(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_up) { + return false; + } + return section->ops->on_up(dia, section); +} + +static bool section_on_down(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_down) { + return false; + } + return section->ops->on_down(dia, section); +} + +static bool section_on_left(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_left) { + return false; + } + return section->ops->on_left(dia, section); +} + +static bool section_on_right(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_right) { + return false; + } + return section->ops->on_right(dia, section); +} + +static enum dialog_action section_on_enter(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (!section || !section->ops->on_enter) { + return DIALOG_OK; + } + return section->ops->on_enter(dia, section); +} + +static bool section_on_focus(struct dialog *dia, bool forward) +{ + struct dialog_section *section = dia->current_section; + + if (!section->ops->on_focus) { + return false; + } + return section->ops->on_focus(dia, section, forward); +} + +static void section_on_leave_focus(struct dialog *dia) +{ + struct dialog_section *section = dia->current_section; + + if (section->ops->on_leave_focus) { + section->ops->on_leave_focus(dia, section); + } +} + +static void section_set_next_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->next; + } while (!section_on_focus(dia, true)); +} + +static void section_set_previous_focus(struct dialog *dia) +{ + section_on_leave_focus(dia); + + do { + dia->current_section = dia->current_section->prev; + } while (!section_on_focus(dia, false)); +} + +WERROR dialog_create(struct dialog *dia) +{ + WERROR rv = WERR_OK; + int row, col; + int nlines, ncols; + struct dialog_section *section; + + nlines = 0; + ncols = 0; + SMB_ASSERT(dia->head_section != NULL); + + /* calculate total size based on sections */ + section = dia->head_section; + do { + nlines += section->nlines; + ncols = MAX(ncols, section->ncols); + section = section->next; + } while (section != dia->head_section); + + /* fill in widths for sections that expand */ + section = dia->head_section; + do { + if (section->ncols < 0) { + section->ncols = ncols; + } + section = section->next; + } while (section != dia->head_section); + + /* create window for dialog */ + nlines += 4; + ncols += 6; + dia->pad = newpad(nlines, ncols); + if (dia->pad == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + dia->centered = false; + if (dia->y < 0 || dia->x < 0) { + dia->centered = true; + center_above_window(&nlines, &ncols, &dia->y, &dia->x); + } + dia->window = newwin(nlines, ncols, dia->y, dia->x); + if (dia->window == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + dia->panel = new_panel(dia->window); + if (dia->panel == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + + /* setup color and border */ + getmaxyx(dia->pad, nlines, ncols); + wbkgdset(dia->pad, ' ' | COLOR_PAIR(dia->color)); + wclear(dia->pad); + mvwhline(dia->pad, 1, 2, 0, ncols - 4); + mvwhline(dia->pad, nlines - 2, 2, 0, ncols - 4); + mvwvline(dia->pad, 2, 1, 0, nlines - 4); + mvwvline(dia->pad, 2, ncols - 2, 0, nlines - 4); + mvwaddch(dia->pad, 1, 1, ACS_ULCORNER); + mvwaddch(dia->pad, 1, ncols - 2, ACS_URCORNER); + mvwaddch(dia->pad, nlines - 2, 1, ACS_LLCORNER); + mvwaddch(dia->pad, nlines - 2, ncols - 2, ACS_LRCORNER); + col = ncols / 2 - MIN(strlen(dia->title) + 2, ncols) / 2; + mvwprintw(dia->pad, 1, col, " %s ", dia->title); + + /* create subwindows for each section */ + row = 2; + section = dia->head_section; + do { + col = 3; + + switch (section->justify) { + case SECTION_JUSTIFY_LEFT: + break; + case SECTION_JUSTIFY_CENTER: + col += (ncols - 6)/ 2 - section->ncols / 2; + break; + case SECTION_JUSTIFY_RIGHT: + break; + } + + section->window = subpad(dia->pad, section->nlines, + section->ncols, row, col); + if (section->window == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto fail; + } + SMB_ASSERT(section->ops->create != NULL); + rv = section->ops->create(dia, section); + row += section->nlines; + section = section->next; + } while (section != dia->head_section && W_ERROR_IS_OK(rv)); + + dia->current_section = dia->head_section; + section_set_next_focus(dia); + +fail: + return rv; +} + +void dialog_show(struct dialog *dia) +{ + int nlines, ncols; + int pad_y, pad_x; + int y, x; + int rv; + + touchwin(dia->pad); + getmaxyx(dia->window, nlines, ncols); + getmaxyx(dia->pad, pad_y, pad_x); + y = 0; + if (pad_y > nlines) { + y = (pad_y - nlines) / 2; + } + x = 0; + if (pad_x > ncols) { + x = (pad_x - ncols) / 2; + } + rv = copywin(dia->pad, dia->window, y, x, 0, 0, + nlines - 1, ncols - 1, false); + SMB_ASSERT(rv == OK); + + getyx(dia->pad, pad_y, pad_x); + wmove(dia->window, pad_y - y, pad_x - x); + touchwin(dia->window); + wnoutrefresh(dia->window); +} + +void dialog_destroy(struct dialog *dia) +{ + struct dialog_section *section; + + section = dia->head_section; + do { + dialog_section_destroy(section); + section = section->next; + } while (section != dia->head_section); + + if (dia->panel) { + del_panel(dia->panel); + dia->panel = NULL; + } + if (dia->window) { + delwin(dia->window); + dia->window = NULL; + } +} + +static int dialog_getch(struct dialog *dia) +{ + int c; + + c = regedit_getch(); + if (c == KEY_RESIZE) { + int nlines, ncols, y, x; + int pad_nlines, pad_ncols; + int win_nlines, win_ncols; + + getmaxyx(dia->window, win_nlines, win_ncols); + getmaxyx(dia->pad, pad_nlines, pad_ncols); + getbegyx(dia->window, y, x); + + nlines = pad_nlines; + ncols = pad_ncols; + + if (dia->centered) { + center_above_window(&nlines, &ncols, &y, &x); + } else { + if (nlines + y > LINES) { + if (nlines > LINES) { + y = 0; + } else { + y = LINES - nlines; + } + } + if (ncols + x > COLS) { + if (ncols > COLS) { + x = 0; + } else { + x = COLS - ncols; + } + } + } + if (nlines != win_nlines || ncols != win_ncols) { + wresize(dia->window, nlines, ncols); + replace_panel(dia->panel, dia->window); + } + move_panel(dia->panel, y, x); + } + + return c; +} + +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action) +{ + int c; + + *err = WERR_OK; + + c = dialog_getch(dia); + + switch (c) { + case '\t': + if (!section_on_tab(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_BTAB: + if (!section_on_btab(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_UP: + if (!section_on_up(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_DOWN: + if (!section_on_down(dia)) { + section_set_next_focus(dia); + } + break; + case KEY_LEFT: + if (!section_on_left(dia)) { + section_set_previous_focus(dia); + } + break; + case KEY_RIGHT: + if (!section_on_right(dia)) { + section_set_next_focus(dia); + } + break; + case '\n': + case KEY_ENTER: + *action = section_on_enter(dia); + switch (*action) { + case DIALOG_IGNORE: + break; + case DIALOG_CANCEL: + return false; + case DIALOG_OK: + return !dia->submit(dia, dia->current_section, + dia->submit_arg); + } + break; + case 27: /* ESC */ + return false; + default: + section_on_input(dia, c); + break; + } + + return true; +} + +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action) +{ + do { + dialog_show(dia); + update_panels(); + doupdate(); + } while (dialog_handle_input(dia, err, action)); +} + +/* text label */ +struct dialog_section_label { + struct dialog_section section; + char **text; +}; + +static WERROR label_create(struct dialog *dia, struct dialog_section *section) +{ + int row; + struct dialog_section_label *label = + talloc_get_type_abort(section, struct dialog_section_label); + + for (row = 0; row < section->nlines; ++row) { + mvwaddstr(section->window, row, 0, label->text[row]); + } + + return WERR_OK; +} + +struct dialog_section_ops label_ops = { + .create = label_create, +}; + +static int label_free(struct dialog_section_label *label) +{ + dialog_section_destroy(&label->section); + return 0; +} + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, va_list ap) +{ + struct dialog_section_label *label; + char *tmp, *ptmp, *line, *saveptr; + int nlines, ncols; + + label = talloc_zero(ctx, struct dialog_section_label); + if (label == NULL) { + return NULL; + } + talloc_set_destructor(label, label_free); + tmp = talloc_vasprintf(label, msg, ap); + if (tmp == NULL) { + goto fail; + } + + for (nlines = 0, ncols = 0, ptmp = tmp; + (line = strtok_r(ptmp, "\n", &saveptr)) != NULL; + ++nlines) { + ptmp = NULL; + label->text = talloc_realloc(label, label->text, + char *, nlines + 1); + if (label->text == NULL) { + goto fail; + } + ncols = MAX(ncols, strlen(line)); + label->text[nlines] = talloc_strdup(label->text, line); + if (label->text[nlines] == NULL) { + goto fail; + } + } + talloc_free(tmp); + dialog_section_init(&label->section, &label_ops, nlines, ncols); + + return &label->section; + +fail: + talloc_free(label); + return NULL; +} + +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...) +{ + va_list ap; + struct dialog_section *rv; + + va_start(ap, msg); + rv = dialog_section_label_new_va(ctx, msg, ap); + va_end(ap); + + return rv; +} + +/* horizontal separator */ +struct dialog_section_hsep { + struct dialog_section section; + int sep; +}; + +static WERROR hsep_create(struct dialog *dia, struct dialog_section *section) +{ + int y, x; + struct dialog_section_hsep *hsep = + talloc_get_type_abort(section, struct dialog_section_hsep); + + whline(section->window, hsep->sep, section->ncols); + + if (hsep->sep == 0 || hsep->sep == ACS_HLINE) { + /* change the border characters around this section to + tee chars */ + getparyx(section->window, y, x); + mvwaddch(dia->pad, y, x - 1, ACS_HLINE); + mvwaddch(dia->pad, y, x - 2, ACS_LTEE); + mvwaddch(dia->pad, y, x + section->ncols, ACS_HLINE); + mvwaddch(dia->pad, y, x + section->ncols + 1, ACS_RTEE); + } + + return WERR_OK; +} + +struct dialog_section_ops hsep_ops = { + .create = hsep_create +}; + +static int hsep_free(struct dialog_section_hsep *hsep) +{ + dialog_section_destroy(&hsep->section); + return 0; +} + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep) +{ + struct dialog_section_hsep *hsep; + + hsep = talloc_zero(ctx, struct dialog_section_hsep); + if (hsep) { + talloc_set_destructor(hsep, hsep_free); + dialog_section_init(&hsep->section, &hsep_ops, 1, -1); + hsep->sep = sep; + } + + return &hsep->section; +} + +/* text input field */ +struct dialog_section_text_field { + struct dialog_section section; + unsigned opts; + FIELD *field[2]; + FORM *form; + int length; +}; + +static int get_cursor_col(struct dialog_section_text_field *field) +{ + int col; + + col = field->form->curcol + field->form->begincol; + + return col; +} + +static WERROR text_field_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + text_field->field[0] = new_field(section->nlines, section->ncols, + 0, 0, 0, 0); + if (text_field->field[0] == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + set_field_back(text_field->field[0], A_REVERSE); + set_field_opts(text_field->field[0], text_field->opts); + + text_field->form = new_form(text_field->field); + if (text_field->form == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + set_form_win(text_field->form, dia->window); + set_form_sub(text_field->form, section->window); + set_current_field(text_field->form, text_field->field[0]); + post_form(text_field->form); + + return WERR_OK; +} + +static void text_field_destroy(struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (text_field->form) { + unpost_form(text_field->form); + free_form(text_field->form); + text_field->form = NULL; + } + if (text_field->field[0]) { + free_field(text_field->field[0]); + text_field->field[0] = NULL; + } +} + +static void text_field_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + switch (c) { + case KEY_BACKSPACE: + if (text_field->length) { + text_field->length--; + } + form_driver(text_field->form, REQ_DEL_PREV); + break; + case '\x7f': + case KEY_DC: + if (text_field->length) { + text_field->length--; + } + form_driver(text_field->form, REQ_DEL_CHAR); + break; + default: + text_field->length++; + form_driver(text_field->form, c); + break; + } +} + +static bool text_field_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_UP_CHAR); + return true; + } + return false; +} + +static bool text_field_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + form_driver(text_field->form, REQ_DOWN_CHAR); + return true; + } + return false; +} + +static bool text_field_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_LEFT_CHAR); + + return true; +} + +static bool text_field_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1 || + get_cursor_col(text_field) < text_field->length) { + form_driver(text_field->form, REQ_RIGHT_CHAR); + } + + return true; +} + +static enum dialog_action text_field_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + if (section->nlines > 1) { + text_field->length += text_field->form->cols; + form_driver(text_field->form, REQ_NEW_LINE); + return DIALOG_IGNORE; + } + + return DIALOG_OK; +} + +static bool text_field_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + pos_form_cursor(text_field->form); + + return true; +} + +struct dialog_section_ops text_field_ops = { + .create = text_field_create, + .destroy = text_field_destroy, + .on_input = text_field_on_input, + .on_up = text_field_on_up, + .on_down = text_field_on_down, + .on_left = text_field_on_left, + .on_right = text_field_on_right, + .on_enter = text_field_on_enter, + .on_focus = text_field_on_focus +}; + +static int text_field_free(struct dialog_section_text_field *text_field) +{ + dialog_section_destroy(&text_field->section); + return 0; +} + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width) +{ + struct dialog_section_text_field *text_field; + + text_field = talloc_zero(ctx, struct dialog_section_text_field); + if (text_field == NULL) { + return NULL; + } + talloc_set_destructor(text_field, text_field_free); + dialog_section_init(&text_field->section, &text_field_ops, + height, width); + text_field->opts = O_ACTIVE | O_PUBLIC | O_EDIT | O_VISIBLE | O_NULLOK; + + return &text_field->section; +} + +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + return string_trim(ctx, field_buffer(text_field->field[0], 0)); +} + +void dialog_section_text_field_set(struct dialog_section *section, + const char *s) +{ + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + text_field->length = strlen(s); + set_field_buffer(text_field->field[0], 0, s); +} + +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section) +{ + int rows, cols, max; + const char **arr; + size_t i; + const char *buf; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + buf = field_buffer(text_field->field[0], 0); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + + arr = talloc_zero_array(ctx, const char *, rows + 1); + if (arr == NULL) { + return NULL; + } + for (i = 0; *buf; ++i, buf += cols) { + SMB_ASSERT(i < rows); + arr[i] = string_trim_n(arr, buf, cols); + } + + return arr; +} + +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array) +{ + int rows, cols, max; + size_t padding, length, idx; + const char **arrayp; + char *buf = NULL; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + dynamic_field_info(text_field->field[0], &rows, &cols, &max); + /* try to fit each string on it's own line. each line + needs to be padded with whitespace manually, since + ncurses fields do not have newlines. */ + for (idx = 0, arrayp = array; *arrayp != NULL; ++arrayp) { + length = MIN(strlen(*arrayp), cols); + padding = cols - length; + buf = talloc_realloc(ctx, buf, char, + talloc_array_length(buf) + + length + padding + 1); + if (buf == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + memcpy(&buf[idx], *arrayp, length); + idx += length; + memset(&buf[idx], ' ', padding); + idx += padding; + buf[idx] = '\0'; + } + + set_field_buffer(text_field->field[0], 0, buf); + talloc_free(buf); + + return WERR_OK; +} + +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out) +{ + bool rv; + const char *buf; + char *endp; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = strtoll(buf, &endp, 0); + rv = true; + if (endp == buf || endp == NULL || endp[0] != '\0') { + rv = false; + } + + return rv; +} + + +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out) +{ + const char *buf; + int error = 0; + struct dialog_section_text_field *text_field = + talloc_get_type_abort(section, struct dialog_section_text_field); + + form_driver(text_field->form, REQ_VALIDATION); + + buf = string_trim(section, field_buffer(text_field->field[0], 0)); + if (buf == NULL) { + return false; + } + *out = smb_strtoull(buf, NULL, 0, &error, SMB_STR_FULL_STR_CONV); + if (error != 0) { + return false; + } + + return true; +} + +/* hex editor field */ +struct dialog_section_hexedit { + struct dialog_section section; + struct hexedit *buf; +}; + +#define HEXEDIT_MIN_SIZE 1 +static WERROR hexedit_create(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit->buf = hexedit_new(dia, section->window, NULL, + HEXEDIT_MIN_SIZE); + if (hexedit->buf == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + hexedit_refresh(hexedit->buf); + + return WERR_OK; +} + +static void hexedit_destroy(struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + if (hexedit->buf) { + TALLOC_FREE(hexedit->buf); + } +} + +static void hexedit_on_input(struct dialog *dia, + struct dialog_section *section, + int c) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + switch (c) { + case KEY_BACKSPACE: + hexedit_driver(hexedit->buf, HE_BACKSPACE); + break; + case '\x7f': + case KEY_DC: + hexedit_driver(hexedit->buf, HE_DELETE); + break; + default: + hexedit_driver(hexedit->buf, c); + break; + } +} + +static bool hexedit_on_up(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_UP); + + return true; +} + +static bool hexedit_on_down(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_DOWN); + + return true; +} + +static bool hexedit_on_left(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_LEFT); + + return true; +} + +static bool hexedit_on_right(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_driver(hexedit->buf, HE_CURSOR_RIGHT); + + return true; +} + +static enum dialog_action hexedit_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + return DIALOG_IGNORE; +} + +static bool hexedit_on_focus(struct dialog *dia, + struct dialog_section *section, bool forward) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + hexedit_set_cursor(hexedit->buf); + + return true; +} + +struct dialog_section_ops hexedit_ops = { + .create = hexedit_create, + .destroy = hexedit_destroy, + .on_input = hexedit_on_input, + .on_up = hexedit_on_up, + .on_down = hexedit_on_down, + .on_left = hexedit_on_left, + .on_right = hexedit_on_right, + .on_enter = hexedit_on_enter, + .on_focus = hexedit_on_focus +}; + +static int hexedit_free(struct dialog_section_hexedit *hexedit) +{ + dialog_section_destroy(&hexedit->section); + return 0; +} + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height) +{ + struct dialog_section_hexedit *hexedit; + + hexedit = talloc_zero(ctx, struct dialog_section_hexedit); + if (hexedit == NULL) { + return NULL; + } + talloc_set_destructor(hexedit, hexedit_free); + dialog_section_init(&hexedit->section, &hexedit_ops, + height, LINE_WIDTH); + + return &hexedit->section; +} + +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + + rv = hexedit_set_buf(hexedit->buf, data, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + hexedit_set_cursor(hexedit->buf); + } + + return rv; +} + +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size) +{ + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + *data = hexedit_get_buf(hexedit->buf); + *size = hexedit_get_buf_len(hexedit->buf); +} + +WERROR dialog_section_hexedit_resize(struct dialog_section *section, + size_t size) +{ + WERROR rv; + struct dialog_section_hexedit *hexedit = + talloc_get_type_abort(section, struct dialog_section_hexedit); + + SMB_ASSERT(hexedit->buf != NULL); + rv = hexedit_resize_buffer(hexedit->buf, size); + if (W_ERROR_IS_OK(rv)) { + hexedit_refresh(hexedit->buf); + } + + return rv; +} + + +/* button box */ +struct dialog_section_buttons { + struct dialog_section section; + struct button_spec *spec; + int current_button; +}; + +static void buttons_unhighlight(struct dialog_section_buttons *buttons) +{ + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + wnoutrefresh(buttons->section.window); +} + +static void buttons_highlight(struct dialog_section_buttons *buttons) +{ + struct button_spec *spec = &buttons->spec[buttons->current_button]; + short pair; + attr_t attr; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(buttons->section.window, &attr, &pair, NULL); + mvwchgat(buttons->section.window, 0, 0, -1, A_NORMAL, pair, NULL); + mvwchgat(buttons->section.window, 0, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(buttons->section.window, 0, spec->col + 2); + wcursyncup(buttons->section.window); + wnoutrefresh(buttons->section.window); +} + +static bool buttons_highlight_next(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button < talloc_array_length(buttons->spec) - 1) { + buttons->current_button++; + buttons_highlight(buttons); + return true; + } + return false; +} + +static bool buttons_highlight_previous(struct dialog_section_buttons *buttons) +{ + if (buttons->current_button > 0) { + buttons->current_button--; + buttons_highlight(buttons); + return true; + } + return false; +} + +static WERROR buttons_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, nbuttons; + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + nbuttons = talloc_array_length(buttons->spec); + for (i = 0; i < nbuttons; ++i) { + struct button_spec *spec = &buttons->spec[i]; + mvwaddstr(section->window, 0, spec->col, spec->label); + } + + buttons->current_button = 0; + + return WERR_OK; +} + +static bool buttons_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_previous(buttons); +} + +static bool buttons_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + return buttons_highlight_next(buttons); +} + +static enum dialog_action buttons_on_enter(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + struct button_spec *spec = &buttons->spec[buttons->current_button]; + + if (spec->on_enter) { + return spec->on_enter(dia, section); + } + + return spec->action; +} + +static bool buttons_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + + if (forward) { + buttons->current_button = 0; + } else { + buttons->current_button = talloc_array_length(buttons->spec) - 1; + } + buttons_highlight(buttons); + + return true; +} + +static void buttons_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_buttons *buttons = + talloc_get_type_abort(section, struct dialog_section_buttons); + buttons_unhighlight(buttons); +} + +struct dialog_section_ops buttons_ops = { + .create = buttons_create, + .on_tab = buttons_on_tab, + .on_btab = buttons_on_btab, + .on_up = buttons_on_btab, + .on_down = buttons_on_tab, + .on_left = buttons_on_btab, + .on_right = buttons_on_tab, + .on_enter = buttons_on_enter, + .on_focus = buttons_on_focus, + .on_leave_focus = buttons_on_leave_focus +}; + +static int buttons_free(struct dialog_section_buttons *buttons) +{ + dialog_section_destroy(&buttons->section); + return 0; +} + +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec) +{ + struct dialog_section_buttons *buttons; + size_t i, nbuttons; + int width; + + buttons = talloc_zero(ctx, struct dialog_section_buttons); + if (buttons == NULL) { + return NULL; + } + talloc_set_destructor(buttons, buttons_free); + + for (nbuttons = 0; spec[nbuttons].label; ++nbuttons) { + } + buttons->spec = talloc_zero_array(buttons, struct button_spec, nbuttons); + if (buttons->spec == NULL) { + goto fail; + } + + for (width = 0, i = 0; i < nbuttons; ++i) { + buttons->spec[i] = spec[i]; + buttons->spec[i].label = talloc_asprintf(buttons->spec, + "[ %s ]", + spec[i].label); + if (!buttons->spec[i].label) { + goto fail; + } + + buttons->spec[i].col = width; + width += strlen(buttons->spec[i].label); + if (i != nbuttons - 1) { + ++width; + } + } + + dialog_section_init(&buttons->section, &buttons_ops, 1, width); + + return &buttons->section; + +fail: + talloc_free(buttons); + return NULL; +} + +/* options */ +struct dialog_section_options { + struct dialog_section section; + struct option_spec *spec; + int current_option; + bool single_select; +}; + +static void options_unhighlight(struct dialog_section_options *options) +{ + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + wnoutrefresh(options->section.window); +} + +static void options_highlight(struct dialog_section_options *options) +{ + struct option_spec *spec = &options->spec[options->current_option]; + short pair; + attr_t attr; + size_t row; + + /* + * Some GCC versions will complain if the macro version of + * wattr_get is used. So we should enforce the use of the + * function instead. See: + * http://lists.gnu.org/archive/html/bug-ncurses/2013-12/msg00017.html + */ + (wattr_get)(options->section.window, &attr, &pair, NULL); + for (row = 0; row < options->section.nlines; ++row) { + mvwchgat(options->section.window, row, 0, -1, A_NORMAL, pair, NULL); + } + mvwchgat(options->section.window, spec->row, spec->col, + strlen(spec->label), A_REVERSE, pair, NULL); + wmove(options->section.window, spec->row, spec->col + 4); + wcursyncup(options->section.window); + wnoutrefresh(options->section.window); +} + +static void options_render_state(struct dialog_section_options *options) +{ + size_t i, noptions; + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *spec = &options->spec[i]; + char c = ' '; + if (*spec->state) + c = 'x'; + mvwaddch(options->section.window, + spec->row, spec->col + 1, c); + wnoutrefresh(options->section.window); + } +} + +static bool options_highlight_next(struct dialog_section_options *options) +{ + if (options->current_option < talloc_array_length(options->spec) - 1) { + options->current_option++; + options_highlight(options); + return true; + } + return false; +} + +static bool options_highlight_previous(struct dialog_section_options *options) +{ + if (options->current_option > 0) { + options->current_option--; + options_highlight(options); + return true; + } + return false; +} + +static WERROR options_create(struct dialog *dia, + struct dialog_section *section) +{ + size_t i, noptions; + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + struct option_spec *spec = &options->spec[i]; + mvwaddstr(section->window, spec->row, spec->col, + spec->label); + } + + options->current_option = 0; + options_render_state(options); + + return WERR_OK; +} + +static bool options_on_btab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_previous(options); +} + +static bool options_on_tab(struct dialog *dia, struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + return options_highlight_next(options); +} + +static void options_on_input(struct dialog *dia, struct dialog_section *section, int c) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (c == ' ') { + struct option_spec *spec = &options->spec[options->current_option]; + if (options->single_select) { + size_t i, noptions; + noptions = talloc_array_length(options->spec); + for (i = 0; i < noptions; ++i) { + *(options->spec[i].state) = false; + } + } + *spec->state = !*spec->state; + options_unhighlight(options); + options_render_state(options); + options_highlight(options); + } +} + +static enum dialog_action options_on_enter(struct dialog *dia, struct dialog_section *section) +{ + options_on_input(dia, section, ' '); + return DIALOG_OK; +} + +static bool options_on_focus(struct dialog *dia, + struct dialog_section *section, + bool forward) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + + if (forward) { + options->current_option = 0; + } else { + options->current_option = talloc_array_length(options->spec) - 1; + } + options_highlight(options); + + return true; +} + +static void options_on_leave_focus(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section_options *options = + talloc_get_type_abort(section, struct dialog_section_options); + options_unhighlight(options); +} + +struct dialog_section_ops options_ops = { + .create = options_create, + .on_tab = options_on_tab, + .on_btab = options_on_btab, + .on_up = options_on_btab, + .on_down = options_on_tab, + .on_left = options_on_btab, + .on_right = options_on_tab, + .on_input = options_on_input, + .on_enter = options_on_enter, + .on_focus = options_on_focus, + .on_leave_focus = options_on_leave_focus +}; + +static int options_free(struct dialog_section_options *options) +{ + dialog_section_destroy(&options->section); + return 0; +} + +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select) +{ + struct dialog_section_options *options; + size_t i, noptions; + int width, maxwidth, maxrows; + + options = talloc_zero(ctx, struct dialog_section_options); + if (options == NULL) { + return NULL; + } + talloc_set_destructor(options, options_free); + + for (noptions = 0; spec[noptions].label; ++noptions) { + } + options->spec = talloc_zero_array(options, struct option_spec, noptions); + if (options->spec == NULL) { + goto fail; + } + + maxrows = noptions / maxcol; + if (noptions % maxcol) { + ++maxrows; + } + + for (width = 0, maxwidth = 0, i = 0; i < noptions; ++i) { + options->spec[i] = spec[i]; + options->spec[i].label = talloc_asprintf(options->spec, + "[ ] %s", + spec[i].label); + if (!options->spec[i].label) { + goto fail; + } + + options->spec[i].col = maxwidth; + options->spec[i].row = i % maxrows; + width = MAX(strlen(options->spec[i].label), width); + if (options->spec[i].row == maxrows - 1 || i == noptions - 1) { + maxwidth += width + 1; + width = 0; + } + } + + dialog_section_init(&options->section, &options_ops, maxrows, maxwidth - 1); + options->single_select = single_select; + + return &options->section; + +fail: + talloc_free(options); + return NULL; +} + + +enum input_type { + DLG_IN_LONG, + DLG_IN_ULONG, + DLG_IN_STR, +}; + +struct input_req { + TALLOC_CTX *ctx; + enum input_type type; + union { + void *out; + unsigned long *out_ulong; + long *out_long; + const char **out_str; + } out; +}; + +static bool input_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct input_req *req = arg; + struct dialog_section *data; + unsigned long long out_ulong; + long long out_long; + + data = dialog_find_section(dia, "input"); + + switch (req->type) { + case DLG_IN_LONG: + if (!dialog_section_text_field_get_int(data, &out_long)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number."); + return false; + } + if (out_long < LONG_MIN || out_long > LONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_long = out_long; + break; + case DLG_IN_ULONG: + if (!dialog_section_text_field_get_uint(data, &out_ulong)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Input must be a number greater than zero."); + return false; + } + if (out_ulong > ULONG_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "Number is out of range."); + return false; + } + *req->out.out_ulong = out_ulong; + break; + case DLG_IN_STR: + *req->out.out_str = dialog_section_text_field_get(req->ctx, data); + break; + } + + return true; +} + +static int dialog_input_internal(TALLOC_CTX *ctx, void *output, + enum input_type type, + const char *title, + const char *msg, va_list ap) + PRINTF_ATTRIBUTE(5,0); + +static int dialog_input_internal(TALLOC_CTX *ctx, void *output, + enum input_type type, + const char *title, + const char *msg, va_list ap) +{ + WERROR err; + struct input_req req; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + + req.ctx = ctx; + req.type = type; + req.out.out = output; + *req.out.out_str = NULL; + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + dialog_set_submit_cb(dia, input_on_submit, &req); + section = dialog_section_label_new_va(dia, msg, ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(section, "input"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} + +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, + const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_STR, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_ULONG, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_input_long(TALLOC_CTX *ctx, long *output, + const char *title, const char *msg, ...) +{ + va_list ap; + int rv; + + va_start(ap, msg); + rv = dialog_input_internal(ctx, output, DLG_IN_LONG, title, msg, ap); + va_end(ap); + + return rv; +} + +int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, + const char *title, const char *msg, ...) +{ + va_list ap; + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct button_spec spec[3]; + + memset(&spec, '\0', sizeof(spec)); + spec[0].label = "OK"; + spec[0].action = DIALOG_OK; + if (type == DIA_CONFIRM) { + spec[1].label = "Cancel"; + spec[1].action = DIALOG_CANCEL; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, title, -1, -1); + va_start(ap, msg); + section = dialog_section_label_new_va(dia, msg, ap); + va_end(ap); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} + + +struct edit_req { + uint32_t type; + uint32_t mode; + struct registry_key *key; + const struct value_item *vitem; +}; + +static WERROR fill_value_buffer(struct dialog *dia, struct edit_req *edit) +{ + char *tmp; + struct dialog_section *data; + + if (edit->vitem == NULL) { + return WERR_OK; + } + + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); + + switch (edit->mode) { + case REG_DWORD: { + uint32_t v = 0; + if (edit->vitem->data.length >= 4) { + v = IVAL(edit->vitem->data.data, 0); + } + tmp = talloc_asprintf(dia, "%u", (unsigned)v); + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + dialog_section_text_field_set(data, tmp); + talloc_free(tmp); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; + + if (!pull_reg_sz(dia, &edit->vitem->data, &s)) { + return WERR_NOT_ENOUGH_MEMORY; + } + dialog_section_text_field_set(data, s); + break; + } + case REG_MULTI_SZ: { + const char **array; + + if (!pull_reg_multi_sz(dia, &edit->vitem->data, &array)) { + return WERR_NOT_ENOUGH_MEMORY; + } + return dialog_section_text_field_set_lines(dia, data, array); + } + case REG_BINARY: + default: + return dialog_section_hexedit_set_buf(data, + edit->vitem->data.data, + edit->vitem->data.length); + } + + return WERR_OK; +} + +static bool value_exists(TALLOC_CTX *ctx, const struct registry_key *key, + const char *name) +{ + uint32_t type; + DATA_BLOB blob; + WERROR rv; + + rv = reg_key_get_value_by_name(ctx, key, name, &type, &blob); + + return W_ERROR_IS_OK(rv); +} + +static bool edit_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct edit_req *edit = arg; + WERROR rv; + DATA_BLOB blob; + const char *name; + struct dialog_section *name_section, *data; + + name_section = dialog_find_section(dia, "name"); + if (name_section) { + name = dialog_section_text_field_get(dia, name_section); + if (*name == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Value name must not be blank."); + return false; + } + if (value_exists(dia, edit->key, name)) { + dialog_notice(dia, DIA_ALERT, "Error", + "Value named \"%s\" already exists.", + name); + return false; + } + } else { + SMB_ASSERT(edit->vitem); + name = edit->vitem->value_name; + } + SMB_ASSERT(name); + + data = dialog_find_section(dia, "data"); + SMB_ASSERT(data != NULL); + + rv = WERR_OK; + switch (edit->mode) { + case REG_DWORD: { + unsigned long long v; + uint32_t val; + + if (!dialog_section_text_field_get_uint(data, &v)) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must be an integer."); + return false; + } + if (v > UINT32_MAX) { + dialog_notice(dia, DIA_ALERT, "Error", + "REG_DWORD value must less than %lu.", + (unsigned long)UINT32_MAX); + return false; + } + val = (uint32_t)v; + blob = data_blob_talloc(dia, NULL, sizeof(val)); + SIVAL(blob.data, 0, val); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *buf; + + buf = dialog_section_text_field_get(dia, data); + if (!buf || !push_reg_sz(dia, &blob, buf)) { + rv = WERR_NOT_ENOUGH_MEMORY; + } + break; + } + case REG_MULTI_SZ: { + const char **lines; + + lines = dialog_section_text_field_get_lines(dia, data); + if (!lines || !push_reg_multi_sz(dia, &blob, lines)) { + rv = WERR_NOT_ENOUGH_MEMORY; + } + break; + } + case REG_BINARY: { + const void *buf; + size_t len; + + dialog_section_hexedit_get_buf(data, &buf, &len); + blob = data_blob_talloc(dia, buf, len); + break; + } + } + + if (W_ERROR_IS_OK(rv)) { + rv = reg_val_set(edit->key, name, edit->type, blob); + } + + if (!W_ERROR_IS_OK(rv)) { + const char *msg = get_friendly_werror_msg(rv); + dialog_notice(dia, DIA_ALERT, "Error", + "Error saving value:\n%s", msg); + + return false; + } + + return true; + +} + +static enum dialog_action edit_on_resize(struct dialog *dia, + struct dialog_section *section) +{ + struct dialog_section *data; + unsigned long size; + int rv; + + data = dialog_find_section(dia, "data"); + rv = dialog_input_ulong(dia, &size, "Resize", "Enter size of buffer"); + if (rv == DIALOG_OK) { + dialog_section_hexedit_resize(data, size); + } + + return DIALOG_IGNORE; +} + +int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary, WERROR *err, + const char **name) +{ + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + struct edit_req edit; + struct button_spec buttons[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + struct button_spec buttons_hexedit[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Resize Buffer", .on_enter = edit_on_resize}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + + + edit.key = key; + edit.vitem = vitem; + edit.type = type; + edit.mode = type; + if (force_binary || (vitem && vitem->unprintable)) { + edit.mode = REG_BINARY; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Edit Value", -1, -1); + dialog_set_submit_cb(dia, edit_on_submit, &edit); + + section = dialog_section_label_new(dia, "Type"); + dialog_append_section(dia, section); + section = dialog_section_label_new(dia, "%s", + str_regtype(type)); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + + section = dialog_section_label_new(dia, "Name"); + dialog_append_section(dia, section); + if (vitem) { + section = dialog_section_label_new(dia, "%s", + vitem->value_name); + } else { + section = dialog_section_text_field_new(dia, 1, 50); + dialog_section_set_name(section, "name"); + } + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + + section = dialog_section_label_new(dia, "Data"); + dialog_append_section(dia, section); + + switch (edit.mode) { + case REG_DWORD: + case REG_SZ: + case REG_EXPAND_SZ: + section = dialog_section_text_field_new(dia, 1, 50); + break; + case REG_MULTI_SZ: + section = dialog_section_text_field_new(dia, 10, 50); + break; + case REG_BINARY: + default: + section = dialog_section_hexedit_new(dia, 10); + break; + } + + dialog_section_set_name(section, "data"); + dialog_append_section(dia, section); + + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + if (edit.mode == REG_BINARY) { + section = dialog_section_buttons_new(dia, buttons_hexedit); + } else { + section = dialog_section_buttons_new(dia, buttons); + } + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + + *err = fill_value_buffer(dia, &edit); + if (!W_ERROR_IS_OK(*err)) { + return DIALOG_CANCEL; + } + + dialog_show(dia); + dialog_modal_loop(dia, err, &action); + + if (action == DIALOG_OK && name) { + if (vitem) { + *name = talloc_strdup(ctx, vitem->value_name); + } else if ((section = dialog_find_section(dia, "name"))) { + *name = dialog_section_text_field_get(ctx, section); + } + } + + talloc_free(dia); + + return action; +} + +int dialog_select_type(TALLOC_CTX *ctx, int *type) +{ + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section; + const char *reg_types[] = { + "REG_BINARY", + "REG_DWORD", + "REG_EXPAND_SZ", + "REG_MULTI_SZ", + "REG_SZ" + }; + #define NTYPES ARRAY_SIZE(reg_types) + struct button_spec spec[] = { + {.label = "OK", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + bool flags[NTYPES] = { true }; + struct option_spec opsec[NTYPES + 1]; + unsigned i; + + memset(&opsec, '\0', sizeof(opsec)); + for (i = 0; i < NTYPES; ++i) { + opsec[i].label = reg_types[i]; + opsec[i].state = &flags[i]; + } + + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "New Value", -1, -1); + + section = dialog_section_label_new(dia, "Select type for new value:"); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, ' '); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, opsec, 2, true); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + dialog_show(dia); + + dialog_modal_loop(dia, &err, &action); + if (action == DIALOG_OK) { + for (i = 0; i < NTYPES; ++i) { + if (flags[i]) { + *type = regtype_by_string(reg_types[i]); + break; + } + } + } + + talloc_free(dia); + + return action; +} + +struct search_req { + TALLOC_CTX *ctx; + struct regedit_search_opts *opts; +}; + +static bool search_on_submit(struct dialog *dia, struct dialog_section *section, + void *arg) +{ + struct search_req *search = arg; + struct dialog_section *query; + + query = dialog_find_section(dia, "query"); + SMB_ASSERT(query != NULL); + + if (!search->opts->search_key && !search->opts->search_value) { + dialog_notice(dia, DIA_ALERT, "Error", + "Must search a key and/or a value"); + return false; + } + + talloc_free(discard_const(search->opts->query)); + search->opts->query = dialog_section_text_field_get(search->ctx, query); + SMB_ASSERT(search->opts->query != NULL); + if (search->opts->query[0] == '\0') { + dialog_notice(dia, DIA_ALERT, "Error", + "Query must not be blank."); + return false; + } + + return true; +} + +int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts) +{ + WERROR err; + enum dialog_action action; + struct dialog *dia; + struct dialog_section *section, *query; + struct search_req search; + struct button_spec spec[] = { + {.label = "Search", .action = DIALOG_OK}, + {.label = "Cancel", .action = DIALOG_CANCEL}, + { 0 } + }; + struct option_spec search_opts[] = { + {.label = "Search Keys", .state = &opts->search_key}, + {.label = "Search Values", .state = &opts->search_value}, + {.label = "Recursive", .state = &opts->search_recursive}, + {.label = "Case Sensitive", .state = &opts->search_case}, + { 0 } + }; + + if (!opts->search_key && !opts->search_value) { + opts->search_key = true; + } + + search.ctx = ctx; + search.opts = opts; + dia = dialog_new(ctx, PAIR_BLACK_CYAN, "Search", -1, -1); + dialog_set_submit_cb(dia, search_on_submit, &search); + section = dialog_section_label_new(dia, "Query"); + dialog_append_section(dia, section); + query = dialog_section_text_field_new(dia, 1, -1); + dialog_section_set_name(query, "query"); + dialog_append_section(dia, query); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_options_new(dia, search_opts, 2, false); + dialog_append_section(dia, section); + section = dialog_section_hsep_new(dia, 0); + dialog_append_section(dia, section); + section = dialog_section_buttons_new(dia, spec); + dialog_section_set_justify(section, SECTION_JUSTIFY_CENTER); + dialog_append_section(dia, section); + + dialog_create(dia); + if (opts->query) { + dialog_section_text_field_set(query, opts->query); + } + + dialog_modal_loop(dia, &err, &action); + talloc_free(dia); + + return action; +} diff --git a/source3/utils/regedit_dialog.h b/source3/utils/regedit_dialog.h new file mode 100644 index 0000000..b8bc3bf --- /dev/null +++ b/source3/utils/regedit_dialog.h @@ -0,0 +1,240 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _REGEDIT_DIALOG_H_ +#define _REGEDIT_DIALOG_H_ + +#include <ncurses.h> +#include <panel.h> +#include <menu.h> + +struct dialog; +struct dialog_section; + +/* dialog submit cb. return true to close dialog, false to keep + it open */ +typedef bool (*dialog_submit_cb)(struct dialog *, struct dialog_section *, + void *); + +struct dialog { + char *title; + WINDOW *window; + WINDOW *pad; + PANEL *panel; + int x; + int y; + short color; + bool centered; + dialog_submit_cb submit; + void *submit_arg; + struct dialog_section *head_section; + struct dialog_section *tail_section; + struct dialog_section *current_section; +}; + +enum dialog_action { + DIALOG_IGNORE, + DIALOG_OK, + DIALOG_CANCEL +}; + +struct dialog_section_ops { + /* create section */ + WERROR (*create)(struct dialog *, struct dialog_section *); + + /* (optional) cleanup the section */ + void (*destroy)(struct dialog_section *); + + /* (optional) handle character input */ + void (*on_input)(struct dialog *, struct dialog_section *, int c); + + /* (optional) handle a tab character. return true if section dealt + with the tab internally, or false to advance focus to + the next dialog section. */ + bool (*on_tab)(struct dialog *, struct dialog_section *); + + /* (optional) handle a btab character. return true if section dealt + with the tab internally, or false to move focus to + the previous dialog section. */ + bool (*on_btab)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_up)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_down)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_left)(struct dialog *, struct dialog_section *); + + /* */ + bool (*on_right)(struct dialog *, struct dialog_section *); + + /* (optional) handle enter key. return DIALOG_OK to submit + dialog, DIALOG_CANCEL to close dialog, or DIALOG_IGNORE to + handle the enter internally. */ + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + + /* (optional) called when this section is about to take focus. forward + is set to true when focus has landed here from forward traversal, + such as from a tab. return true to accept focus, false to pass to an + adjacent section. */ + bool (*on_focus)(struct dialog *, struct dialog_section *, bool forward); + + /* (optional) called when focus is leaving this section */ + void (*on_leave_focus)(struct dialog *, struct dialog_section *); +}; + +enum section_justify { + SECTION_JUSTIFY_LEFT, + SECTION_JUSTIFY_CENTER, + SECTION_JUSTIFY_RIGHT, +}; + +struct dialog_section { + char *name; + int nlines; + int ncols; + WINDOW *window; + enum section_justify justify; + const struct dialog_section_ops *ops; + struct dialog_section *next; + struct dialog_section *prev; +}; + +struct dialog *dialog_new(TALLOC_CTX *ctx, short color, + const char *title, int y, int x); + +void dialog_section_destroy(struct dialog_section *section); +void dialog_section_init(struct dialog_section *section, + const struct dialog_section_ops *ops, + int nlines, int ncols); + +void dialog_section_set_name(struct dialog_section *section, const char *name); +const char *dialog_section_get_name(struct dialog_section *section); +void dialog_section_set_justify(struct dialog_section *section, + enum section_justify justify); + +void dialog_append_section(struct dialog *dia, + struct dialog_section *section); +struct dialog_section *dialog_find_section(struct dialog *dia, + const char *name); + +WERROR dialog_create(struct dialog *dia); +void dialog_show(struct dialog *dia); +void dialog_destroy(struct dialog *dia); +void dialog_set_submit_cb(struct dialog *dia, dialog_submit_cb cb, void *arg); +bool dialog_handle_input(struct dialog *dia, WERROR *err, + enum dialog_action *action); +void dialog_modal_loop(struct dialog *dia, WERROR *err, + enum dialog_action *action); + +struct dialog_section *dialog_section_label_new_va(TALLOC_CTX *ctx, + const char *msg, + va_list ap) + PRINTF_ATTRIBUTE(2,0); +struct dialog_section *dialog_section_label_new(TALLOC_CTX *ctx, + const char *msg, ...) + PRINTF_ATTRIBUTE(2,3); + +struct dialog_section *dialog_section_hsep_new(TALLOC_CTX *ctx, int sep); + + +struct dialog_section *dialog_section_text_field_new(TALLOC_CTX *ctx, + int height, int width); +const char *dialog_section_text_field_get(TALLOC_CTX *ctx, + struct dialog_section *section); +const char **dialog_section_text_field_get_lines(TALLOC_CTX *ctx, + struct dialog_section *section); +bool dialog_section_text_field_get_int(struct dialog_section *section, + long long *out); +bool dialog_section_text_field_get_uint(struct dialog_section *section, + unsigned long long *out); +void dialog_section_text_field_set(struct dialog_section *section, + const char *s); +WERROR dialog_section_text_field_set_lines(TALLOC_CTX *ctx, + struct dialog_section *section, + const char **array); + +struct dialog_section *dialog_section_hexedit_new(TALLOC_CTX *ctx, int height); +WERROR dialog_section_hexedit_set_buf(struct dialog_section *section, + const void *data, size_t size); +void dialog_section_hexedit_get_buf(struct dialog_section *section, + const void **data, size_t *size); +WERROR dialog_section_hexedit_resize(struct dialog_section *section, + size_t size); + +struct button_spec { + const char *label; + enum dialog_action (*on_enter)(struct dialog *, + struct dialog_section *); + enum dialog_action action; + + /* internal */ + int col; +}; +struct dialog_section *dialog_section_buttons_new(TALLOC_CTX *ctx, + const struct button_spec *spec); + +struct option_spec { + const char *label; + bool *state; + + /* internal */ + int col; + int row; +}; +struct dialog_section *dialog_section_options_new(TALLOC_CTX *ctx, + const struct option_spec *spec, + int maxcol, bool single_select); + +enum dialog_type { + DIA_ALERT, + DIA_CONFIRM +}; + +int dialog_notice(TALLOC_CTX *ctx, enum dialog_type type, + const char *title, const char *msg, ...) + PRINTF_ATTRIBUTE(4,5); + +int dialog_input(TALLOC_CTX *ctx, const char **output, const char *title, + const char *msg, ...) PRINTF_ATTRIBUTE(4,5); +int dialog_input_long(TALLOC_CTX *ctx, long *output, + const char *title, const char *msg, ...) + PRINTF_ATTRIBUTE(4,5); +int dialog_input_ulong(TALLOC_CTX *ctx, unsigned long *output, + const char *title, const char *msg, ...) + PRINTF_ATTRIBUTE(4,5); + +struct registry_key; +struct value_item; + +int dialog_edit_value(TALLOC_CTX *ctx, struct registry_key *key, + uint32_t type, const struct value_item *vitem, + bool force_binary, WERROR *err, + const char **name); + +int dialog_select_type(TALLOC_CTX *ctx, int *type); + +struct regedit_search_opts; + +int dialog_search_input(TALLOC_CTX *ctx, struct regedit_search_opts *opts); + +#endif diff --git a/source3/utils/regedit_hexedit.c b/source3/utils/regedit_hexedit.c new file mode 100644 index 0000000..413e563 --- /dev/null +++ b/source3/utils/regedit_hexedit.c @@ -0,0 +1,563 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "regedit_hexedit.h" + +/* + offset hex1 hex2 ascii + 00000000 FF FF FF FF FF FF FF FF ........ +*/ + +#define HEX_COL1 10 +#define HEX_COL1_END 21 +#define HEX_COL2 23 +#define HEX_COL2_END 34 +#define ASCII_COL 36 +#define ASCII_COL_END LINE_WIDTH +#define BYTES_PER_LINE 8 + +struct hexedit { + size_t offset; + size_t len; + size_t alloc_size; + int cursor_y; + int cursor_x; + size_t cursor_offset; + size_t cursor_line_offset; + int nibble; + uint8_t *data; + WINDOW *win; +}; + +static int max_rows(WINDOW *win) +{ + int maxy; + + maxy = getmaxy(win); + + return maxy - 1; +} + +struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data, + size_t sz) +{ + WERROR rv; + struct hexedit *buf; + + buf = talloc_zero(ctx, struct hexedit); + if (buf == NULL) { + return NULL; + } + + buf->win = parent; + + rv = hexedit_set_buf(buf, data, sz); + if (!W_ERROR_IS_OK(rv)) { + goto fail; + } + + return buf; + +fail: + talloc_free(buf); + + return NULL; +} + +WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz) +{ + TALLOC_FREE(buf->data); + + buf->data = talloc_zero_array(buf, uint8_t, sz); + if (buf->data == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + if (data != NULL) { + memcpy(buf->data, data, sz); + } + + buf->len = sz; + buf->alloc_size = sz; + buf->cursor_x = HEX_COL1; + buf->cursor_y = 0; + buf->cursor_offset = 0; + buf->cursor_line_offset = 0; + buf->nibble = 0; + + return WERR_OK; +} + +const void *hexedit_get_buf(struct hexedit *buf) +{ + return buf->data; +} + +size_t hexedit_get_buf_len(struct hexedit *buf) +{ + return buf->len; +} + +static size_t bytes_per_screen(WINDOW *win) +{ + return max_rows(win) * BYTES_PER_LINE; +} + +void hexedit_set_cursor(struct hexedit *buf) +{ + wmove(buf->win, max_rows(buf->win), 0); + wattron(buf->win, A_REVERSE | A_STANDOUT); + wclrtoeol(buf->win); + if (buf->cursor_offset < buf->len) { + wprintw(buf->win, "Len:%lu Off:%lu Val:0x%X", buf->len, + buf->cursor_offset, buf->data[buf->cursor_offset]); + } else { + wprintw(buf->win, "Len:%lu Off:%lu", buf->len, + buf->cursor_offset); + } + wattroff(buf->win, A_REVERSE | A_STANDOUT); + wmove(buf->win, buf->cursor_y, buf->cursor_x); + wcursyncup(buf->win); + wsyncup(buf->win); + untouchwin(buf->win); +} + +void hexedit_refresh(struct hexedit *buf) +{ + size_t end; + size_t lineno; + size_t off; + + werase(buf->win); + if (buf->len == 0) { + mvwprintw(buf->win, 0, 0, "%08X", 0); + return; + } + + end = buf->offset + bytes_per_screen(buf->win); + if (end > buf->len) { + end = buf->len; + } + + for (off = buf->offset, lineno = 0; + off < end; + off += BYTES_PER_LINE, ++lineno) { + uint8_t *line = buf->data + off; + size_t i, endline; + + wmove(buf->win, lineno, 0); + wprintw(buf->win, "%08zX ", off); + + endline = BYTES_PER_LINE; + + if (off + BYTES_PER_LINE > buf->len) { + endline = buf->len - off; + } + + for (i = 0; i < endline; ++i) { + wprintw(buf->win, "%02X", line[i]); + if (i + 1 < endline) { + if (i == 3) { + wprintw(buf->win, " "); + } else { + wprintw(buf->win, " "); + } + } + } + + wmove(buf->win, lineno, ASCII_COL); + for (i = 0; i < endline; ++i) { + if (isprint(line[i])) { + waddch(buf->win, line[i]); + } else { + waddch(buf->win, '.'); + } + } + } +} + +static void calc_cursor_offset(struct hexedit *buf) +{ + buf->cursor_offset = buf->offset + buf->cursor_y * BYTES_PER_LINE + + buf->cursor_line_offset; +} + +static int offset_to_hex_col(size_t pos) +{ + switch (pos) { + case 0: + return HEX_COL1; + case 1: + return HEX_COL1 + 3; + case 2: + return HEX_COL1 + 6; + case 3: + return HEX_COL1 + 9; + + case 4: + return HEX_COL2; + case 5: + return HEX_COL2 + 3; + case 6: + return HEX_COL2 + 6; + case 7: + return HEX_COL2 + 9; + } + + return -1; +} + +static bool scroll_up(struct hexedit *buf) +{ + if (buf->offset == 0) { + return false; + } + + buf->offset -= BYTES_PER_LINE; + + return true; +} + +static void cursor_down(struct hexedit *buf) +{ + size_t space; + bool need_refresh = false; + + space = buf->offset + (buf->cursor_y + 1) * BYTES_PER_LINE; + if (space > buf->len) { + return; + } + + if (buf->cursor_y + 1 == max_rows(buf->win)) { + buf->offset += BYTES_PER_LINE; + need_refresh = true; + } else { + buf->cursor_y++; + } + + if (buf->cursor_offset + BYTES_PER_LINE > buf->len) { + buf->nibble = 0; + buf->cursor_offset = buf->len; + buf->cursor_line_offset = buf->len - space; + if (buf->cursor_x >= ASCII_COL) { + buf->cursor_x = ASCII_COL + buf->cursor_line_offset; + } else { + buf->cursor_x = offset_to_hex_col(buf->cursor_line_offset); + } + } + if (need_refresh) { + hexedit_refresh(buf); + } + calc_cursor_offset(buf); +} + +static void cursor_up(struct hexedit *buf) +{ + if (buf->cursor_y == 0) { + if (scroll_up(buf)) { + hexedit_refresh(buf); + } + } else { + buf->cursor_y--; + } + + calc_cursor_offset(buf); +} + +static bool is_over_gap(struct hexedit *buf) +{ + int col; + + if (buf->cursor_x < ASCII_COL) { + if (buf->cursor_x >= HEX_COL2) { + col = buf->cursor_x - HEX_COL2; + } else { + col = buf->cursor_x - HEX_COL1; + } + + switch (col) { + case 2: + case 5: + case 8: + return true; + } + } + + return false; +} + +static void cursor_left(struct hexedit *buf) +{ + if (buf->cursor_x == HEX_COL1) { + return; + } + if (buf->cursor_x == HEX_COL2) { + buf->cursor_x = HEX_COL1_END - 1; + buf->cursor_line_offset = 3; + buf->nibble = 1; + } else if (buf->cursor_x == ASCII_COL) { + size_t off = buf->offset + buf->cursor_y * BYTES_PER_LINE; + if (off + 7 >= buf->len) { + size_t lastpos = buf->len - off; + buf->cursor_x = offset_to_hex_col(lastpos) + 1; + buf->cursor_line_offset = lastpos; + } else { + buf->cursor_x = HEX_COL2_END - 1; + buf->cursor_line_offset = 7; + } + buf->nibble = 1; + } else { + if (buf->cursor_x > ASCII_COL || buf->nibble == 0) { + buf->cursor_line_offset--; + } + buf->cursor_x--; + buf->nibble = !buf->nibble; + } + + if (is_over_gap(buf)) { + buf->cursor_x--; + } + + calc_cursor_offset(buf); +} + +static void cursor_right(struct hexedit *buf) +{ + int new_x = buf->cursor_x + 1; + + if (new_x == ASCII_COL_END) { + return; + } + if ((buf->cursor_x >= ASCII_COL || buf->nibble == 1) && + buf->cursor_offset == buf->len) { + if (buf->cursor_x < ASCII_COL) { + new_x = ASCII_COL; + buf->cursor_line_offset = 0; + buf->nibble = 0; + } else { + return; + } + } + if (new_x == HEX_COL1_END) { + new_x = HEX_COL2; + buf->cursor_line_offset = 4; + buf->nibble = 0; + } else if (new_x == HEX_COL2_END) { + new_x = ASCII_COL; + buf->cursor_line_offset = 0; + buf->nibble = 0; + } else { + if (buf->cursor_x >= ASCII_COL || buf->nibble == 1) { + buf->cursor_line_offset++; + } + buf->nibble = !buf->nibble; + } + + buf->cursor_x = new_x; + + if (is_over_gap(buf)) { + buf->cursor_x++; + } + + calc_cursor_offset(buf); +} + +static void do_edit(struct hexedit *buf, int c) +{ + uint8_t *byte; + + if (buf->cursor_offset == buf->len) { + hexedit_resize_buffer(buf, buf->len + 1); + } + + byte = buf->data + buf->cursor_offset; + + if (buf->cursor_x >= ASCII_COL) { + *byte = (uint8_t)c; + + mvwprintw(buf->win, buf->cursor_y, + offset_to_hex_col(buf->cursor_line_offset), "%X", c); + if (!isprint(c)) { + c = '.'; + } + mvwaddch(buf->win, buf->cursor_y, + ASCII_COL + buf->cursor_line_offset, c); + if (buf->cursor_x + 1 != ASCII_COL_END) { + cursor_right(buf); + } else { + cursor_down(buf); + } + } else { + if (!isxdigit(c)) { + return; + } + c = toupper(c); + waddch(buf->win, c); + + if (isdigit(c)) { + c = c - '0'; + } else { + c = c - 'A' + 10; + } + if (buf->nibble == 0) { + *byte = (*byte & 0x0f) | c << 4; + } else { + *byte = (*byte & 0xf0) | c; + } + + c = *byte; + if (!isprint(c)) { + c = '.'; + } + mvwaddch(buf->win, buf->cursor_y, + ASCII_COL + buf->cursor_line_offset, c); + + if (buf->cursor_x + 1 != HEX_COL2_END) { + cursor_right(buf); + } else { + cursor_down(buf); + } + } + + hexedit_refresh(buf); +} + +static void erase_at(struct hexedit *buf, size_t pos) +{ + if (pos >= buf->len) { + return; + } + + if (pos < buf->len - 1) { + /* squeeze the character out of the buffer */ + uint8_t *p = buf->data + pos; + uint8_t *end = buf->data + buf->len; + memmove(p, p + 1, end - p - 1); + } + + buf->len--; + hexedit_refresh(buf); +} + +static void do_backspace(struct hexedit *buf) +{ + size_t off; + bool do_erase = true; + + if (buf->cursor_offset == 0) { + return; + } + + off = buf->cursor_offset; + if (buf->cursor_x == ASCII_COL) { + cursor_up(buf); + buf->cursor_line_offset = 7; + buf->cursor_x = ASCII_COL_END - 1; + calc_cursor_offset(buf); + } else if (buf->cursor_x == HEX_COL1) { + cursor_up(buf); + buf->cursor_line_offset = 7; + buf->cursor_x = HEX_COL2_END - 1; + buf->nibble = 1; + calc_cursor_offset(buf); + } else { + if (buf->cursor_x < ASCII_COL && buf->nibble) { + do_erase = false; + } + cursor_left(buf); + } + if (do_erase) { + erase_at(buf, off - 1); + } +} + +static void do_delete(struct hexedit *buf) +{ + erase_at(buf, buf->cursor_offset); +} + +void hexedit_driver(struct hexedit *buf, int c) +{ + switch (c) { + case HE_CURSOR_UP: + cursor_up(buf); + break; + case HE_CURSOR_DOWN: + cursor_down(buf); + break; + case HE_CURSOR_LEFT: + cursor_left(buf); + break; + case HE_CURSOR_RIGHT: + cursor_right(buf); + break; + case HE_CURSOR_PGUP: + break; + case HE_CURSOR_PGDN: + break; + case HE_BACKSPACE: + do_backspace(buf); + break; + case HE_DELETE: + do_delete(buf); + break; + default: + do_edit(buf, c & 0xff); + break; + } + + hexedit_set_cursor(buf); +} + +WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz) +{ + /* reset the cursor if it'll be out of bounds + after the resize */ + if (buf->cursor_offset > newsz) { + buf->cursor_y = 0; + buf->cursor_x = HEX_COL1; + buf->offset = 0; + buf->cursor_offset = 0; + buf->cursor_line_offset = 0; + buf->nibble = 0; + } + + if (newsz > buf->len) { + if (newsz > buf->alloc_size) { + uint8_t *d; + buf->alloc_size *= 2; + if (newsz > buf->alloc_size) { + buf->alloc_size = newsz; + } + d = talloc_realloc(buf, buf->data, uint8_t, + buf->alloc_size); + if (d == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + buf->data = d; + } + memset(buf->data + buf->len, '\0', newsz - buf->len); + buf->len = newsz; + } else { + buf->len = newsz; + } + + return WERR_OK; +} diff --git a/source3/utils/regedit_hexedit.h b/source3/utils/regedit_hexedit.h new file mode 100644 index 0000000..dfbe27a --- /dev/null +++ b/source3/utils/regedit_hexedit.h @@ -0,0 +1,49 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _HEXEDIT_H_ +#define _HEXEDIT_H_ + +#include <ncurses.h> + +enum { + HE_CURSOR_UP = 0x1000, + HE_CURSOR_DOWN = 0x1100, + HE_CURSOR_LEFT = 0x1200, + HE_CURSOR_RIGHT = 0x1300, + HE_CURSOR_PGUP = 0x1400, + HE_CURSOR_PGDN = 0x1500, + HE_BACKSPACE = 0x1600, + HE_DELETE = 0x1700, +}; + +#define LINE_WIDTH 44 +struct hexedit; + +struct hexedit *hexedit_new(TALLOC_CTX *ctx, WINDOW *parent, const void *data, + size_t sz); +WERROR hexedit_set_buf(struct hexedit *buf, const void *data, size_t sz); +const void *hexedit_get_buf(struct hexedit *buf); +size_t hexedit_get_buf_len(struct hexedit *buf); +void hexedit_set_cursor(struct hexedit *buf); +void hexedit_refresh(struct hexedit *buf); +void hexedit_driver(struct hexedit *buf, int c); +WERROR hexedit_resize_buffer(struct hexedit *buf, size_t newsz); + +#endif diff --git a/source3/utils/regedit_list.c b/source3/utils/regedit_list.c new file mode 100644 index 0000000..b5405f2 --- /dev/null +++ b/source3/utils/regedit_list.c @@ -0,0 +1,591 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "regedit_list.h" +#include "regedit.h" + +struct multilist { + WINDOW *window; + WINDOW *pad; + + unsigned window_height; + unsigned window_width; + unsigned start_row; + unsigned cursor_row; + + unsigned ncols; + struct multilist_column *columns; + + const void *data; + unsigned nrows; + const void *current_row; + const struct multilist_accessors *cb; +}; + +/* data getters */ +static const void *data_get_first_row(struct multilist *list) +{ + SMB_ASSERT(list->cb->get_first_row); + return list->cb->get_first_row(list->data); +} + +static const void *data_get_next_row(struct multilist *list, const void *row) +{ + SMB_ASSERT(list->cb->get_next_row); + return list->cb->get_next_row(list->data, row); +} + +static const void *data_get_prev_row(struct multilist *list, const void *row) +{ + const void *tmp, *next; + + if (list->cb->get_prev_row) { + return list->cb->get_prev_row(list->data, row); + } + + tmp = data_get_first_row(list); + if (tmp == row) { + return NULL; + } + + for (; tmp && (next = data_get_next_row(list, tmp)) != row; + tmp = next) { + } + + SMB_ASSERT(tmp != NULL); + + return tmp; +} + +static unsigned data_get_row_count(struct multilist *list) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_count) + return list->cb->get_row_count(list->data); + + for (i = 0, row = data_get_first_row(list); + row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return i; +} + +static const void *data_get_row_n(struct multilist *list, size_t n) +{ + unsigned i; + const void *row; + + if (list->cb->get_row_n) + return list->cb->get_row_n(list->data, n); + + for (i = 0, row = data_get_first_row(list); + i < n && row != NULL; + ++i, row = data_get_next_row(list, row)) { + } + + return row; +} + +static const char *data_get_column_header(struct multilist *list, unsigned col) +{ + SMB_ASSERT(list->cb->get_column_header); + return list->cb->get_column_header(list->data, col); +} + +static const char *data_get_item_label(struct multilist *list, const void *row, + unsigned col) +{ + SMB_ASSERT(list->cb->get_item_label); + return list->cb->get_item_label(row, col); +} + +static const char *data_get_item_prefix(struct multilist *list, const void *row, + unsigned col) +{ + if (list->cb->get_item_prefix) + return list->cb->get_item_prefix(row, col); + return ""; +} + +static int multilist_free(struct multilist *list) +{ + if (list->pad) { + delwin(list->pad); + } + + return 0; +} + +struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window, + const struct multilist_accessors *cb, + unsigned ncol) +{ + struct multilist *list; + + SMB_ASSERT(ncol > 0); + + list = talloc_zero(ctx, struct multilist); + if (list == NULL) { + return NULL; + } + talloc_set_destructor(list, multilist_free); + + list->cb = cb; + list->ncols = ncol; + list->columns = talloc_zero_array(list, struct multilist_column, ncol); + if (list->columns == NULL) { + talloc_free(list); + return NULL; + } + multilist_set_window(list, window); + + return list; +} + +struct multilist_column *multilist_column_config(struct multilist *list, + unsigned col) +{ + SMB_ASSERT(col < list->ncols); + return &list->columns[col]; +} + +static void put_padding(WINDOW *win, size_t col_width, size_t item_len) +{ + size_t amt; + + SMB_ASSERT(item_len <= col_width); + + amt = col_width - item_len; + while (amt--) { + waddch(win, ' '); + } +} + +static void put_item(struct multilist *list, WINDOW *win, unsigned col, + const char *item, int attr) +{ + bool append_sep = true; + unsigned i; + size_t len; + struct multilist_column *col_info; + bool trim = false; + + SMB_ASSERT(col < list->ncols); + SMB_ASSERT(item != NULL); + + if (col == list->ncols - 1) { + append_sep = false; + } + col_info = &list->columns[col]; + + len = strlen(item); + if (len > col_info->width) { + len = col_info->width; + trim = true; + } + + if (col_info->align_right) { + put_padding(win, col_info->width, len); + } + for (i = 0; i < len; ++i) { + if (i == len - 1 && trim) { + waddch(win, '~' | attr); + } else { + waddch(win, item[i] | attr); + } + } + if (!col_info->align_right) { + put_padding(win, col_info->width, len); + } + + if (append_sep) { + waddch(win, ' '); + waddch(win, '|'); + waddch(win, ' '); + } +} + +static void put_header(struct multilist *list) +{ + unsigned col; + const char *header; + + if (!list->cb->get_column_header) { + return; + } + + wmove(list->window, 0, 0); + for (col = 0; col < list->ncols; ++col) { + header = data_get_column_header(list, col); + SMB_ASSERT(header != NULL); + put_item(list, list->window, col, header, + A_BOLD | COLOR_PAIR(PAIR_YELLOW_BLUE)); + } +} + +static WERROR put_data(struct multilist *list) +{ + const void *row; + int ypos; + unsigned col; + const char *prefix, *item; + char *tmp; + + for (ypos = 0, row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row), ++ypos) { + wmove(list->pad, ypos, 0); + for (col = 0; col < list->ncols; ++col) { + prefix = data_get_item_prefix(list, row, col); + SMB_ASSERT(prefix != NULL); + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + tmp = talloc_asprintf(list, "%s%s", prefix, item); + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + put_item(list, list->pad, col, tmp, 0); + talloc_free(tmp); + } + } + + return WERR_OK; +} + +#define MIN_WIDTH 3 +static struct multilist_column *find_widest_column(struct multilist *list) +{ + unsigned col; + struct multilist_column *colp; + + SMB_ASSERT(list->ncols > 0); + colp = &list->columns[0]; + + for (col = 1; col < list->ncols; ++col) { + if (list->columns[col].width > colp->width) { + colp = &list->columns[col]; + } + } + + if (colp->width < MIN_WIDTH) { + return NULL; + } + + return colp; +} + +static WERROR calc_column_widths(struct multilist *list) +{ + const void *row; + unsigned col; + size_t len; + const char *item; + size_t width, total_width, overflow; + struct multilist_column *colp; + + /* calculate the maximum widths for each column */ + for (col = 0; col < list->ncols; ++col) { + len = 0; + if (list->cb->get_column_header) { + item = data_get_column_header(list, col); + len = strlen(item); + } + list->columns[col].width = len; + } + + for (row = data_get_first_row(list); + row != NULL; + row = data_get_next_row(list, row)) { + for (col = 0; col < list->ncols; ++col) { + item = data_get_item_prefix(list, row, col); + SMB_ASSERT(item != NULL); + len = strlen(item); + + item = data_get_item_label(list, row, col); + SMB_ASSERT(item != NULL); + len += strlen(item); + if (len > list->columns[col].width) { + list->columns[col].width = len; + } + } + } + + /* calculate row width */ + for (width = 0, col = 0; col < list->ncols; ++col) { + width += list->columns[col].width; + } + /* width including column spacing and separations */ + total_width = width + (list->ncols - 1) * 3; + /* if everything fits, we're done */ + if (total_width <= list->window_width) { + return WERR_OK; + } + + overflow = total_width - list->window_width; + + /* attempt to trim as much as possible to fit all the columns to + the window */ + while (overflow && (colp = find_widest_column(list))) { + colp->width--; + overflow--; + } + + return WERR_OK; +} + +static void highlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_REVERSE, 0, NULL); +} + +static void unhighlight_current_row(struct multilist *list) +{ + mvwchgat(list->pad, list->cursor_row, 0, -1, A_NORMAL, 0, NULL); +} + +const void *multilist_get_data(struct multilist *list) +{ + return list->data; +} + +WERROR multilist_set_data(struct multilist *list, const void *data) +{ + WERROR rv; + + SMB_ASSERT(list->window != NULL); + list->data = data; + + calc_column_widths(list); + + if (list->pad) { + delwin(list->pad); + } + /* construct a pad that is exactly the width of the window, and + as tall as required to fit all data rows. */ + list->nrows = data_get_row_count(list); + list->pad = newpad(MAX(list->nrows, 1), list->window_width); + if (list->pad == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + /* add the column headers to the window and render all rows to + the pad. */ + werase(list->window); + put_header(list); + rv = put_data(list); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + /* initialize the cursor */ + list->start_row = 0; + list->cursor_row = 0; + list->current_row = data_get_first_row(list); + highlight_current_row(list); + + return WERR_OK; +} + +static int get_window_height(struct multilist *list) +{ + int height; + + height = list->window_height; + if (list->cb->get_column_header) { + height--; + } + + return height; +} + +static void fix_start_row(struct multilist *list) +{ + int height; + + /* adjust start_row so that the cursor appears on the screen */ + + height = get_window_height(list); + if (list->cursor_row < list->start_row) { + list->start_row = list->cursor_row; + } else if (list->cursor_row >= list->start_row + height) { + list->start_row = list->cursor_row - height + 1; + } + if (list->nrows > height && list->nrows - list->start_row < height) { + list->start_row = list->nrows - height; + } +} + +WERROR multilist_set_window(struct multilist *list, WINDOW *window) +{ + int maxy, maxx; + bool rerender = false; + + getmaxyx(window, maxy, maxx); + + /* rerender pad if window width is different. */ + if (list->data && maxx != list->window_width) { + rerender = true; + } + + list->window = window; + list->window_width = maxx; + list->window_height = maxy; + list->start_row = 0; + if (rerender) { + const void *row = multilist_get_current_row(list); + WERROR rv = multilist_set_data(list, list->data); + if (W_ERROR_IS_OK(rv) && row) { + multilist_set_current_row(list, row); + } + return rv; + } else { + put_header(list); + fix_start_row(list); + } + + return WERR_OK; +} + +void multilist_refresh(struct multilist *list) +{ + int window_start_row, height; + + if (list->nrows == 0) { + return; + } + + /* copy from pad, starting at start_row, to the window, accounting + for the column header (if present). */ + height = MIN(list->window_height, list->nrows); + window_start_row = 0; + if (list->cb->get_column_header) { + window_start_row = 1; + if (height < list->window_height) { + height++; + } + } + copywin(list->pad, list->window, list->start_row, 0, + window_start_row, 0, height - 1, list->window_width - 1, + false); +} + +void multilist_driver(struct multilist *list, int c) +{ + unsigned page; + const void *tmp = NULL; + + if (list->nrows == 0) { + return; + } + + switch (c) { + case ML_CURSOR_UP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row--; + tmp = data_get_prev_row(list, list->current_row); + break; + case ML_CURSOR_DOWN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row++; + tmp = data_get_next_row(list, list->current_row); + break; + case ML_CURSOR_PGUP: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->cursor_row) { + list->cursor_row = 0; + } else { + list->cursor_row -= page; + list->start_row -= page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_PGDN: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + page = get_window_height(list); + if (page > list->nrows - list->cursor_row - 1) { + list->cursor_row = list->nrows - 1; + } else { + list->cursor_row += page; + list->start_row += page; + } + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_HOME: + if (list->cursor_row == 0) { + return; + } + unhighlight_current_row(list); + list->cursor_row = 0; + tmp = data_get_row_n(list, list->cursor_row); + break; + case ML_CURSOR_END: + if (list->cursor_row == list->nrows - 1) { + return; + } + unhighlight_current_row(list); + list->cursor_row = list->nrows - 1; + tmp = data_get_row_n(list, list->cursor_row); + break; + } + + SMB_ASSERT(tmp); + list->current_row = tmp; + highlight_current_row(list); + fix_start_row(list); +} + +const void *multilist_get_current_row(struct multilist *list) +{ + return list->current_row; +} + +void multilist_set_current_row(struct multilist *list, const void *row) +{ + unsigned i; + const void *tmp; + + for (i = 0, tmp = data_get_first_row(list); + tmp != NULL; + ++i, tmp = data_get_next_row(list, tmp)) { + if (tmp == row) { + unhighlight_current_row(list); + list->cursor_row = i; + list->current_row = row; + highlight_current_row(list); + fix_start_row(list); + return; + } + } +} diff --git a/source3/utils/regedit_list.h b/source3/utils/regedit_list.h new file mode 100644 index 0000000..abd6ffd --- /dev/null +++ b/source3/utils/regedit_list.h @@ -0,0 +1,82 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _REGEDIT_LIST_H_ +#define _REGEDIT_LIST_H_ + +#include "includes.h" +#include <ncurses.h> + +struct multilist_accessors { + /* (optional) return the column header for col */ + const char *(*get_column_header)(const void *data, unsigned col); + + /* return a pointer to the first row of data */ + const void *(*get_first_row)(const void *data); + + /* (optional) return a count of all data rows */ + size_t (*get_row_count)(const void *data); + + /* return the next row or NULL if there aren't any more */ + const void *(*get_next_row)(const void *data, const void *row); + + /* (optional) return the previous row or NULL if row is on top. */ + const void *(*get_prev_row)(const void *data, const void *row); + + /* (optional) return row n of data */ + const void *(*get_row_n)(const void *data, size_t n); + + /* return the label for row and col */ + const char *(*get_item_label)(const void *row, unsigned col); + + /* (optional) return a prefix string to be prepended to an item's + label. */ + const char *(*get_item_prefix)(const void *row, unsigned col); +}; + +struct multilist_column { + size_t width; + unsigned int align_right:1; +}; + +struct multilist; + +struct multilist *multilist_new(TALLOC_CTX *ctx, WINDOW *window, + const struct multilist_accessors *cb, + unsigned ncol); +struct multilist_column *multilist_column_config(struct multilist *list, + unsigned col); +WERROR multilist_set_window(struct multilist *list, WINDOW *window); +const void *multilist_get_data(struct multilist *list); +WERROR multilist_set_data(struct multilist *list, const void *data); +void multilist_refresh(struct multilist *list); + +enum { + ML_CURSOR_UP, + ML_CURSOR_DOWN, + ML_CURSOR_PGUP, + ML_CURSOR_PGDN, + ML_CURSOR_HOME, + ML_CURSOR_END +}; +void multilist_driver(struct multilist *list, int c); +const void *multilist_get_current_row(struct multilist *list); +void multilist_set_current_row(struct multilist *list, const void *row); + +#endif diff --git a/source3/utils/regedit_samba3.c b/source3/utils/regedit_samba3.c new file mode 100644 index 0000000..a1f8b39 --- /dev/null +++ b/source3/utils/regedit_samba3.c @@ -0,0 +1,244 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* s3 registry backend, adapted from rpc backend */ + +#include "includes.h" +#include "lib/registry/registry.h" +#include "regedit.h" + +struct samba3_key { + struct registry_key key; + struct samba3_registry_key s3key; +}; + +struct samba3_registry_context { + struct registry_context context; +}; + +static struct registry_operations reg_backend_s3; + +static struct { + uint32_t hkey; + const char *name; +} known_hives[] = { + { HKEY_LOCAL_MACHINE, "HKLM" }, + { HKEY_CURRENT_USER, "HKCU" }, + { HKEY_CLASSES_ROOT, "HKCR" }, + { HKEY_PERFORMANCE_DATA, "HKPD" }, + { HKEY_USERS, "HKU" }, + { HKEY_DYN_DATA, "HKDD" }, + { HKEY_CURRENT_CONFIG, "HKCC" }, + { 0, NULL } +}; + +static WERROR samba3_get_predefined_key(struct registry_context *ctx, + uint32_t hkey_type, + struct registry_key **k) +{ + int n; + const char *name; + struct samba3_key *mykeydata; + + *k = NULL; + name = NULL; + + for(n = 0; known_hives[n].hkey; n++) { + if(known_hives[n].hkey == hkey_type) { + name = known_hives[n].name; + break; + } + } + + if (name == NULL) { + DEBUG(1, ("No such hive %d\n", hkey_type)); + return WERR_NO_MORE_ITEMS; + } + + mykeydata = talloc_zero(ctx, struct samba3_key); + W_ERROR_HAVE_NO_MEMORY(mykeydata); + mykeydata->key.context = ctx; + *k = (struct registry_key *)mykeydata; + + return reg_openhive_wrap(ctx, name, &mykeydata->s3key); +} + +static WERROR samba3_open_key(TALLOC_CTX *mem_ctx, struct registry_key *h, + const char *name, struct registry_key **key) +{ + struct samba3_key *parentkeydata, *mykeydata; + + parentkeydata = talloc_get_type(h, struct samba3_key); + + mykeydata = talloc_zero(mem_ctx, struct samba3_key); + W_ERROR_HAVE_NO_MEMORY(mykeydata); + mykeydata->key.context = h->context; + *key = (struct registry_key *)mykeydata; + + return reg_openkey_wrap(mem_ctx, &parentkeydata->s3key, + name, &mykeydata->s3key); +} + +static WERROR samba3_get_value_by_index(TALLOC_CTX *mem_ctx, + const struct registry_key *parent, + uint32_t n, + const char **value_name, + uint32_t *type, + DATA_BLOB *data) +{ + struct samba3_key *mykeydata; + + mykeydata = talloc_get_type(parent, struct samba3_key); + + return reg_enumvalue_wrap(mem_ctx, &mykeydata->s3key, n, + discard_const(value_name), type, data); +} + +static WERROR samba3_get_value_by_name(TALLOC_CTX *mem_ctx, + const struct registry_key *parent, + const char *value_name, + uint32_t *type, + DATA_BLOB *data) +{ + struct samba3_key *mykeydata; + + mykeydata = talloc_get_type(parent, struct samba3_key); + + return reg_queryvalue_wrap(mem_ctx, &mykeydata->s3key, + value_name, type, data); +} + +static WERROR samba3_get_subkey_by_index(TALLOC_CTX *mem_ctx, + const struct registry_key *parent, + uint32_t n, + const char **name, + const char **keyclass, + NTTIME *last_changed_time) +{ + struct samba3_key *mykeydata; + + mykeydata = talloc_get_type(parent, struct samba3_key); + + *keyclass = NULL; + + return reg_enumkey_wrap(mem_ctx, &mykeydata->s3key, n, + discard_const(name), last_changed_time); +} + +static WERROR samba3_add_key(TALLOC_CTX *mem_ctx, + struct registry_key *parent, const char *path, + const char *key_class, + struct security_descriptor *sec, + struct registry_key **key) +{ + struct samba3_key *parentkd; + struct samba3_key *newkd; + + parentkd = talloc_get_type(parent, struct samba3_key); + newkd = talloc_zero(mem_ctx, struct samba3_key); + + W_ERROR_HAVE_NO_MEMORY(newkd); + newkd->key.context = parent->context; + *key = (struct registry_key *)newkd; + + return reg_createkey_wrap(mem_ctx, &parentkd->s3key, path, + &newkd->s3key); +} + +static WERROR samba3_del_key(TALLOC_CTX *mem_ctx, struct registry_key *parent, + const char *name) +{ + struct samba3_key *mykeydata; + + mykeydata = talloc_get_type(parent, struct samba3_key); + + return reg_deletekey_wrap(&mykeydata->s3key, name); +} + +static WERROR samba3_del_value(TALLOC_CTX *mem_ctx, struct registry_key *key, + const char *name) +{ + struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key); + + return reg_deletevalue_wrap(&mykeydata->s3key, name); +} + +static WERROR samba3_set_value(struct registry_key *key, const char *name, + uint32_t type, const DATA_BLOB data) +{ + struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key); + + return reg_setvalue_wrap(&mykeydata->s3key, name, type, data); +} + +static WERROR samba3_get_info(TALLOC_CTX *mem_ctx, + const struct registry_key *key, + const char **classname, + uint32_t *num_subkeys, + uint32_t *num_values, + NTTIME *last_changed_time, + uint32_t *max_subkeylen, + uint32_t *max_valnamelen, + uint32_t *max_valbufsize) +{ + struct samba3_key *mykeydata = talloc_get_type(key, struct samba3_key); + uint32_t max_subkeysize, secdescsize; + + return reg_queryinfokey_wrap(&mykeydata->s3key, num_subkeys, + max_subkeylen, &max_subkeysize, + num_values, max_valnamelen, + max_valbufsize, &secdescsize, + last_changed_time); +} + +static struct registry_operations reg_backend_s3 = { + .name = "samba3", + .open_key = samba3_open_key, + .get_predefined_key = samba3_get_predefined_key, + .enum_key = samba3_get_subkey_by_index, + .enum_value = samba3_get_value_by_index, + .get_value = samba3_get_value_by_name, + .set_value = samba3_set_value, + .delete_value = samba3_del_value, + .create_key = samba3_add_key, + .delete_key = samba3_del_key, + .get_key_info = samba3_get_info, +}; + +WERROR reg_open_samba3(TALLOC_CTX *mem_ctx, struct registry_context **ctx) +{ + WERROR rv; + struct samba3_registry_context *rctx; + + /* initialize s3 registry */ + rv = reg_init_wrap(); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + rctx = talloc_zero(mem_ctx, struct samba3_registry_context); + if (rctx == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + *ctx = (struct registry_context *)rctx; + (*ctx)->ops = ®_backend_s3; + + return WERR_OK; +} diff --git a/source3/utils/regedit_treeview.c b/source3/utils/regedit_treeview.c new file mode 100644 index 0000000..a3df94d --- /dev/null +++ b/source3/utils/regedit_treeview.c @@ -0,0 +1,705 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "regedit_treeview.h" +#include "regedit_list.h" +#include "lib/registry/registry.h" + +#define HEADING_X 3 + +static int tree_node_free(struct tree_node *node) +{ + DEBUG(9, ("tree_node_free('%s', %p)\n", node->name, node)); + return 0; +} + +struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, + const char *name, struct registry_key *key) +{ + struct tree_node *node; + + node = talloc_zero(ctx, struct tree_node); + if (!node) { + return NULL; + } + talloc_set_destructor(node, tree_node_free); + DEBUG(9, ("tree_node_new('%s', %p)\n", name, node)); + + node->name = talloc_strdup(node, name); + if (!node->name) { + talloc_free(node); + return NULL; + } + + if (key) { + node->key = talloc_steal(node, key); + } + + if (parent) { + /* Check if this node is the first descendant of parent. */ + if (!parent->child_head) { + parent->child_head = node; + } + node->parent = parent; + } + + return node; +} + +/* prepare a root node with all available hives as children */ +struct tree_node *tree_node_new_root(TALLOC_CTX *ctx, + struct registry_context *regctx) +{ + const char *hives[] = { + "HKEY_CLASSES_ROOT", + "HKEY_CURRENT_USER", + "HKEY_LOCAL_MACHINE", + "HKEY_PERFORMANCE_DATA", + "HKEY_USERS", + "HKEY_CURRENT_CONFIG", + "HKEY_DYN_DATA", + "HKEY_PERFORMANCE_TEXT", + "HKEY_PERFORMANCE_NLSTEXT", + NULL + }; + struct tree_node *root, *prev, *node; + struct registry_key *key; + WERROR rv; + size_t i; + + root = tree_node_new(ctx, NULL, "ROOT", NULL); + if (root == NULL) { + return NULL; + } + prev = NULL; + + for (i = 0; hives[i] != NULL; ++i) { + rv = reg_get_predefined_key_by_name(regctx, hives[i], &key); + if (!W_ERROR_IS_OK(rv)) { + continue; + } + + node = tree_node_new(root, root, hives[i], key); + if (node == NULL) { + return NULL; + } + if (prev) { + tree_node_append(prev, node); + } + prev = node; + } + + return root; +} + +void tree_node_append(struct tree_node *left, struct tree_node *right) +{ + if (left->next) { + right->next = left->next; + left->next->previous = right; + } + left->next = right; + right->previous = left; +} + +void tree_node_append_last(struct tree_node *list, struct tree_node *node) +{ + tree_node_append(tree_node_last(list), node); +} + +struct tree_node *tree_node_pop(struct tree_node **plist) +{ + struct tree_node *node; + + node = *plist; + + if (node == NULL) + return NULL; + + *plist = node->previous; + if (*plist == NULL) { + *plist = node->next; + } + if (node->previous) { + node->previous->next = node->next; + } + if (node->next) { + node->next->previous = node->previous; + } + if (node->parent && node->parent->child_head == node) { + node->parent->child_head = node->next; + } + node->next = NULL; + node->previous = NULL; + + return node; +} + +struct tree_node *tree_node_first(struct tree_node *list) +{ + /* Grab the first node in this list from the parent if available. */ + if (list->parent) { + return list->parent->child_head; + } + + while (list && list->previous) { + list = list->previous; + } + + return list; +} + +struct tree_node *tree_node_last(struct tree_node *list) +{ + while (list && list->next) { + list = list->next; + } + + return list; +} + +static uint32_t get_num_subkeys(struct tree_node *node) +{ + const char *classname; + uint32_t num_subkeys; + uint32_t num_values; + NTTIME last_change_time; + uint32_t max_subkeynamelen; + uint32_t max_valnamelen; + uint32_t max_valbufsize; + WERROR rv; + + rv = reg_key_get_info(node, node->key, &classname, &num_subkeys, + &num_values, &last_change_time, + &max_subkeynamelen, &max_valnamelen, + &max_valbufsize); + + if (W_ERROR_IS_OK(rv)) { + return num_subkeys; + } + + return 0; +} + +WERROR tree_node_reopen_key(struct registry_context *ctx, + struct tree_node *node) +{ + SMB_ASSERT(node->parent != NULL); + SMB_ASSERT(node->name != NULL); + TALLOC_FREE(node->key); + + if (tree_node_is_top_level(node)) { + WERROR rv; + struct registry_key *key; + rv = reg_get_predefined_key_by_name(ctx, node->name, &key); + if (W_ERROR_IS_OK(rv)) { + node->key = talloc_steal(node, key); + } + return rv; + } + + return reg_open_key(node, node->parent->key, node->name, &node->key); +} + +bool tree_node_has_children(struct tree_node *node) +{ + if (node->child_head) { + return true; + } + + return get_num_subkeys(node) > 0; +} + +static int node_cmp(struct tree_node **a, struct tree_node **b) +{ + return strcmp((*a)->name, (*b)->name); +} + +void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node) +{ + list = tree_node_first(list); + + if (node_cmp(&list, &node) >= 0) { + tree_node_append(node, list); + if (list->parent) { + list->parent->child_head = node; + } + return; + } + + while (list->next && node_cmp(&list->next, &node) < 0) { + list = list->next; + } + + tree_node_append(list, node); +} + +WERROR tree_node_load_children(struct tree_node *node) +{ + struct registry_key *key; + const char *reg_key_name, *klass; + NTTIME modified; + uint32_t i, nsubkeys, count; + WERROR rv; + struct tree_node *prev, **array; + + /* does this node already have it's children loaded? */ + if (node->child_head) + return WERR_OK; + + nsubkeys = get_num_subkeys(node); + if (nsubkeys == 0) + return WERR_OK; + + array = talloc_zero_array(node, struct tree_node *, nsubkeys); + if (array == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + for (count = 0, i = 0; i < nsubkeys; ++i) { + rv = reg_key_get_subkey_by_index(node, node->key, i, + ®_key_name, &klass, + &modified); + if (!W_ERROR_IS_OK(rv)) { + goto finish; + } + + rv = reg_open_key(node, node->key, reg_key_name, &key); + if (!W_ERROR_IS_OK(rv)) { + continue; + } + + array[count] = tree_node_new(array, node, reg_key_name, key); + if (array[count] == NULL) { + rv = WERR_NOT_ENOUGH_MEMORY; + goto finish; + } + ++count; + } + + if (count) { + TYPESAFE_QSORT(array, count, node_cmp); + + for (i = 1, prev = array[0]; i < count; ++i) { + talloc_steal(node, array[i]); + tree_node_append(prev, array[i]); + prev = array[i]; + } + node->child_head = talloc_steal(node, array[0]); + + rv = WERR_OK; + } + +finish: + talloc_free(array); + + return rv; +} + +static WERROR next_depth_first(struct tree_node **node) +{ + WERROR rv = WERR_OK; + + SMB_ASSERT(node != NULL && *node != NULL); + + if (tree_node_has_children(*node)) { + /* 1. If the node has children, go to the first one. */ + rv = tree_node_load_children(*node); + if (W_ERROR_IS_OK(rv)) { + SMB_ASSERT((*node)->child_head != NULL); + *node = (*node)->child_head; + } + } else if ((*node)->next) { + /* 2. If there's a node directly after this one, go there */ + *node = (*node)->next; + } else { + /* 3. Otherwise, go up the hierarchy to find the next one */ + do { + *node = (*node)->parent; + if (*node && (*node)->next) { + *node = (*node)->next; + break; + } + } while (*node); + } + + return rv; +} + +static WERROR prev_depth_first(struct tree_node **node) +{ + WERROR rv = WERR_OK; + + SMB_ASSERT(node != NULL && *node != NULL); + + if ((*node)->previous) { + *node = (*node)->previous; + while (tree_node_has_children(*node)) { + rv = tree_node_load_children(*node); + if (W_ERROR_IS_OK(rv)) { + SMB_ASSERT((*node)->child_head != NULL); + *node = tree_node_last((*node)->child_head); + } + } + } else if (!tree_node_is_top_level(*node)) { + *node = (*node)->parent; + } else { + *node = NULL; + } + + return rv; +} + +bool tree_node_next(struct tree_node **node, bool depth, WERROR *err) +{ + *err = WERR_OK; + + if (*node == NULL) { + return false; + } + + if (depth) { + *err = next_depth_first(node); + } else { + *node = (*node)->next; + } + + return *node != NULL && W_ERROR_IS_OK(*err); +} + +bool tree_node_prev(struct tree_node **node, bool depth, WERROR *err) +{ + *err = WERR_OK; + + if (*node == NULL) { + return false; + } + + if (depth) { + *err = prev_depth_first(node); + } else { + *node = (*node)->previous; + } + + return *node != NULL && W_ERROR_IS_OK(*err); +} + +void tree_view_clear(struct tree_view *view) +{ + multilist_set_data(view->list, NULL); +} + +WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root) +{ + multilist_set_data(view->list, NULL); + talloc_free(view->root); + view->root = root; + return tree_view_update(view, root->child_head); +} + +WERROR tree_view_set_path(struct tree_view *view, const char **path) +{ + struct tree_node *top, *node; + WERROR rv; + + top = view->root->child_head; + while (*path) { + for (node = top; node != NULL; node = node->next) { + if (strcmp(*path, node->name) == 0) { + if (path[1] && tree_node_has_children(node)) { + rv = tree_node_load_children(node); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + SMB_ASSERT(node->child_head); + top = node->child_head; + break; + } else { + tree_view_update(view, top); + tree_view_set_current_node(view, node); + return WERR_OK; + } + } + } + ++path; + } + + return WERR_OK; +} + +WERROR tree_view_update(struct tree_view *view, struct tree_node *list) +{ + WERROR rv; + + rv = multilist_set_data(view->list, list); + if (W_ERROR_IS_OK(rv)) { + multilist_refresh(view->list); + } + + return rv; +} + +/* is this node in the current level? */ +bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node) +{ + const struct tree_node *first; + + first = multilist_get_data(view->list); + + return first && first->parent == node->parent; +} + +void tree_view_set_current_node(struct tree_view *view, struct tree_node *node) +{ + multilist_set_current_row(view->list, node); +} + +struct tree_node *tree_view_get_current_node(struct tree_view *view) +{ + const void *row = multilist_get_current_row(view->list); + return talloc_get_type_abort(row, struct tree_node); +} + +void tree_view_driver(struct tree_view *view, int c) +{ + multilist_driver(view->list, c); +} + +void tree_view_set_selected(struct tree_view *view, bool reverse) +{ + attr_t attr = A_NORMAL; + + if (reverse) { + attr = A_REVERSE; + } + mvwchgat(view->window, 0, HEADING_X, 3, attr, 0, NULL); +} + +void tree_view_show(struct tree_view *view) +{ + multilist_refresh(view->list); + touchwin(view->window); + wnoutrefresh(view->window); + wnoutrefresh(view->sub); +} + +static int tree_view_free(struct tree_view *view) +{ + if (view->panel) { + del_panel(view->panel); + } + if (view->sub) { + delwin(view->sub); + } + if (view->window) { + delwin(view->window); + } + + return 0; +} + +static const char *tv_get_column_header(const void *data, unsigned col) +{ + SMB_ASSERT(col == 0); + return "Name"; +} + +static const void *tv_get_first_row(const void *data) +{ + if (data == NULL) { + return NULL; + } + + return talloc_get_type_abort(data, struct tree_node); +} + +static const void *tv_get_next_row(const void *data, const void *row) +{ + const struct tree_node *node; + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); + return node->next; +} + +static const void *tv_get_prev_row(const void *data, const void *row) +{ + const struct tree_node *node; + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); + return node->previous; +} + +static const char *tv_get_item_prefix(const void *row, unsigned col) +{ + struct tree_node *node; + + SMB_ASSERT(col == 0); + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); + if (tree_node_has_children(node)) { + return "+"; + } + return " "; +} + +static const char *tv_get_item_label(const void *row, unsigned col) +{ + const struct tree_node *node; + SMB_ASSERT(col == 0); + SMB_ASSERT(row != NULL); + node = talloc_get_type_abort(row, struct tree_node); + return node->name; +} + +static struct multilist_accessors tv_accessors = { + .get_column_header = tv_get_column_header, + .get_first_row = tv_get_first_row, + .get_next_row = tv_get_next_row, + .get_prev_row = tv_get_prev_row, + .get_item_prefix = tv_get_item_prefix, + .get_item_label = tv_get_item_label +}; + +struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, + int nlines, int ncols, int begin_y, + int begin_x) +{ + struct tree_view *view; + + view = talloc_zero(ctx, struct tree_view); + if (view == NULL) { + return NULL; + } + + talloc_set_destructor(view, tree_view_free); + + view->window = newwin(nlines, ncols, begin_y, begin_x); + if (view->window == NULL) { + goto fail; + } + view->sub = subwin(view->window, nlines - 2, ncols - 2, + begin_y + 1, begin_x + 1); + if (view->sub == NULL) { + goto fail; + } + box(view->window, 0, 0); + mvwprintw(view->window, 0, HEADING_X, "Key"); + + view->panel = new_panel(view->window); + if (view->panel == NULL) { + goto fail; + } + view->root = root; + + view->list = multilist_new(view, view->sub, &tv_accessors, 1); + if (view->list == NULL) { + goto fail; + } + tree_view_update(view, root->child_head); + + return view; + +fail: + talloc_free(view); + + return NULL; +} + +void tree_view_resize(struct tree_view *view, int nlines, int ncols, + int begin_y, int begin_x) +{ + WINDOW *nwin, *nsub; + + nwin = newwin(nlines, ncols, begin_y, begin_x); + if (nwin == NULL) { + return; + } + nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); + if (nsub == NULL) { + delwin(nwin); + return; + } + replace_panel(view->panel, nwin); + delwin(view->sub); + delwin(view->window); + view->window = nwin; + view->sub = nsub; + box(view->window, 0, 0); + mvwprintw(view->window, 0, HEADING_X, "Key"); + multilist_set_window(view->list, view->sub); + tree_view_show(view); +} + +const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node) +{ + const char **array; + size_t nitems, idx; + struct tree_node *p; + + for (nitems = 0, p = node; !tree_node_is_root(p); p = p->parent) { + ++nitems; + } + + array = talloc_zero_array(ctx, const char *, nitems + 1); + if (array == NULL) { + return NULL; + } + + for (idx = nitems - 1, p = node; + !tree_node_is_root(p); + p = p->parent, --idx) { + array[idx] = talloc_strdup(array, p->name); + if (array[idx] == NULL) { + talloc_free(discard_const(array)); + return NULL; + } + } + + return array; +} + +/* print the path of node to label */ +size_t tree_node_print_path(WINDOW *label, struct tree_node *node) +{ + size_t len = 1; + const char **path; + TALLOC_CTX *frame; + + if (node == NULL) + return 0; + + werase(label); + wprintw(label, "/"); + + if (tree_node_is_top_level(node)) + return 0; + + frame = talloc_stackframe(); + path = tree_node_get_path(frame, node->parent); + + while (*path) { + len += strlen(*path) + 1; + wprintw(label, "%s/", *path); + ++path; + } + + talloc_free(frame); + + return len; +} diff --git a/source3/utils/regedit_treeview.h b/source3/utils/regedit_treeview.h new file mode 100644 index 0000000..4b892bb --- /dev/null +++ b/source3/utils/regedit_treeview.h @@ -0,0 +1,89 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _REGEDIT_TREEVIEW_H_ +#define _REGEDIT_TREEVIEW_H_ + +#include "includes.h" +#include <ncurses.h> +#include <panel.h> + +struct registry_key; + +struct tree_node { + + char *name; + struct registry_key *key; + + struct tree_node *parent; + struct tree_node *child_head; + struct tree_node *previous; + struct tree_node *next; +}; + +struct multilist; + +struct tree_view { + + struct tree_node *root; + WINDOW *window; + WINDOW *sub; + PANEL *panel; + struct multilist *list; +}; + +struct registry_context; + +struct tree_node *tree_node_new(TALLOC_CTX *ctx, struct tree_node *parent, + const char *name, struct registry_key *key); +struct tree_node *tree_node_new_root(TALLOC_CTX *ctx, + struct registry_context *regctx); +#define tree_node_is_root(node) ((node)->key == NULL) +#define tree_node_is_top_level(node) tree_node_is_root((node)->parent) +void tree_node_append(struct tree_node *left, struct tree_node *right); +struct tree_node *tree_node_pop(struct tree_node **plist); +struct tree_node *tree_node_first(struct tree_node *list); +struct tree_node *tree_node_last(struct tree_node *list); +bool tree_node_next(struct tree_node **node, bool depth, WERROR *err); +bool tree_node_prev(struct tree_node **node, bool depth, WERROR *err); +void tree_node_append_last(struct tree_node *list, struct tree_node *node); +size_t tree_node_print_path(WINDOW *label, struct tree_node *node); +const char **tree_node_get_path(TALLOC_CTX *ctx, struct tree_node *node); +struct tree_view *tree_view_new(TALLOC_CTX *ctx, struct tree_node *root, + int nlines, int ncols, + int begin_y, int begin_x); +void tree_view_set_selected(struct tree_view *view, bool select); +void tree_view_resize(struct tree_view *view, int nlines, int ncols, + int begin_y, int begin_x); +void tree_view_show(struct tree_view *view); +void tree_view_clear(struct tree_view *view); +WERROR tree_view_set_root(struct tree_view *view, struct tree_node *root); +WERROR tree_view_set_path(struct tree_view *view, const char **path); +WERROR tree_view_update(struct tree_view *view, struct tree_node *list); +WERROR tree_node_reopen_key(struct registry_context *ctx, + struct tree_node *node); +bool tree_node_has_children(struct tree_node *node); +WERROR tree_node_load_children(struct tree_node *node); +void tree_node_insert_sorted(struct tree_node *list, struct tree_node *node); +bool tree_view_is_node_visible(struct tree_view *view, struct tree_node *node); +void tree_view_set_current_node(struct tree_view *view, struct tree_node *node); +struct tree_node *tree_view_get_current_node(struct tree_view *view); +void tree_view_driver(struct tree_view *view, int c); + +#endif diff --git a/source3/utils/regedit_valuelist.c b/source3/utils/regedit_valuelist.c new file mode 100644 index 0000000..78ea3fa --- /dev/null +++ b/source3/utils/regedit_valuelist.c @@ -0,0 +1,496 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "regedit.h" +#include "regedit_valuelist.h" +#include "regedit_list.h" +#include "lib/registry/registry.h" + +#define HEADING_X 3 + +static int value_list_free(struct value_list *vl) +{ + if (vl->panel) { + del_panel(vl->panel); + } + if (vl->sub) { + delwin(vl->sub); + } + if (vl->window) { + delwin(vl->window); + } + + return 0; +} + +static const char *vl_get_column_header(const void *data, unsigned col) +{ + switch (col) { + case 0: + return "Name"; + case 1: + return "Type"; + case 2: + return "Data"; + } + + return "???"; +} + +static const void *vl_get_first_row(const void *data) +{ + const struct value_list *vl; + + if (data) { + vl = talloc_get_type_abort(data, struct value_list); + if (vl->nvalues) { + return &vl->values[0]; + } + } + + return NULL; +} + +static const void *vl_get_next_row(const void *data, const void *row) +{ + const struct value_list *vl; + const struct value_item *value = row; + + SMB_ASSERT(data != NULL); + SMB_ASSERT(value != NULL); + vl = talloc_get_type_abort(data, struct value_list); + if (value == &vl->values[vl->nvalues - 1]) { + return NULL; + } + + return value + 1; +} + +static const void *vl_get_prev_row(const void *data, const void *row) +{ + const struct value_list *vl; + const struct value_item *value = row; + + SMB_ASSERT(data != NULL); + SMB_ASSERT(value != NULL); + vl = talloc_get_type_abort(data, struct value_list); + if (value == &vl->values[0]) { + return NULL; + } + + return value - 1; +} + +static const char *vl_get_item_label(const void *row, unsigned col) +{ + const struct value_item *value = row; + + SMB_ASSERT(value != NULL); + SMB_ASSERT(value->value_name != NULL); + switch (col) { + case 0: + return value->value_name; + case 1: + return str_regtype(value->type); + case 2: + if (value->value) { + return value->value; + } + return ""; + } + + return "???"; +} + +static struct multilist_accessors vl_accessors = { + .get_column_header = vl_get_column_header, + .get_first_row = vl_get_first_row, + .get_next_row = vl_get_next_row, + .get_prev_row = vl_get_prev_row, + .get_item_label = vl_get_item_label +}; + +struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, + int begin_y, int begin_x) +{ + struct value_list *vl; + + vl = talloc_zero(ctx, struct value_list); + if (vl == NULL) { + return NULL; + } + + talloc_set_destructor(vl, value_list_free); + + vl->window = newwin(nlines, ncols, begin_y, begin_x); + if (vl->window == NULL) { + goto fail; + } + vl->sub = subwin(vl->window, nlines - 2, ncols - 2, + begin_y + 1, begin_x + 1); + if (vl->sub == NULL) { + goto fail; + } + box(vl->window, 0, 0); + mvwprintw(vl->window, 0, HEADING_X, "Value"); + + vl->panel = new_panel(vl->window); + if (vl->panel == NULL) { + goto fail; + } + + vl->list = multilist_new(vl, vl->sub, &vl_accessors, 3); + if (vl->list == NULL) { + goto fail; + } + + return vl; + +fail: + talloc_free(vl); + + return NULL; +} + +void value_list_set_selected(struct value_list *vl, bool reverse) +{ + attr_t attr = A_NORMAL; + + if (reverse) { + attr = A_REVERSE; + } + mvwchgat(vl->window, 0, HEADING_X, 5, attr, 0, NULL); +} + +void value_list_resize(struct value_list *vl, int nlines, int ncols, + int begin_y, int begin_x) +{ + WINDOW *nwin, *nsub; + + nwin = newwin(nlines, ncols, begin_y, begin_x); + if (nwin == NULL) { + return; + } + nsub = subwin(nwin, nlines - 2, ncols - 2, begin_y + 1, begin_x + 1); + if (nsub == NULL) { + delwin(nwin); + return; + } + replace_panel(vl->panel, nwin); + delwin(vl->sub); + delwin(vl->window); + vl->window = nwin; + vl->sub = nsub; + box(vl->window, 0, 0); + mvwprintw(vl->window, 0, HEADING_X, "Value"); + multilist_set_window(vl->list, vl->sub); + value_list_show(vl); +} + +static uint32_t get_num_values(TALLOC_CTX *ctx, const struct registry_key *key) +{ + const char *classname; + uint32_t num_subkeys; + uint32_t num_values; + NTTIME last_change_time; + uint32_t max_subkeynamelen; + uint32_t max_valnamelen; + uint32_t max_valbufsize; + WERROR rv; + + rv = reg_key_get_info(ctx, key, &classname, &num_subkeys, + &num_values, &last_change_time, + &max_subkeynamelen, &max_valnamelen, + &max_valbufsize); + + if (W_ERROR_IS_OK(rv)) { + return num_values; + } + + return 0; +} + +void value_list_show(struct value_list *vl) +{ + multilist_refresh(vl->list); + touchwin(vl->window); + wnoutrefresh(vl->window); + wnoutrefresh(vl->sub); +} + +static bool string_is_printable(const char *s) +{ + const char *p; + + for (p = s; *p; ++p) { + if (!isprint(*p)) { + return false; + } + } + + return true; +} + +static WERROR append_data_summary(TALLOC_CTX *ctx, struct value_item *vitem) +{ + char *tmp = NULL; + +/* This is adapted from print_registry_value() in net_registry_util.c */ + + switch(vitem->type) { + case REG_DWORD: { + uint32_t v = 0; + if (vitem->data.length >= 4) { + v = IVAL(vitem->data.data, 0); + } + tmp = talloc_asprintf(ctx, "0x%08x (%u)", v, v); + break; + } + case REG_SZ: + case REG_EXPAND_SZ: { + const char *s; + + if (!pull_reg_sz(ctx, &vitem->data, &s)) { + break; + } + vitem->unprintable = !string_is_printable(s); + if (vitem->unprintable) { + tmp = talloc_asprintf(ctx, "(unprintable)"); + } else { + tmp = talloc_asprintf(ctx, "%s", s); + } + break; + } + case REG_MULTI_SZ: { + size_t i, len; + const char **a; + const char *val; + + if (!pull_reg_multi_sz(ctx, &vitem->data, &a)) { + break; + } + for (len = 0; a[len] != NULL; ++len) { + } + tmp = talloc_asprintf(ctx, "(%u) ", (unsigned)len); + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + for (i = 0; i < len; ++i) { + if (!string_is_printable(a[i])) { + val = "(unprintable)"; + vitem->unprintable = true; + } else { + val = a[i]; + } + if (i == len - 1) { + tmp = talloc_asprintf_append(tmp, + "[%u]=\"%s\"", + (unsigned)i, val); + } else { + tmp = talloc_asprintf_append(tmp, + "[%u]=\"%s\", ", + (unsigned)i, val); + } + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + } + break; + } + case REG_BINARY: + tmp = talloc_asprintf(ctx, "(%d bytes)", + (int)vitem->data.length); + break; + default: + tmp = talloc_asprintf(ctx, "(unknown)"); + break; + } + + if (tmp == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + vitem->value = tmp; + + return WERR_OK; +} + +static int vitem_cmp(struct value_item *a, struct value_item *b) +{ + return strcmp(a->value_name, b->value_name); +} + +/* load only the value names into memory to enable searching */ +WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key) +{ + uint32_t nvalues; + uint32_t idx; + struct value_item *vitem, *new_items; + WERROR rv; + + multilist_set_data(vl->list, NULL); + vl->nvalues = 0; + TALLOC_FREE(vl->values); + + nvalues = get_num_values(vl, key); + if (nvalues == 0) { + return WERR_OK; + } + + new_items = talloc_zero_array(vl, struct value_item, nvalues); + if (new_items == NULL) { + return WERR_NOT_ENOUGH_MEMORY; + } + + for (idx = 0; idx < nvalues; ++idx) { + vitem = &new_items[idx]; + rv = reg_key_get_value_by_index(new_items, key, idx, + &vitem->value_name, + &vitem->type, + &vitem->data); + if (!W_ERROR_IS_OK(rv)) { + talloc_free(new_items); + return rv; + } + } + + TYPESAFE_QSORT(new_items, nvalues, vitem_cmp); + vl->nvalues = nvalues; + vl->values = new_items; + + return rv; +} + +/* sync up the UI with the list */ +WERROR value_list_sync(struct value_list *vl) +{ + uint32_t idx; + WERROR rv; + + for (idx = 0; idx < vl->nvalues; ++idx) { + rv = append_data_summary(vl->values, &vl->values[idx]); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + } + + rv = multilist_set_data(vl->list, vl); + if (W_ERROR_IS_OK(rv)) { + multilist_refresh(vl->list); + } + + return rv; +} + +WERROR value_list_load(struct value_list *vl, struct registry_key *key) +{ + WERROR rv; + + rv = value_list_load_quick(vl, key); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + rv = value_list_sync(vl); + + return rv; +} + +struct value_item *value_list_find_next_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match) +{ + struct value_item *end; + + if (!vl->values) { + return NULL; + } + + if (vitem) { + ++vitem; + } else { + vitem = &vl->values[0]; + } + + for (end = &vl->values[vl->nvalues]; vitem < end; ++vitem) { + if (match(vitem->value_name, s)) { + return vitem; + } + } + + return NULL; +} + +struct value_item *value_list_find_prev_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match) +{ + struct value_item *end; + + if (!vl->values) { + return NULL; + } + + if (vitem) { + --vitem; + } else { + vitem = &vl->values[vl->nvalues - 1]; + } + + for (end = &vl->values[-1]; vitem > end; --vitem) { + if (match(vitem->value_name, s)) { + return vitem; + } + } + + return NULL; +} + +struct value_item *value_list_get_current_item(struct value_list *vl) +{ + return discard_const_p(struct value_item, + multilist_get_current_row(vl->list)); +} + +void value_list_set_current_item_by_name(struct value_list *vl, + const char *name) +{ + size_t i; + + for (i = 0; i < vl->nvalues; ++i) { + if (strequal(vl->values[i].value_name, name)) { + multilist_set_current_row(vl->list, &vl->values[i]); + return; + } + } +} + +void value_list_set_current_item(struct value_list *vl, + const struct value_item *item) +{ + multilist_set_current_row(vl->list, item); +} + +void value_list_driver(struct value_list *vl, int c) +{ + multilist_driver(vl->list, c); +} diff --git a/source3/utils/regedit_valuelist.h b/source3/utils/regedit_valuelist.h new file mode 100644 index 0000000..1178389 --- /dev/null +++ b/source3/utils/regedit_valuelist.h @@ -0,0 +1,72 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef _REGEDIT_VALUELIST_H_ +#define _REGEDIT_VALUELIST_H_ + +#include <ncurses.h> +#include <panel.h> + +struct registry_key; + +struct value_item { + uint32_t type; + DATA_BLOB data; + const char *value_name; + char *value; + bool unprintable; +}; + +struct multilist; + +struct value_list { + WINDOW *window; + WINDOW *sub; + PANEL *panel; + size_t nvalues; + struct value_item *values; + struct multilist *list; +}; +struct value_list *value_list_new(TALLOC_CTX *ctx, int nlines, int ncols, + int begin_y, int begin_x); +void value_list_show(struct value_list *vl); +void value_list_set_selected(struct value_list *vl, bool select); +const char **value_list_load_names(TALLOC_CTX *ctx, struct registry_key *key); +WERROR value_list_load(struct value_list *vl, struct registry_key *key); +void value_list_resize(struct value_list *vl, int nlines, int ncols, + int begin_y, int begin_x); +struct value_item *value_list_get_current_item(struct value_list *vl); +void value_list_set_current_item(struct value_list *vl, + const struct value_item *item); +void value_list_set_current_item_by_name(struct value_list *vl, + const char *name); +void value_list_driver(struct value_list *vl, int c); + +WERROR value_list_load_quick(struct value_list *vl, struct registry_key *key); +WERROR value_list_sync(struct value_list *vl); +struct value_item *value_list_find_next_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match); +struct value_item *value_list_find_prev_item(struct value_list *vl, + struct value_item *vitem, + const char *s, + regedit_search_match_fn_t match); + +#endif diff --git a/source3/utils/regedit_wrap.c b/source3/utils/regedit_wrap.c new file mode 100644 index 0000000..93297eb --- /dev/null +++ b/source3/utils/regedit_wrap.c @@ -0,0 +1,143 @@ +/* + * Samba Unix/Linux SMB client library + * Registry Editor + * Copyright (C) Christopher Davis 2012 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/* Wrap s3 registry API calls to avoid conflicts with 'struct registry_key', + etc, in s4 libregistry. */ + +#include "includes.h" +#include "registry.h" +#include "registry/reg_api.h" +#include "registry/reg_init_basic.h" +#include "registry/reg_util_token.h" + +#include "regedit.h" + +WERROR reg_openhive_wrap(TALLOC_CTX *ctx, const char *hive, + struct samba3_registry_key *pkey) +{ + struct security_token *token; + WERROR rv; + + SMB_ASSERT(pkey->key == NULL); + + rv = ntstatus_to_werror(registry_create_admin_token(ctx, &token)); + if (!W_ERROR_IS_OK(rv)) { + return rv; + } + + return reg_openhive(ctx, hive, REG_KEY_READ | REG_KEY_WRITE, token, + &pkey->key); +} + +WERROR reg_openkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent, + const char *name, struct samba3_registry_key *pkey) +{ + SMB_ASSERT(pkey->key == NULL); + return reg_openkey(ctx, parent->key, name, + REG_KEY_READ | REG_KEY_WRITE, &pkey->key); +} + +WERROR reg_enumvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + uint32_t idx, char **name, uint32_t *type, + DATA_BLOB *data) +{ + struct registry_value *val = NULL; + WERROR rv; + + rv = reg_enumvalue(ctx, key->key, idx, name, &val); + + if (val && W_ERROR_IS_OK(rv)) { + *type = (uint32_t)val->type; + *data = val->data; + } + + return rv; +} + +WERROR reg_queryvalue_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + const char *name, uint32_t *type, DATA_BLOB *data) +{ + struct registry_value *val = NULL; + WERROR rv; + + rv = reg_queryvalue(ctx, key->key, name, &val); + + if (val && W_ERROR_IS_OK(rv)) { + *type = (uint32_t)val->type; + *data = val->data; + } + + return rv; +} + +WERROR reg_enumkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *key, + uint32_t idx, char **name, NTTIME *last_write_time) +{ + return reg_enumkey(ctx, key->key, idx, name, last_write_time); +} + +WERROR reg_createkey_wrap(TALLOC_CTX *ctx, struct samba3_registry_key *parent, + const char *subkeypath, + struct samba3_registry_key *pkey) +{ + enum winreg_CreateAction act; + + SMB_ASSERT(pkey->key == NULL); + return reg_createkey(ctx, parent->key, subkeypath, + REG_KEY_READ | REG_KEY_WRITE, &pkey->key, &act); +} + +WERROR reg_deletekey_wrap(struct samba3_registry_key *parent, const char *path) +{ + return reg_deletekey(parent->key, path); +} + +WERROR reg_deletevalue_wrap(struct samba3_registry_key *key, const char *name) +{ + return reg_deletevalue(key->key, name); +} + +WERROR reg_queryinfokey_wrap(struct samba3_registry_key *key, + uint32_t *num_subkeys, uint32_t *max_subkeylen, + uint32_t *max_subkeysize, uint32_t *num_values, + uint32_t *max_valnamelen, + uint32_t *max_valbufsize, uint32_t *secdescsize, + NTTIME *last_changed_time) +{ + return reg_queryinfokey(key->key, num_subkeys, max_subkeylen, + max_subkeysize, num_values, max_valnamelen, + max_valbufsize, secdescsize, + last_changed_time); +} + +WERROR reg_setvalue_wrap(struct samba3_registry_key *key, const char *name, + uint32_t type, const DATA_BLOB data) +{ + struct registry_value val; + + val.type = type; + val.data = data; + + return reg_setvalue(key->key, name, &val); +} + +WERROR reg_init_wrap(void) +{ + return registry_init_basic(); +} diff --git a/source3/utils/sharesec.c b/source3/utils/sharesec.c new file mode 100644 index 0000000..a6481e2 --- /dev/null +++ b/source3/utils/sharesec.c @@ -0,0 +1,613 @@ +/* + * Unix SMB/Netbios implementation. + * Utility for managing share permissions + * + * Copyright (C) Tim Potter 2000 + * Copyright (C) Jeremy Allison 2000 + * Copyright (C) Jelmer Vernooij 2003 + * Copyright (C) Gerald (Jerry) Carter 2005. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +struct cli_state; + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "../libcli/security/security.h" +#include "passdb/machine_sid.h" +#include "util_sd.h" +#include "cmdline_contexts.h" +#include "lib/util/string_wrappers.h" +#include "lib/param/param.h" + +static TALLOC_CTX *ctx; + +enum acl_mode { SMB_ACL_DELETE, + SMB_ACL_MODIFY, + SMB_ACL_ADD, + SMB_ACL_SET, + SMB_SD_DELETE, + SMB_SD_SETSDDL, + SMB_SD_VIEWSDDL, + SMB_ACL_VIEW, + SMB_ACL_VIEW_ALL }; + +/******************************************************************** +********************************************************************/ + +static struct security_descriptor* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size ) +{ + struct security_descriptor *sd = NULL; + struct security_ace *ace; + struct security_acl *theacl; + int num_ace; + const char *pacl; + int i; + + if ( !szACL ) + return NULL; + + pacl = szACL; + num_ace = count_chars( pacl, ',' ) + 1; + + if ( !(ace = talloc_zero_array( mem_ctx, struct security_ace, num_ace )) ) + return NULL; + + for ( i=0; i<num_ace; i++ ) { + char *end_acl = strchr_m( pacl, ',' ); + fstring acl_string; + + strncpy( acl_string, pacl, MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1) ); + acl_string[MIN( PTR_DIFF( end_acl, pacl ), sizeof(fstring)-1)] = '\0'; + + if ( !parse_ace(NULL, &ace[i], acl_string ) ) + return NULL; + + pacl = end_acl; + pacl++; + } + + if ( !(theacl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) ) + return NULL; + + sd = make_sec_desc( mem_ctx, SD_REVISION, SEC_DESC_SELF_RELATIVE, + NULL, NULL, NULL, theacl, sd_size); + + return sd; +} + +/* add an ACE to a list of ACEs in a struct security_acl */ +static bool add_ace(TALLOC_CTX *mem_ctx, struct security_acl **the_acl, struct security_ace *ace) +{ + struct security_acl *acl = *the_acl; + + if (acl == NULL) { + acl = make_sec_acl(mem_ctx, 3, 1, ace); + if (acl == NULL) { + return false; + } + } + + if (acl->num_aces == UINT32_MAX) { + return false; + } + ADD_TO_ARRAY( + acl, struct security_ace, *ace, &acl->aces, &acl->num_aces); + *the_acl = acl; + return True; +} + +/* The MSDN is contradictory over the ordering of ACE entries in an ACL. + However NT4 gives a "The information may have been modified by a + computer running Windows NT 5.0" if denied ACEs do not appear before + allowed ACEs. */ + +static int ace_compare(struct security_ace *ace1, struct security_ace *ace2) +{ + if (security_ace_equal(ace1, ace2)) + return 0; + + if (ace1->type != ace2->type) + return ace2->type - ace1->type; + + if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) + return dom_sid_compare(&ace1->trustee, &ace2->trustee); + + if (ace1->flags != ace2->flags) + return ace1->flags - ace2->flags; + + if (ace1->access_mask != ace2->access_mask) + return ace1->access_mask - ace2->access_mask; + + if (ace1->size != ace2->size) + return ace1->size - ace2->size; + + return memcmp(ace1, ace2, sizeof(struct security_ace)); +} + +static void sort_acl(struct security_acl *the_acl) +{ + uint32_t i; + if (!the_acl) return; + + TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare); + + for (i=1;i<the_acl->num_aces;) { + if (security_ace_equal(&the_acl->aces[i-1], + &the_acl->aces[i])) { + ARRAY_DEL_ELEMENT( + the_acl->aces, i, the_acl->num_aces); + the_acl->num_aces--; + } else { + i++; + } + } +} + + +static int change_share_sec(TALLOC_CTX *mem_ctx, const char *sharename, char *the_acl, enum acl_mode mode) +{ + struct security_descriptor *sd = NULL; + struct security_descriptor *old = NULL; + size_t sd_size = 0; + uint32_t i, j; + NTSTATUS status; + + if (mode != SMB_ACL_SET && mode != SMB_SD_DELETE) { + if (!(old = get_share_security( mem_ctx, sharename, &sd_size )) ) { + fprintf(stderr, "Unable to retrieve permissions for share " + "[%s]\n", sharename); + return -1; + } + } + + if ( (mode != SMB_ACL_VIEW && mode != SMB_SD_DELETE) && + !(sd = parse_acl_string(mem_ctx, the_acl, &sd_size )) ) { + fprintf( stderr, "Failed to parse acl\n"); + return -1; + } + + switch (mode) { + case SMB_ACL_VIEW_ALL: + /* should not happen */ + return 0; + case SMB_ACL_VIEW: + sec_desc_print(NULL, stdout, old, false); + return 0; + case SMB_ACL_DELETE: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (security_ace_equal(&sd->dacl->aces[i], + &old->dacl->aces[j])) { + uint32_t k; + for (k=j; k<old->dacl->num_aces-1;k++) { + old->dacl->aces[k] = old->dacl->aces[k+1]; + } + old->dacl->num_aces--; + found = True; + break; + } + } + + if (!found) { + printf("ACL for ACE:"); + print_ace(NULL, stdout, &sd->dacl->aces[i], false); + printf(" not found\n"); + } + } + break; + case SMB_ACL_MODIFY: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (dom_sid_equal(&sd->dacl->aces[i].trustee, + &old->dacl->aces[j].trustee)) { + old->dacl->aces[j] = sd->dacl->aces[i]; + found = True; + } + } + + if (!found) { + struct dom_sid_buf buf; + printf("ACL for SID %s not found\n", + dom_sid_str_buf(&sd->dacl->aces[i].trustee, &buf)); + } + } + + if (sd->owner_sid) { + old->owner_sid = sd->owner_sid; + } + + if (sd->group_sid) { + old->group_sid = sd->group_sid; + } + break; + case SMB_ACL_ADD: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + add_ace(mem_ctx, &old->dacl, &sd->dacl->aces[i]); + } + break; + case SMB_ACL_SET: + old = sd; + break; + case SMB_SD_DELETE: + status = delete_share_security(sharename); + if (!NT_STATUS_IS_OK(status)) { + fprintf( stderr, "Failed to delete security descriptor for " + "share [%s]\n", sharename ); + return -1; + } + return 0; + default: + fprintf(stderr, "invalid command\n"); + return -1; + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + status = set_share_security(sharename, old); + if (!NT_STATUS_IS_OK(status)) { + fprintf( stderr, "Failed to store acl for share [%s]\n", sharename ); + return 2; + } + return 0; +} + +static int set_sharesec_sddl(const char *sharename, const char *sddl) +{ + struct security_descriptor *sd; + NTSTATUS status; + + sd = sddl_decode(talloc_tos(), sddl, get_global_sam_sid()); + if (sd == NULL) { + fprintf(stderr, "Failed to parse acl\n"); + return -1; + } + + status = set_share_security(sharename, sd); + TALLOC_FREE(sd); + if (!NT_STATUS_IS_OK(status)) { + fprintf(stderr, "Failed to store acl for share [%s]\n", + sharename); + return -1; + } + + return 0; +} + +static int view_sharesec_sddl(const char *sharename) +{ + struct security_descriptor *sd; + size_t sd_size; + char *acl; + + sd = get_share_security(talloc_tos(), sharename, &sd_size); + if (sd == NULL) { + fprintf(stderr, "Unable to retrieve permissions for share " + "[%s]\n", sharename); + return -1; + } + + acl = sddl_encode(talloc_tos(), sd, get_global_sam_sid()); + TALLOC_FREE(sd); + if (acl == NULL) { + fprintf(stderr, "Unable to sddl-encode permissions for share " + "[%s]\n", sharename); + return -1; + } + printf("%s\n", acl); + TALLOC_FREE(acl); + return 0; +} + +/******************************************************************** + main program +********************************************************************/ + +enum { + OPT_VIEW_ALL = 1000, + OPT_VIEW_SDDL, +}; + +int main(int argc, const char *argv[]) +{ + int opt; + int retval = 0; + enum acl_mode mode = SMB_ACL_SET; + static char *the_acl = NULL; + fstring sharename; + bool force_acl = False; + int snum; + poptContext pc; + bool initialize_sid = False; + bool ok; + struct loadparm_context *lp_ctx = NULL; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "remove", + .shortName = 'r', + .argInfo = POPT_ARG_STRING, + .arg = &the_acl, + .val = 'r', + .descrip = "Remove ACEs", + .argDescrip = "ACL", + }, + { + .longName = "modify", + .shortName = 'm', + .argInfo = POPT_ARG_STRING, + .arg = &the_acl, + .val = 'm', + .descrip = "Modify existing ACEs", + .argDescrip = "ACL", + }, + { + .longName = "add", + .shortName = 'a', + .argInfo = POPT_ARG_STRING, + .arg = &the_acl, + .val = 'a', + .descrip = "Add ACEs", + .argDescrip = "ACL", + }, + { + .longName = "replace", + .shortName = 'R', + .argInfo = POPT_ARG_STRING, + .arg = &the_acl, + .val = 'R', + .descrip = "Overwrite share permission ACL", + .argDescrip = "ACLS", + }, + { + .longName = "delete", + .shortName = 'D', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'D', + .descrip = "Delete the entire security descriptor", + }, + { + .longName = "setsddl", + .shortName = 'S', + .argInfo = POPT_ARG_STRING, + .arg = the_acl, + .val = 'S', + .descrip = "Set the SD in sddl format", + }, + { + .longName = "viewsddl", + .argInfo = POPT_ARG_NONE, + .arg = the_acl, + .val = OPT_VIEW_SDDL, + .descrip = "View the SD in sddl format", + }, + { + .longName = "view", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'v', + .descrip = "View current share permissions", + }, + { + .longName = "view-all", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_VIEW_ALL, + .descrip = "View all current share permissions", + }, + { + .longName = "machine-sid", + .shortName = 'M', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'M', + .descrip = "Initialize the machine SID", + }, + { + .longName = "force", + .shortName = 'F', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'F', + .descrip = "Force storing the ACL", + .argDescrip = "ACLS", + }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + if ( !(ctx = talloc_stackframe()) ) { + fprintf( stderr, "Failed to initialize talloc context!\n"); + return -1; + } + + smb_init_locale(); + + ok = samba_cmdline_init(ctx, + SAMBA_CMDLINE_CONFIG_NONE, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(ctx); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + /* set default debug level to 1 regardless of what smb.conf sets */ + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(ctx); + exit(1); + } + + poptSetOtherOptionHelp(pc, "sharename\n"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'r': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_DELETE; + break; + + case 'm': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_MODIFY; + break; + + case 'a': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_ADD; + break; + + case 'R': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_SET; + break; + + case 'D': + mode = SMB_SD_DELETE; + break; + + case 'S': + mode = SMB_SD_SETSDDL; + the_acl = smb_xstrdup(poptGetOptArg(pc)); + break; + + case OPT_VIEW_SDDL: + mode = SMB_SD_VIEWSDDL; + break; + + case 'v': + mode = SMB_ACL_VIEW; + break; + + case 'F': + force_acl = True; + break; + + case 'M': + initialize_sid = True; + break; + case OPT_VIEW_ALL: + mode = SMB_ACL_VIEW_ALL; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + setlinebuf(stdout); + + lp_load_with_registry_shares(get_dyn_CONFIGFILE()); + + /* check for initializing secrets.tdb first */ + + if ( initialize_sid ) { + struct dom_sid *sid = get_global_sam_sid(); + struct dom_sid_buf buf; + + if ( !sid ) { + fprintf( stderr, "Failed to retrieve Machine SID!\n"); + retval = 3; + goto done; + } + + printf ("%s\n", dom_sid_str_buf(sid, &buf) ); + retval = 0; + goto done; + } + + if ( mode == SMB_ACL_VIEW && force_acl ) { + fprintf( stderr, "Invalid combination of -F and -v\n"); + retval = -1; + goto done; + } + + if (mode == SMB_ACL_VIEW_ALL) { + int i; + + for (i=0; i<lp_numservices(); i++) { + TALLOC_CTX *frame = talloc_stackframe(); + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *service = lp_servicename(frame, lp_sub, i); + + if (service == NULL) { + continue; + } + + printf("[%s]\n", service); + change_share_sec(frame, service, NULL, SMB_ACL_VIEW); + printf("\n"); + TALLOC_FREE(frame); + } + goto done; + } + + /* get the sharename */ + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + retval = -1; + goto done; + } + + fstrcpy(sharename, poptGetArg(pc)); + + snum = lp_servicenumber( sharename ); + + if ( snum == -1 && !force_acl ) { + fprintf( stderr, "Invalid sharename: %s\n", sharename); + retval = -1; + goto done; + } + + switch (mode) { + case SMB_SD_SETSDDL: + retval = set_sharesec_sddl(sharename, the_acl); + break; + case SMB_SD_VIEWSDDL: + retval = view_sharesec_sddl(sharename); + break; + default: + retval = change_share_sec(ctx, sharename, the_acl, mode); + break; + } + +done: + gfree_all(); + poptFreeContext(pc); + talloc_destroy(ctx); + + return retval; +} diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c new file mode 100644 index 0000000..ff11ba4 --- /dev/null +++ b/source3/utils/smbcacls.c @@ -0,0 +1,2614 @@ +/* + Unix SMB/CIFS implementation. + ACL get/set utility + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Jelmer Vernooij 2003 + Copyright (C) Noel Power <noel.power@suse.com> 2013 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_lsa.h" +#include "rpc_client/cli_lsarpc.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "libsmb/clirap.h" +#include "passdb/machine_sid.h" +#include "../librpc/gen_ndr/ndr_lsa_c.h" +#include "util_sd.h" +#include "lib/param/param.h" + +static char DIRSEP_CHAR = '\\'; + +static int inheritance = 0; +static const char *save_file = NULL; +static const char *restore_file = NULL; +static int recurse; +static int test_args; +static int sddl; +static int query_sec_info = -1; +static int set_sec_info = -1; +static bool want_mxac; + +static const char *domain_sid = NULL; + +enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD }; +enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP, REQUEST_INHERIT}; +enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; + +struct cacl_callback_state { + struct cli_credentials *creds; + struct cli_state *cli; + struct security_descriptor *aclsd; + struct security_acl *acl_to_add; + enum acl_mode mode; + char *the_acl; + bool acl_no_propagate; + bool numeric; +}; + +static NTSTATUS cli_lsa_lookup_domain_sid(struct cli_state *cli, + struct dom_sid *sid) +{ + union lsa_PolicyInformation *info = NULL; + struct smbXcli_tcon *orig_tcon = NULL; + char *orig_share = NULL; + struct rpc_pipe_client *rpc_pipe = NULL; + struct policy_handle handle; + NTSTATUS status, result; + TALLOC_CTX *frame = talloc_stackframe(); + + if (cli_state_has_tcon(cli)) { + cli_state_save_tcon_share(cli, &orig_tcon, &orig_share); + } + + status = cli_tree_connect(cli, "IPC$", "?????", NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + + status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc, &rpc_pipe); + if (!NT_STATUS_IS_OK(status)) { + goto tdis; + } + + status = rpccli_lsa_open_policy(rpc_pipe, frame, True, + GENERIC_EXECUTE_ACCESS, &handle); + if (!NT_STATUS_IS_OK(status)) { + goto tdis; + } + + status = dcerpc_lsa_QueryInfoPolicy2(rpc_pipe->binding_handle, + frame, &handle, + LSA_POLICY_INFO_DOMAIN, + &info, &result); + + if (any_nt_status_not_ok(status, result, &status)) { + goto tdis; + } + + *sid = *info->domain.sid; + +tdis: + TALLOC_FREE(rpc_pipe); + cli_tdis(cli); +done: + cli_state_restore_tcon_share(cli, orig_tcon, orig_share); + TALLOC_FREE(frame); + return status; +} + +static struct dom_sid *get_domain_sid(struct cli_state *cli) +{ + NTSTATUS status; + struct dom_sid_buf buf; + + struct dom_sid *sid = talloc(talloc_tos(), struct dom_sid); + if (sid == NULL) { + DEBUG(0, ("Out of memory\n")); + return NULL; + } + + if (domain_sid) { + if (!dom_sid_parse(domain_sid, sid)) { + DEBUG(0,("failed to parse domain sid\n")); + TALLOC_FREE(sid); + } + } else { + status = cli_lsa_lookup_domain_sid(cli, sid); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("failed to lookup domain sid: %s\n", nt_errstr(status))); + TALLOC_FREE(sid); + } + + } + + DEBUG(2,("Domain SID: %s\n", dom_sid_str_buf(sid, &buf))); + return sid; +} + +/* add an ACE to a list of ACEs in a struct security_acl */ +static bool add_ace_with_ctx(TALLOC_CTX *ctx, struct security_acl **the_acl, + const struct security_ace *ace) + +{ + struct security_acl *acl = *the_acl; + + if (acl == NULL) { + acl = make_sec_acl(ctx, 3, 0, NULL); + if (acl == NULL) { + return false; + } + } + + if (acl->num_aces == UINT32_MAX) { + return false; + } + ADD_TO_ARRAY( + acl, struct security_ace, *ace, &acl->aces, &acl->num_aces); + *the_acl = acl; + return True; +} + +static bool add_ace(struct security_acl **the_acl, struct security_ace *ace) +{ + return add_ace_with_ctx(talloc_tos(), the_acl, ace); +} + +/* parse a ascii version of a security descriptor */ +static struct security_descriptor *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str) +{ + const char *p = str; + char *tok; + struct security_descriptor *ret = NULL; + size_t sd_size; + struct dom_sid owner_sid = { .num_auths = 0 }; + bool have_owner = false; + struct dom_sid group_sid = { .num_auths = 0 }; + bool have_group = false; + struct security_acl *dacl=NULL; + int revision=1; + + while (next_token_talloc(ctx, &p, &tok, "\t,\r\n")) { + if (strncmp(tok,"REVISION:", 9) == 0) { + revision = strtol(tok+9, NULL, 16); + continue; + } + + if (strncmp(tok,"OWNER:", 6) == 0) { + if (have_owner) { + printf("Only specify owner once\n"); + goto done; + } + if (!StringToSid(cli, &owner_sid, tok+6)) { + printf("Failed to parse owner sid\n"); + goto done; + } + have_owner = true; + continue; + } + + if (strncmp(tok,"GROUP:", 6) == 0) { + if (have_group) { + printf("Only specify group once\n"); + goto done; + } + if (!StringToSid(cli, &group_sid, tok+6)) { + printf("Failed to parse group sid\n"); + goto done; + } + have_group = true; + continue; + } + + if (strncmp(tok,"ACL:", 4) == 0) { + struct security_ace ace; + if (!parse_ace(cli, &ace, tok+4)) { + goto done; + } + if(!add_ace(&dacl, &ace)) { + printf("Failed to add ACL %s\n", tok); + goto done; + } + continue; + } + + printf("Failed to parse token '%s' in security descriptor,\n", tok); + goto done; + } + + ret = make_sec_desc( + ctx, + revision, + SEC_DESC_SELF_RELATIVE, + have_owner ? &owner_sid : NULL, + have_group ? &group_sid : NULL, + NULL, + dacl, + &sd_size); + +done: + return ret; +} + +/***************************************************** +get fileinfo for filename +*******************************************************/ +static uint16_t get_fileinfo(struct cli_state *cli, const char *filename) +{ + uint16_t fnum = (uint16_t)-1; + NTSTATUS status; + struct smb_create_returns cr = {0}; + + /* The desired access below is the only one I could find that works + with NT4, W2KP and Samba */ + + status = cli_ntcreate( + cli, /* cli */ + filename, /* fname */ + 0, /* CreatFlags */ + READ_CONTROL_ACCESS, /* CreatFlags */ + 0, /* FileAttributes */ + FILE_SHARE_READ| + FILE_SHARE_WRITE, /* ShareAccess */ + FILE_OPEN, /* CreateDisposition */ + 0x0, /* CreateOptions */ + 0x0, /* SecurityFlags */ + &fnum, /* pfid */ + &cr); /* cr */ + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open %s: %s\n", filename, nt_errstr(status)); + return 0; + } + + cli_close(cli, fnum); + return cr.file_attributes; +} + +/***************************************************** +get sec desc for filename +*******************************************************/ +static struct security_descriptor *get_secdesc_with_ctx(TALLOC_CTX *ctx, + struct cli_state *cli, + const char *filename) +{ + uint16_t fnum = (uint16_t)-1; + struct security_descriptor *sd; + NTSTATUS status; + uint32_t sec_info; + uint32_t desired_access = 0; + + if (query_sec_info == -1) { + sec_info = SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL; + } else { + sec_info = query_sec_info; + } + + if (sec_info & (SECINFO_OWNER | SECINFO_GROUP | SECINFO_DACL)) { + desired_access |= SEC_STD_READ_CONTROL; + } + if (sec_info & SECINFO_SACL) { + desired_access |= SEC_FLAG_SYSTEM_SECURITY; + } + + if (desired_access == 0) { + desired_access |= SEC_STD_READ_CONTROL; + } + + status = cli_ntcreate(cli, filename, 0, desired_access, + 0, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, 0x0, 0x0, &fnum, NULL); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open %s: %s\n", filename, nt_errstr(status)); + return NULL; + } + + status = cli_query_security_descriptor(cli, fnum, sec_info, + ctx, &sd); + + cli_close(cli, fnum); + + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to get security descriptor: %s\n", + nt_errstr(status)); + return NULL; + } + return sd; +} + +static struct security_descriptor *get_secdesc(struct cli_state *cli, + const char *filename) +{ + return get_secdesc_with_ctx(talloc_tos(), cli, filename); +} +/***************************************************** +set sec desc for filename +*******************************************************/ +static bool set_secdesc(struct cli_state *cli, const char *filename, + struct security_descriptor *sd) +{ + uint16_t fnum = (uint16_t)-1; + bool result=true; + NTSTATUS status; + uint32_t desired_access = 0; + uint32_t sec_info; + + if (set_sec_info == -1) { + sec_info = 0; + + if (sd->dacl || (sd->type & SEC_DESC_DACL_PRESENT)) { + sec_info |= SECINFO_DACL; + } + if (sd->sacl || (sd->type & SEC_DESC_SACL_PRESENT)) { + sec_info |= SECINFO_SACL; + } + if (sd->owner_sid) { + sec_info |= SECINFO_OWNER; + } + if (sd->group_sid) { + sec_info |= SECINFO_GROUP; + } + } else { + sec_info = set_sec_info; + } + + /* Make the desired_access more specific. */ + if (sec_info & SECINFO_DACL) { + desired_access |= SEC_STD_WRITE_DAC; + } + if (sec_info & SECINFO_SACL) { + desired_access |= SEC_FLAG_SYSTEM_SECURITY; + } + if (sec_info & (SECINFO_OWNER | SECINFO_GROUP)) { + desired_access |= SEC_STD_WRITE_OWNER; + } + + status = cli_ntcreate(cli, filename, 0, + desired_access, + 0, FILE_SHARE_READ|FILE_SHARE_WRITE, + FILE_OPEN, 0x0, 0x0, &fnum, NULL); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to open %s: %s\n", filename, nt_errstr(status)); + return false; + } + + status = cli_set_security_descriptor(cli, fnum, sec_info, sd); + if (!NT_STATUS_IS_OK(status)) { + printf("ERROR: security descriptor set failed: %s\n", + nt_errstr(status)); + result=false; + } + + cli_close(cli, fnum); + return result; +} + +/***************************************************** +get maximum access for a file +*******************************************************/ +static int cacl_mxac(struct cli_state *cli, const char *filename) +{ + NTSTATUS status; + uint32_t mxac; + + status = cli_query_mxac(cli, filename, &mxac); + if (!NT_STATUS_IS_OK(status)) { + printf("Failed to get mxac: %s\n", nt_errstr(status)); + return EXIT_FAILED; + } + + printf("Maximum access: 0x%x\n", mxac); + + return EXIT_OK; +} + + +/***************************************************** +dump the acls for a file +*******************************************************/ +static int cacl_dump(struct cli_state *cli, const char *filename, bool numeric) +{ + struct security_descriptor *sd; + int ret; + + if (test_args) { + return EXIT_OK; + } + + sd = get_secdesc(cli, filename); + if (sd == NULL) { + return EXIT_FAILED; + } + + if (sddl) { + char *str = sddl_encode(talloc_tos(), sd, get_domain_sid(cli)); + if (str == NULL) { + return EXIT_FAILED; + } + printf("%s\n", str); + TALLOC_FREE(str); + } else { + sec_desc_print(cli, stdout, sd, numeric); + } + + if (want_mxac) { + ret = cacl_mxac(cli, filename); + if (ret != EXIT_OK) { + return ret; + } + } + + return EXIT_OK; +} + +/***************************************************** +Change the ownership or group ownership of a file. Just +because the NT docs say this can't be done :-). JRA. +*******************************************************/ + +static int owner_set(struct cli_state *cli, enum chown_mode change_mode, + const char *filename, const char *new_username) +{ + struct dom_sid sid; + struct security_descriptor *sd; + size_t sd_size; + + if (!StringToSid(cli, &sid, new_username)) + return EXIT_PARSE_ERROR; + + sd = make_sec_desc(talloc_tos(), + SECURITY_DESCRIPTOR_REVISION_1, + SEC_DESC_SELF_RELATIVE, + (change_mode == REQUEST_CHOWN) ? &sid : NULL, + (change_mode == REQUEST_CHGRP) ? &sid : NULL, + NULL, NULL, &sd_size); + + if (!set_secdesc(cli, filename, sd)) { + return EXIT_FAILED; + } + + return EXIT_OK; +} + + +/* The MSDN is contradictory over the ordering of ACE entries in an + ACL. However NT4 gives a "The information may have been modified + by a computer running Windows NT 5.0" if denied ACEs do not appear + before allowed ACEs. At + http://technet.microsoft.com/en-us/library/cc781716.aspx the + canonical order is specified as "Explicit Deny, Explicit Allow, + Inherited ACEs unchanged" */ + +static int ace_compare(struct security_ace *ace1, struct security_ace *ace2) +{ + if (security_ace_equal(ace1, ace2)) + return 0; + + if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && + !(ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) + return 1; + if (!(ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && + (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) + return -1; + if ((ace1->flags & SEC_ACE_FLAG_INHERITED_ACE) && + (ace2->flags & SEC_ACE_FLAG_INHERITED_ACE)) + return ace1 - ace2; + + if (ace1->type != ace2->type) + return ace2->type - ace1->type; + + if (dom_sid_compare(&ace1->trustee, &ace2->trustee)) + return dom_sid_compare(&ace1->trustee, &ace2->trustee); + + if (ace1->flags != ace2->flags) + return ace1->flags - ace2->flags; + + if (ace1->access_mask != ace2->access_mask) + return ace1->access_mask - ace2->access_mask; + + if (ace1->size != ace2->size) + return ace1->size - ace2->size; + + return memcmp(ace1, ace2, sizeof(struct security_ace)); +} + +static void sort_acl(struct security_acl *the_acl) +{ + uint32_t i; + if (!the_acl) return; + + TYPESAFE_QSORT(the_acl->aces, the_acl->num_aces, ace_compare); + + for (i=1;i<the_acl->num_aces;) { + if (security_ace_equal(&the_acl->aces[i-1], + &the_acl->aces[i])) { + ARRAY_DEL_ELEMENT( + the_acl->aces, i, the_acl->num_aces); + the_acl->num_aces--; + } else { + i++; + } + } +} + +/***************************************************** +set the ACLs on a file given a security descriptor +*******************************************************/ + +static int cacl_set_from_sd(struct cli_state *cli, const char *filename, + struct security_descriptor *sd, enum acl_mode mode, + bool numeric) +{ + struct security_descriptor *old = NULL; + uint32_t i, j; + size_t sd_size; + int result = EXIT_OK; + + if (!sd) return EXIT_PARSE_ERROR; + if (test_args) return EXIT_OK; + + if (mode != SMB_ACL_SET) { + /* + * Do not fetch old ACL when it will be overwritten + * completely with a new one. + */ + old = get_secdesc(cli, filename); + + if (!old) { + return EXIT_FAILED; + } + } + + /* the logic here is rather more complex than I would like */ + switch (mode) { + case SMB_ACL_DELETE: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (security_ace_equal(&sd->dacl->aces[i], + &old->dacl->aces[j])) { + uint32_t k; + for (k=j; k<old->dacl->num_aces-1;k++) { + old->dacl->aces[k] = old->dacl->aces[k+1]; + } + old->dacl->num_aces--; + found = True; + break; + } + } + + if (!found) { + printf("ACL for ACE:"); + print_ace(cli, stdout, &sd->dacl->aces[i], + numeric); + printf(" not found\n"); + } + } + break; + + case SMB_ACL_MODIFY: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + bool found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (dom_sid_equal(&sd->dacl->aces[i].trustee, + &old->dacl->aces[j].trustee)) { + old->dacl->aces[j] = sd->dacl->aces[i]; + found = True; + } + } + + if (!found) { + fstring str; + + SidToString(cli, str, + &sd->dacl->aces[i].trustee, + numeric); + printf("ACL for SID %s not found\n", str); + } + } + + if (sd->owner_sid) { + old->owner_sid = sd->owner_sid; + } + + if (sd->group_sid) { + old->group_sid = sd->group_sid; + } + + break; + + case SMB_ACL_ADD: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + add_ace(&old->dacl, &sd->dacl->aces[i]); + } + break; + + case SMB_ACL_SET: + old = sd; + break; + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + /* Create new security descriptor and set it */ + + /* We used to just have "WRITE_DAC_ACCESS" without WRITE_OWNER. + But if we're sending an owner, even if it's the same as the one + that already exists then W2K3 insists we open with WRITE_OWNER access. + I need to check that setting a SD with no owner set works against WNT + and W2K. JRA. + */ + + sd = make_sec_desc(talloc_tos(),old->revision, old->type, + old->owner_sid, old->group_sid, + NULL, old->dacl, &sd_size); + + if (!set_secdesc(cli, filename, sd)) { + result = EXIT_FAILED; + } + + return result; +} + +/***************************************************** +set the ACLs on a file given an ascii description +*******************************************************/ + +static int cacl_set(struct cli_state *cli, const char *filename, + char *the_acl, enum acl_mode mode, bool numeric) +{ + struct security_descriptor *sd = NULL; + + if (sddl) { + const char *msg = NULL; + size_t msg_offset = 0; + enum ace_condition_flags flags = + ACE_CONDITION_FLAG_ALLOW_DEVICE; + sd = sddl_decode_err_msg(talloc_tos(), + the_acl, + get_domain_sid(cli), + flags, + &msg, + &msg_offset); + if (sd == NULL) { + DBG_ERR("could not decode '%s'\n", the_acl); + if (msg != NULL) { + DBG_ERR(" %*c\n", + (int)msg_offset, '^'); + DBG_ERR("error '%s'\n", msg); + } + } + } else { + sd = sec_desc_parse(talloc_tos(), cli, the_acl); + } + + if (sd == NULL) { + return EXIT_PARSE_ERROR; + } + if (test_args) { + return EXIT_OK; + } + return cacl_set_from_sd(cli, filename, sd, mode, numeric); +} + +/***************************************************** +set the inherit on a file +*******************************************************/ +static int inherit(struct cli_state *cli, const char *filename, + const char *type) +{ + struct security_descriptor *old,*sd; + uint32_t oldattr; + size_t sd_size; + int result = EXIT_OK; + + old = get_secdesc(cli, filename); + + if (!old) { + return EXIT_FAILED; + } + + oldattr = get_fileinfo(cli,filename); + + if (strcmp(type,"allow")==0) { + if ((old->type & SEC_DESC_DACL_PROTECTED) == + SEC_DESC_DACL_PROTECTED) { + uint32_t i; + char *parentname,*temp; + struct security_descriptor *parent; + temp = talloc_strdup(talloc_tos(), filename); + + old->type=old->type & (~SEC_DESC_DACL_PROTECTED); + + /* look at parent and copy in all its inheritable ACL's. */ + string_replace(temp, '\\', '/'); + if (!parent_dirname(talloc_tos(),temp,&parentname,NULL)) { + return EXIT_FAILED; + } + string_replace(parentname, '/', '\\'); + parent = get_secdesc(cli,parentname); + if (parent == NULL) { + return EXIT_FAILED; + } + for (i=0;i<parent->dacl->num_aces;i++) { + struct security_ace *ace=&parent->dacl->aces[i]; + /* Add inherited flag to all aces */ + ace->flags=ace->flags| + SEC_ACE_FLAG_INHERITED_ACE; + if ((oldattr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) { + if ((ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) == + SEC_ACE_FLAG_CONTAINER_INHERIT) { + add_ace(&old->dacl, ace); + } + } else { + if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) == + SEC_ACE_FLAG_OBJECT_INHERIT) { + /* clear flags for files */ + ace->flags=0; + add_ace(&old->dacl, ace); + } + } + } + } else { + printf("Already set to inheritable permissions.\n"); + return EXIT_FAILED; + } + } else if (strcmp(type,"remove")==0) { + if ((old->type & SEC_DESC_DACL_PROTECTED) != + SEC_DESC_DACL_PROTECTED) { + old->type=old->type | SEC_DESC_DACL_PROTECTED; + + /* remove all inherited ACL's. */ + if (old->dacl) { + int i; + struct security_acl *temp=old->dacl; + old->dacl=make_sec_acl(talloc_tos(), 3, 0, NULL); + for (i=temp->num_aces-1;i>=0;i--) { + struct security_ace *ace=&temp->aces[i]; + /* Remove all ace with INHERITED flag set */ + if ((ace->flags & SEC_ACE_FLAG_INHERITED_ACE) != + SEC_ACE_FLAG_INHERITED_ACE) { + add_ace(&old->dacl,ace); + } + } + } + } else { + printf("Already set to no inheritable permissions.\n"); + return EXIT_FAILED; + } + } else if (strcmp(type,"copy")==0) { + if ((old->type & SEC_DESC_DACL_PROTECTED) != + SEC_DESC_DACL_PROTECTED) { + old->type=old->type | SEC_DESC_DACL_PROTECTED; + + /* + * convert all inherited ACL's to non + * inherited ACL's. + */ + if (old->dacl) { + uint32_t i; + for (i=0;i<old->dacl->num_aces;i++) { + struct security_ace *ace=&old->dacl->aces[i]; + /* Remove INHERITED FLAG from all aces */ + ace->flags=ace->flags&(~SEC_ACE_FLAG_INHERITED_ACE); + } + } + } else { + printf("Already set to no inheritable permissions.\n"); + return EXIT_FAILED; + } + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + sd = make_sec_desc(talloc_tos(),old->revision, old->type, + old->owner_sid, old->group_sid, + NULL, old->dacl, &sd_size); + + if (!set_secdesc(cli, filename, sd)) { + result = EXIT_FAILED; + } + + return result; +} + +/***************************************************** + Return a connection to a server. +*******************************************************/ +static struct cli_state *connect_one(struct cli_credentials *creds, + const char *server, const char *share) +{ + struct cli_state *c = NULL; + NTSTATUS nt_status; + uint32_t flags = 0; + + nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server, + NULL, 0, + share, "?????", + creds, + flags); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); + return NULL; + } + + return c; +} + +/* + * Process resulting combination of mask & fname ensuring + * terminated with wildcard + */ +static char *build_dirname(TALLOC_CTX *ctx, + const char *mask, char *dir, char *fname) +{ + char *mask2 = NULL; + char *p = NULL; + + mask2 = talloc_strdup(ctx, mask); + if (!mask2) { + return NULL; + } + p = strrchr_m(mask2, DIRSEP_CHAR); + if (p) { + p[1] = 0; + } else { + mask2[0] = '\0'; + } + mask2 = talloc_asprintf_append(mask2, + "%s\\*", + fname); + return mask2; +} + +/* + * Returns a copy of the ACL flags in ace modified according + * to some inheritance rules. + * a) SEC_ACE_FLAG_INHERITED_ACE is propagated to children + * b) SEC_ACE_FLAG_INHERIT_ONLY is set on container children for OI (only) + * c) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are + * stripped from flags to be propagated to non-container children + * d) SEC_ACE_FLAG_OBJECT_INHERIT & SEC_ACE_FLAG_CONTAINER_INHERIT are + * stripped from flags to be propagated if the NP flag + * SEC_ACE_FLAG_NO_PROPAGATE_INHERIT is present + */ + +static uint8_t get_flags_to_propagate(bool is_container, + struct security_ace *ace) +{ + uint8_t newflags = ace->flags; + /* OBJECT inheritance */ + bool acl_objinherit = (ace->flags & + SEC_ACE_FLAG_OBJECT_INHERIT) == SEC_ACE_FLAG_OBJECT_INHERIT; + /* CONTAINER inheritance */ + bool acl_cntrinherit = (ace->flags & + SEC_ACE_FLAG_CONTAINER_INHERIT) == + SEC_ACE_FLAG_CONTAINER_INHERIT; + /* PROHIBIT inheritance */ + bool prohibit_inheritance = ((ace->flags & + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) == + SEC_ACE_FLAG_NO_PROPAGATE_INHERIT); + + /* Assume we are not propagating the ACE */ + + newflags &= ~SEC_ACE_FLAG_INHERITED_ACE; + /* all children need to have the SEC_ACE_FLAG_INHERITED_ACE set */ + if (acl_cntrinherit || acl_objinherit) { + /* + * object inherit ( alone ) on a container needs + * SEC_ACE_FLAG_INHERIT_ONLY + */ + if (is_container) { + if (acl_objinherit && !acl_cntrinherit) { + newflags |= SEC_ACE_FLAG_INHERIT_ONLY; + } + /* + * this is tricky, the only time we would not + * propagate the ace for a container is if + * prohibit_inheritance is set and object inheritance + * alone is set + */ + if ((prohibit_inheritance + && acl_objinherit + && !acl_cntrinherit) == false) { + newflags |= SEC_ACE_FLAG_INHERITED_ACE; + } + } else { + /* + * don't apply object/container inheritance flags to + * non dirs + */ + newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT + | SEC_ACE_FLAG_CONTAINER_INHERIT + | SEC_ACE_FLAG_INHERIT_ONLY); + /* + * only apply ace to file if object inherit + */ + if (acl_objinherit) { + newflags |= SEC_ACE_FLAG_INHERITED_ACE; + } + } + + /* if NP is specified strip NP and all OI/CI INHERIT flags */ + if (prohibit_inheritance) { + newflags &= ~(SEC_ACE_FLAG_OBJECT_INHERIT + | SEC_ACE_FLAG_CONTAINER_INHERIT + | SEC_ACE_FLAG_INHERIT_ONLY + | SEC_ACE_FLAG_NO_PROPAGATE_INHERIT); + } + } + return newflags; +} + +/* + * This function builds a new acl for 'caclfile', first it removes any + * existing inheritable ace(s) from the current acl of caclfile, secondly it + * applies any inheritable acls of the parent of caclfile ( inheritable acls of + * caclfile's parent are passed via acl_to_add member of cbstate ) + * + */ +static NTSTATUS propagate_inherited_aces(char *caclfile, + struct cacl_callback_state *cbstate) +{ + TALLOC_CTX *aclctx = NULL; + NTSTATUS status; + int result; + int fileattr; + struct security_descriptor *old = NULL; + bool is_container = false; + struct security_acl *acl_to_add = cbstate->acl_to_add; + struct security_acl *acl_to_remove = NULL; + uint32_t i, j; + + aclctx = talloc_new(NULL); + if (aclctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + old = get_secdesc_with_ctx(aclctx, cbstate->cli, caclfile); + + if (!old) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + /* inhibit propagation? */ + if ((old->type & SEC_DESC_DACL_PROTECTED) == + SEC_DESC_DACL_PROTECTED){ + status = NT_STATUS_OK; + goto out; + } + + fileattr = get_fileinfo(cbstate->cli, caclfile); + is_container = (fileattr & FILE_ATTRIBUTE_DIRECTORY); + + /* find acl(s) that are inherited */ + for (j = 0; old->dacl && j < old->dacl->num_aces; j++) { + + if (old->dacl->aces[j].flags & SEC_ACE_FLAG_INHERITED_ACE) { + if (!add_ace_with_ctx(aclctx, &acl_to_remove, + &old->dacl->aces[j])) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + } + } + + /* remove any acl(s) that are inherited */ + if (acl_to_remove) { + for (i = 0; i < acl_to_remove->num_aces; i++) { + struct security_ace ace = acl_to_remove->aces[i]; + for (j = 0; old->dacl && j < old->dacl->num_aces; j++) { + + if (security_ace_equal(&ace, + &old->dacl->aces[j])) { + uint32_t k; + for (k = j; k < old->dacl->num_aces-1; + k++) { + old->dacl->aces[k] = + old->dacl->aces[k+1]; + } + old->dacl->num_aces--; + break; + } + } + } + } + /* propagate any inheritable ace to be added */ + if (acl_to_add) { + for (i = 0; i < acl_to_add->num_aces; i++) { + struct security_ace ace = acl_to_add->aces[i]; + bool is_objectinherit = (ace.flags & + SEC_ACE_FLAG_OBJECT_INHERIT) == + SEC_ACE_FLAG_OBJECT_INHERIT; + bool is_inherited; + /* don't propagate flags to a file unless OI */ + if (!is_objectinherit && !is_container) { + continue; + } + /* + * adjust flags according to inheritance + * rules + */ + ace.flags = get_flags_to_propagate(is_container, &ace); + is_inherited = (ace.flags & + SEC_ACE_FLAG_INHERITED_ACE) == + SEC_ACE_FLAG_INHERITED_ACE; + /* don't propagate non inherited flags */ + if (!is_inherited) { + continue; + } + if (!add_ace_with_ctx(aclctx, &old->dacl, &ace)) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + } + } + + result = cacl_set_from_sd(cbstate->cli, caclfile, + old, + SMB_ACL_SET, cbstate->numeric); + if (result != EXIT_OK) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + status = NT_STATUS_OK; +out: + TALLOC_FREE(aclctx); + return status; +} + +/* + * Returns true if 'ace' contains SEC_ACE_FLAG_OBJECT_INHERIT or + * SEC_ACE_FLAG_CONTAINER_INHERIT + */ +static bool is_inheritable_ace(struct security_ace *ace) +{ + uint8_t flags = ace->flags; + if (flags & (SEC_ACE_FLAG_OBJECT_INHERIT + | SEC_ACE_FLAG_CONTAINER_INHERIT)) { + return true; + } + return false; +} + +/* This method does some basic sanity checking with respect to automatic + * inheritance. e.g. it checks if it is possible to do a set, it detects illegal + * attempts to set inherited permissions directly. Additionally this method + * does some basic initialisation for instance it parses the ACL passed on the + * command line. + */ +static NTSTATUS prepare_inheritance_propagation(TALLOC_CTX *ctx, char *filename, + struct cacl_callback_state *cbstate) +{ + NTSTATUS result; + char *the_acl = cbstate->the_acl; + struct cli_state *cli = cbstate->cli; + enum acl_mode mode = cbstate->mode; + struct security_descriptor *sd = NULL; + struct security_descriptor *old = NULL; + uint32_t j; + bool propagate = false; + + old = get_secdesc_with_ctx(ctx, cli, filename); + if (old == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* parse acl passed on the command line */ + if (sddl) { + const char *msg = NULL; + size_t msg_offset = 0; + enum ace_condition_flags flags = + ACE_CONDITION_FLAG_ALLOW_DEVICE; + + cbstate->aclsd = sddl_decode_err_msg(ctx, + the_acl, + get_domain_sid(cli), + flags, + &msg, + &msg_offset); + if (cbstate->aclsd == NULL) { + DBG_ERR("could not decode '%s'\n", the_acl); + if (msg != NULL) { + DBG_ERR(" %*c\n", + (int)msg_offset, '^'); + DBG_ERR("error '%s'\n", msg); + } + } + } else { + cbstate->aclsd = sec_desc_parse(ctx, cli, the_acl); + } + + if (!cbstate->aclsd) { + result = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + sd = cbstate->aclsd; + + /* set operation if inheritance is enabled doesn't make sense */ + if (mode == SMB_ACL_SET && ((old->type & SEC_DESC_DACL_PROTECTED) != + SEC_DESC_DACL_PROTECTED)){ + d_printf("Inheritance enabled at %s, can't apply set operation\n",filename); + result = NT_STATUS_UNSUCCESSFUL; + goto out; + + } + + /* + * search command line acl for any illegal SEC_ACE_FLAG_INHERITED_ACE + * flags that are set + */ + for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) { + struct security_ace *ace = &sd->dacl->aces[j]; + if (ace->flags & SEC_ACE_FLAG_INHERITED_ACE) { + d_printf("Illegal parameter %s\n", the_acl); + result = NT_STATUS_UNSUCCESSFUL; + goto out; + } + if (!propagate) { + if (is_inheritable_ace(ace)) { + propagate = true; + } + } + } + + result = NT_STATUS_OK; +out: + cbstate->acl_no_propagate = !propagate; + return result; +} + +/* + * This method builds inheritable ace(s) from filename (which should be + * a container) that need propagating to children in order to provide + * automatic inheritance. Those inheritable ace(s) are stored in + * acl_to_add member of cbstate for later processing + * (see propagate_inherited_aces) + */ +static NTSTATUS get_inheritable_aces(TALLOC_CTX *ctx, char *filename, + struct cacl_callback_state *cbstate) +{ + NTSTATUS result; + struct cli_state *cli = NULL; + struct security_descriptor *sd = NULL; + struct security_acl *acl_to_add = NULL; + uint32_t j; + + cli = cbstate->cli; + sd = get_secdesc_with_ctx(ctx, cli, filename); + + if (sd == NULL) { + return NT_STATUS_NO_MEMORY; + } + + /* + * Check if any inheritance related flags are used, if not then + * nothing to do. At the same time populate acls for inheritance + * related ace(s) that need to be added to or deleted from children as + * a result of inheritance propagation. + */ + + for (j = 0; sd->dacl && j < sd->dacl->num_aces; j++) { + struct security_ace *ace = &sd->dacl->aces[j]; + if (is_inheritable_ace(ace)) { + bool added = add_ace_with_ctx(ctx, &acl_to_add, ace); + if (!added) { + result = NT_STATUS_NO_MEMORY; + goto out; + } + } + } + cbstate->acl_to_add = acl_to_add; + result = NT_STATUS_OK; +out: + return result; +} + +/* + * Callback handler to handle child elements processed by cli_list, we attempt + * to propagate inheritable ace(s) to each child via the function + * propagate_inherited_aces. Children that are themselves directories are passed + * to cli_list again ( to descend the directory structure ) + */ +static NTSTATUS cacl_set_cb(struct file_info *f, + const char *mask, void *state) +{ + struct cacl_callback_state *cbstate = + (struct cacl_callback_state *)state; + struct cli_state *cli = NULL; + struct cli_credentials *creds = NULL; + + TALLOC_CTX *dirctx = NULL; + NTSTATUS status; + struct cli_state *targetcli = NULL; + + char *dir = NULL; + char *dir_end = NULL; + char *mask2 = NULL; + char *targetpath = NULL; + char *caclfile = NULL; + + dirctx = talloc_new(NULL); + if (!dirctx) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + cli = cbstate->cli; + creds = cbstate->creds; + + /* Work out the directory. */ + dir = talloc_strdup(dirctx, mask); + if (!dir) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + dir_end = strrchr(dir, DIRSEP_CHAR); + if (dir_end != NULL) { + *dir_end = '\0'; + } + + if (!f->name || !f->name[0]) { + d_printf("Empty dir name returned. Possible server misconfiguration.\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + if (f->attr & FILE_ATTRIBUTE_DIRECTORY) { + struct cacl_callback_state dir_cbstate; + uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_HIDDEN; + dir_end = NULL; + + /* ignore special '.' & '..' */ + if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) { + status = NT_STATUS_OK; + goto out; + } + + mask2 = build_dirname(dirctx, mask, dir, f->name); + if (mask2 == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + /* check for dfs */ + status = cli_resolve_path(dirctx, "", creds, cli, + mask2, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* + * prepare path to caclfile, remove any existing wildcard + * chars and convert path separators. + */ + + caclfile = talloc_strdup(dirctx, targetpath); + if (!caclfile) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + dir_end = strrchr(caclfile, '*'); + if (dir_end != NULL) { + *dir_end = '\0'; + } + + string_replace(caclfile, '/', '\\'); + /* + * make directory specific copy of cbstate here + * (for this directory level) to be available as + * the parent cbstate for the children of this directory. + * Note: cbstate is overwritten for the current file being + * processed. + */ + dir_cbstate = *cbstate; + dir_cbstate.cli = targetcli; + + /* + * propagate any inherited ace from our parent + */ + status = propagate_inherited_aces(caclfile, &dir_cbstate); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* + * get inheritable ace(s) for this dir/container + * that will be propagated to its children + */ + status = get_inheritable_aces(dirctx, caclfile, + &dir_cbstate); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + /* + * ensure cacl_set_cb gets called for children + * of this directory (targetpath) + */ + status = cli_list(targetcli, targetpath, + attribute, cacl_set_cb, + (void *)&dir_cbstate); + + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + } else { + /* + * build full path to caclfile and replace '/' with '\' so + * other utility functions can deal with it + */ + + targetpath = talloc_asprintf(dirctx, "%s/%s", dir, f->name); + if (!targetpath) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + string_replace(targetpath, '/', '\\'); + + /* attempt to propagate any inherited ace to file caclfile */ + status = propagate_inherited_aces(targetpath, cbstate); + + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + } + status = NT_STATUS_OK; +out: + if (!NT_STATUS_IS_OK(status)) { + d_printf("error %s: processing %s\n", + nt_errstr(status), + targetpath); + } + TALLOC_FREE(dirctx); + return status; +} + + +/* + * Wrapper around cl_list to descend the directory tree pointed to by 'filename', + * helper callback function 'cacl_set_cb' handles the child elements processed + * by cli_list. + */ +static int inheritance_cacl_set(char *filename, + struct cacl_callback_state *cbstate) +{ + int result; + NTSTATUS ntstatus; + int fileattr; + char *mask = NULL; + struct cli_state *cli = cbstate->cli; + TALLOC_CTX *ctx = NULL; + bool isdirectory = false; + uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_HIDDEN; + ctx = talloc_init("inherit_set"); + if (ctx == NULL) { + d_printf("out of memory\n"); + result = EXIT_FAILED; + goto out; + } + + /* ensure we have a filename that starts with '\' */ + if (!filename || *filename != DIRSEP_CHAR) { + /* illegal or no filename */ + result = EXIT_FAILED; + d_printf("illegal or missing name '%s'\n", filename); + goto out; + } + + + fileattr = get_fileinfo(cli, filename); + isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY; + + /* + * if we've got as far as here then we have already evaluated + * the args. + */ + if (test_args) { + result = EXIT_OK; + goto out; + } + + mask = NULL; + /* make sure we have a trailing '\*' for directory */ + if (!isdirectory) { + mask = talloc_strdup(ctx, filename); + } else if (strlen(filename) > 1) { + /* + * if the passed file name doesn't have a trailing '\' + * append it. + */ + char *name_end = strrchr(filename, DIRSEP_CHAR); + if (name_end != filename + strlen(filename) + 1) { + mask = talloc_asprintf(ctx, "%s\\*", filename); + } else { + mask = talloc_strdup(ctx, filename); + } + } else { + /* filename is a single '\', just append '*' */ + mask = talloc_asprintf_append(mask, "%s*", filename); + } + + if (!mask) { + result = EXIT_FAILED; + goto out; + } + + /* + * prepare for automatic propagation of the acl passed on the + * cmdline. + */ + + ntstatus = prepare_inheritance_propagation(ctx, filename, + cbstate); + if (!NT_STATUS_IS_OK(ntstatus)) { + d_printf("error: %s processing %s\n", + nt_errstr(ntstatus), filename); + result = EXIT_FAILED; + goto out; + } + + result = cacl_set_from_sd(cli, filename, cbstate->aclsd, + cbstate->mode, cbstate->numeric); + + /* + * strictly speaking it could be considered an error if a file was + * specified with '--propagate-inheritance'. However we really want + * to eventually get rid of '--propagate-inheritance' so we will be + * more forgiving here and instead just exit early. + */ + if (!isdirectory || (result != EXIT_OK)) { + goto out; + } + + /* check if there is actually any need to propagate */ + if (cbstate->acl_no_propagate) { + goto out; + } + /* get inheritable attributes this parent container (e.g. filename) */ + ntstatus = get_inheritable_aces(ctx, filename, cbstate); + if (NT_STATUS_IS_OK(ntstatus)) { + /* process children */ + ntstatus = cli_list(cli, mask, attribute, + cacl_set_cb, + (void *)cbstate); + } + + if (!NT_STATUS_IS_OK(ntstatus)) { + d_printf("error: %s processing %s\n", + nt_errstr(ntstatus), filename); + result = EXIT_FAILED; + goto out; + } + +out: + TALLOC_FREE(ctx); + return result; +} + +struct diritem { + struct diritem *prev, *next; + /* + * dirname and targetpath below are sanitized, + * e.g. + * + start and end with '\' + * + have no trailing '*' + * + all '/' have been converted to '\' + */ + char *dirname; + char *targetpath; + struct cli_state *targetcli; +}; + +struct save_restore_stats +{ + int success; + int failure; +}; + +struct dump_context { + struct diritem *list; + struct cli_credentials *creds; + struct cli_state *cli; + struct save_restore_stats *stats; + int save_fd; + struct diritem *dir; + NTSTATUS status; +}; + +static int write_dacl(struct dump_context *ctx, + struct cli_state *cli, + const char *filename, + const char *origfname) +{ + struct security_descriptor *sd = NULL; + char *str = NULL; + const char *output_fmt = "%s\r\n%s\r\n"; + const char *tmp = NULL; + char *out_str = NULL; + uint8_t *dest = NULL; + ssize_t s_len; + size_t d_len; + bool ok; + int result; + TALLOC_CTX *frame = talloc_stackframe(); + + if (test_args) { + return EXIT_OK; + } + + if (ctx->save_fd < 0) { + DBG_ERR("error processing %s no file descriptor\n", filename); + result = EXIT_FAILED; + goto out; + } + + sd = get_secdesc(cli, filename); + if (sd == NULL) { + result = EXIT_FAILED; + goto out; + } + + sd->owner_sid = NULL; + sd->group_sid = NULL; + + str = sddl_encode(frame, sd, get_domain_sid(cli)); + if (str == NULL) { + DBG_ERR("error processing %s couldn't encode DACL\n", filename); + result = EXIT_FAILED; + goto out; + } + /* + * format of icacls save file is + * a line containing the path of the file/dir + * followed by a line containing the sddl format + * of the dacl. + * The format of the strings are null terminated + * 16-bit Unicode. Each line is terminated by "\r\n" + */ + + tmp = origfname; + /* skip leading '\' */ + if (tmp[0] == '\\') { + tmp++; + } + out_str = talloc_asprintf(frame, output_fmt, tmp, str); + + if (out_str == NULL) { + result = EXIT_FAILED; + goto out; + } + + s_len = strlen(out_str); + + ok = convert_string_talloc(out_str, + CH_UNIX, + CH_UTF16, + out_str, + s_len, (void **)(void *)&dest, &d_len); + if (!ok) { + DBG_ERR("error processing %s out of memory\n", tmp); + result = EXIT_FAILED; + goto out; + } + + if (write(ctx->save_fd, dest, d_len) != d_len) { + DBG_ERR("error processing %s failed to write to file.\n", tmp); + result = EXIT_FAILED; + goto out; + } + fsync(ctx->save_fd); + + result = EXIT_OK; + ctx->stats->success += 1; + fprintf(stdout, "Successfully processed file: %s\n", tmp); +out: + TALLOC_FREE(frame); + if (result != EXIT_OK) { + ctx->stats->failure += 1; + } + return result; +} + +/* + * Sanitize directory name. + * Given a directory name 'dir' ensure it; + * o starts with '\' + * o ends with '\' + * o doesn't end with trailing '*' + * o ensure all '/' are converted to '\' + */ + +static char *sanitize_dirname(TALLOC_CTX *ctx, + const char *dir) +{ + char *mask = NULL; + char *name_end = NULL; + + mask = talloc_strdup(ctx, dir); + name_end = strrchr(mask, '*'); + if (name_end) { + *name_end = '\0'; + } + + name_end = strrchr(mask, DIRSEP_CHAR); + + if (strlen(mask) > 0 && name_end != mask + (strlen(mask) - 1)) { + mask = talloc_asprintf(ctx, "%s\\", mask); + } + + string_replace(mask, '/', '\\'); + return mask; +} + +/* + * Process each entry (child) of a directory. + * Each entry, regardless of whether it is itself a file or directory + * has it's dacl written to the restore/save file. + * Each directory is saved to context->list (for further processing) + * write_dacl will update the stats (success/fail) + */ +static NTSTATUS cacl_dump_dacl_cb(struct file_info *f, + const char *mask, void *state) +{ + struct dump_context *ctx = talloc_get_type_abort(state, + struct dump_context); + + NTSTATUS status; + + char *mask2 = NULL; + char *targetpath = NULL; + char *unresolved = NULL; + + /* + * if we have already encountered an error + * bail out + */ + if (!NT_STATUS_IS_OK(ctx->status)) { + return ctx->status; + } + + if (!f->name || !f->name[0]) { + DBG_ERR("Empty dir name returned. Possible server " + "misconfiguration.\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + mask2 = sanitize_dirname(ctx, mask); + if (!mask2) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + if (f->attr & FILE_ATTRIBUTE_DIRECTORY) { + struct diritem *item = NULL; + + /* ignore special '.' & '..' */ + if ((f->name == NULL) || ISDOT(f->name) || ISDOTDOT(f->name)) { + status = NT_STATUS_OK; + goto out; + } + + /* Work out the directory. */ + unresolved = sanitize_dirname(ctx, ctx->dir->dirname); + if (!unresolved) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name); + + if (unresolved == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + item = talloc_zero(ctx, struct diritem); + if (item == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + item->dirname = unresolved; + + mask2 = talloc_asprintf(ctx, "%s%s", mask2, f->name); + if (!mask2) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + status = cli_resolve_path(ctx, "", ctx->creds, ctx->cli, + mask2, &item->targetcli, &targetpath); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("error failed to resolve: %s\n", + nt_errstr(status)); + goto out; + } + + item->targetpath = sanitize_dirname(ctx, targetpath); + if (!item->targetpath) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + if (write_dacl(ctx, + item->targetcli, + item->targetpath, unresolved) != EXIT_OK) { + status = NT_STATUS_UNSUCCESSFUL; + /* + * cli_list happily ignores error encountered + * when processing the callback so we need + * to save any error status encountered while + * processing directories (so we can stop recursing + * those as soon as possible). + * Changing the current behaviour of the callback + * handling by cli_list would be I think be too + * risky. + */ + ctx->status = status; + goto out; + } + + DLIST_ADD_END(ctx->list, item); + + } else { + unresolved = sanitize_dirname(ctx, ctx->dir->dirname); + if (!unresolved) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + unresolved = talloc_asprintf(ctx, "%s%s", unresolved, f->name); + + if (!unresolved) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + /* + * build full path to the file and replace '/' with '\' so + * other utility functions can deal with it + */ + + targetpath = talloc_asprintf(ctx, "%s%s", mask2, f->name); + + if (!targetpath) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + if (write_dacl(ctx, + ctx->dir->targetcli, + targetpath, unresolved) != EXIT_OK) { + status = NT_STATUS_UNSUCCESSFUL; + /* + * cli_list happily ignores error encountered + * when processing the callback so we need + * to save any error status encountered while + * processing directories (so we can stop recursing + * those as soon as possible). + * Changing the current behaviour of the callback + * handling by cli_list would be I think be too + * risky. + */ + ctx->status = status; + goto out; + } + } + status = NT_STATUS_OK; +out: + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("error %s: processing %s\n", + nt_errstr(status), targetpath); + } + return status; +} + +/* + * dump_ctx contains a list of directories to be processed + * + each directory 'dir' is scanned by cli_list, the cli_list + * callback 'cacl_dump_dacl_cb' writes out the dacl of each + * child of 'dir' (regardless of whether it is a dir or file) + * to the restore/save file. Additionally any directories encountered + * are returned in the passed in dump_ctx->list member + * + the directory list returned from cli_list is passed and processed + * by recursively calling dump_dacl_dirtree + * + */ +static int dump_dacl_dirtree(struct dump_context *dump_ctx) +{ + struct diritem *item = NULL; + struct dump_context *new_dump_ctx = NULL; + int result; + for (item = dump_ctx->list; item; item = item->next) { + uint16_t attribute = FILE_ATTRIBUTE_DIRECTORY + | FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN; + NTSTATUS status; + char *mask = NULL; + + new_dump_ctx = talloc_zero(dump_ctx, struct dump_context); + + if (new_dump_ctx == NULL) { + DBG_ERR("out of memory\n"); + result = EXIT_FAILED; + goto out; + } + + if (item->targetcli == NULL) { + status = cli_resolve_path(new_dump_ctx, + "", + dump_ctx->creds, + dump_ctx->cli, + item->dirname, + &item->targetcli, + &item->targetpath); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to resolve path %s " + "error: %s\n", + item->dirname, nt_errstr(status)); + result = EXIT_FAILED; + goto out; + } + } + new_dump_ctx->creds = dump_ctx->creds; + new_dump_ctx->save_fd = dump_ctx->save_fd; + new_dump_ctx->stats = dump_ctx->stats; + new_dump_ctx->dir = item; + new_dump_ctx->cli = item->targetcli; + + mask = talloc_asprintf(new_dump_ctx, "%s*", + new_dump_ctx->dir->targetpath); + status = cli_list(new_dump_ctx->dir->targetcli, + mask, + attribute, cacl_dump_dacl_cb, new_dump_ctx); + + if (!NT_STATUS_IS_OK(status) || + !NT_STATUS_IS_OK(new_dump_ctx->status)) { + NTSTATUS tmpstatus; + if (!NT_STATUS_IS_OK(status)) { + /* + * cli_list failed for some reason + * so we need to update the failure stat + */ + new_dump_ctx->stats->failure += 1; + tmpstatus = status; + } else { + /* cacl_dump_dacl_cb should have updated stat */ + tmpstatus = new_dump_ctx->status; + } + DBG_ERR("error %s: processing %s\n", + nt_errstr(tmpstatus), item->dirname); + result = EXIT_FAILED; + goto out; + } + result = dump_dacl_dirtree(new_dump_ctx); + if (result != EXIT_OK) { + goto out; + } + } + + result = EXIT_OK; +out: + TALLOC_FREE(new_dump_ctx); + return result; +} + +static int cacl_dump_dacl(struct cli_state *cli, + struct cli_credentials *creds, + char *filename) +{ + int fileattr; + char *mask = NULL; + TALLOC_CTX *ctx = NULL; + bool isdirectory = false; + int result; + struct dump_context *dump_ctx = NULL; + struct save_restore_stats stats = {0}; + struct diritem *item = NULL; + struct cli_state *targetcli = NULL; + char *targetpath = NULL; + NTSTATUS status; + + ctx = talloc_init("cacl_dump"); + if (ctx == NULL) { + DBG_ERR("out of memory\n"); + result = EXIT_FAILED; + goto out; + } + + dump_ctx = talloc_zero(ctx, struct dump_context); + if (dump_ctx == NULL) { + DBG_ERR("out of memory\n"); + result = EXIT_FAILED; + goto out; + } + + dump_ctx->save_fd = open(save_file, + O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); + + if (dump_ctx->save_fd < 0) { + result = EXIT_FAILED; + goto out; + } + + dump_ctx->creds = creds; + dump_ctx->cli = cli; + dump_ctx->stats = &stats; + + /* ensure we have a filename that starts with '\' */ + if (!filename || *filename != DIRSEP_CHAR) { + /* illegal or no filename */ + result = EXIT_FAILED; + DBG_ERR("illegal or missing name '%s'\n", filename); + goto out; + } + + status = cli_resolve_path(dump_ctx, "", + dump_ctx->creds, + dump_ctx->cli, + filename, &targetcli, &targetpath); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed resolve %s\n", filename); + result = EXIT_FAILED; + goto out; + } + + fileattr = get_fileinfo(targetcli, targetpath); + isdirectory = (fileattr & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY; + + /* + * if we've got as far as here then we have already evaluated + * the args. + */ + if (test_args) { + result = EXIT_OK; + goto out; + } + + mask = NULL; + /* make sure we have a trailing '\*' for directory */ + if (!isdirectory) { + mask = talloc_strdup(ctx, filename); + } else if (strlen(filename) > 1) { + mask = sanitize_dirname(ctx, filename); + } else { + /* filename is a single '\' */ + mask = talloc_strdup(ctx, filename); + } + if (!mask) { + result = EXIT_FAILED; + goto out; + } + + write_dacl(dump_ctx, targetcli, targetpath, filename); + if (isdirectory && recurse) { + item = talloc_zero(dump_ctx, struct diritem); + if (!item) { + result = EXIT_FAILED; + goto out; + } + item->dirname = mask; + DLIST_ADD_END(dump_ctx->list, item); + dump_dacl_dirtree(dump_ctx); + } + + fprintf(stdout, "Successfully processed %d files: " + "Failed processing %d files\n", + dump_ctx->stats->success, dump_ctx->stats->failure); + result = EXIT_OK; +out: + if (dump_ctx && dump_ctx->save_fd > 0) { + close(dump_ctx->save_fd); + } + TALLOC_FREE(ctx); + return result; +} + +struct restore_dacl { + const char *path; + struct security_descriptor *sd; +}; + +/* + * Restore dacls from 'savefile' produced by + * 'icacls name /save' or 'smbcacls --save' + */ +static int cacl_restore(struct cli_state *cli, + struct cli_credentials *creds, + bool numeric, const char *restorefile) +{ + int restore_fd; + int result; + struct save_restore_stats stats = { 0 }; + + char **lines = NULL; + char *content = NULL; + char *convert_content = NULL; + size_t content_size; + struct restore_dacl *entries = NULL; + int numlines, i = 0; + bool ok; + struct dom_sid *sid = NULL; + + if (restorefile == NULL) { + DBG_ERR("No restore file specified\n"); + result = EXIT_FAILED; + goto out; + } + + if (test_args) { + result = EXIT_OK; + goto out; + } + + restore_fd = open(restorefile, O_RDONLY, S_IRUSR | S_IWUSR); + if (restore_fd < 0) { + DBG_ERR("Failed to open %s.\n", restorefile); + result = EXIT_FAILED; + goto out; + } + + content = fd_load(restore_fd, &content_size, 0, talloc_tos()); + + close(restore_fd); + + if (content == NULL) { + DBG_ERR("Failed to load content from %s.\n", restorefile); + result = EXIT_FAILED; + goto out; + } + + ok = convert_string_talloc(talloc_tos(), + CH_UTF16, + CH_UNIX, + content, + utf16_len_n(content, content_size), + (void **)(void *)&convert_content, + &content_size); + + TALLOC_FREE(content); + + if (!ok) { + DBG_ERR("Failed to convert content from %s " + "to CH_UNIX.\n", restorefile); + result = EXIT_FAILED; + goto out; + } + + lines = file_lines_parse(convert_content, + content_size, &numlines, talloc_tos()); + + if (lines == NULL) { + DBG_ERR("Failed to parse lines from content of %s.", + restorefile); + result = EXIT_FAILED; + goto out; + } + + entries = talloc_zero_array(lines, struct restore_dacl, numlines / 2); + + if (entries == NULL) { + DBG_ERR("error processing %s, out of memory\n", restorefile); + result = EXIT_FAILED; + goto out; + } + + sid = get_domain_sid(cli); + + while (i < numlines) { + int index = i / 2; + int first_line = (i % 2) == 0; + + if (first_line) { + char *tmp = NULL; + tmp = lines[i]; + /* line can be blank if root of share */ + if (strlen(tmp) == 0) { + entries[index].path = talloc_strdup(lines, + "\\"); + } else { + entries[index].path = lines[i]; + } + } else { + const char *msg = NULL; + size_t msg_offset = 0; + enum ace_condition_flags flags = + ACE_CONDITION_FLAG_ALLOW_DEVICE; + entries[index].sd = sddl_decode_err_msg(lines, + lines[i], + sid, + flags, + &msg, + &msg_offset); + if(entries[index].sd == NULL) { + DBG_ERR("could not decode '%s'\n", lines[i]); + if (msg != NULL) { + DBG_ERR(" %*c\n", + (int)msg_offset, '^'); + DBG_ERR("error '%s'\n", msg); + } + result = EXIT_FAILED; + goto out; + } + entries[index].sd->type |= + SEC_DESC_DACL_AUTO_INHERIT_REQ; + entries[index].sd->type |= SEC_DESC_SACL_AUTO_INHERITED; + } + i++; + } + for (i = 0; i < (numlines / 2); i++) { + int mode = SMB_ACL_SET; + int set_result; + struct cli_state *targetcli = NULL; + char *targetpath = NULL; + NTSTATUS status; + + /* check for dfs */ + status = cli_resolve_path(talloc_tos(), + "", + creds, + cli, + entries[i].path, + &targetcli, &targetpath); + + if (!NT_STATUS_IS_OK(status)) { + printf("Error failed to process file: %s\n", + entries[i].path); + stats.failure += 1; + continue; + } + + set_result = cacl_set_from_sd(targetcli, + targetpath, + entries[i].sd, mode, numeric); + + if (set_result == EXIT_OK) { + printf("Successfully processed file: %s\n", + entries[i].path); + stats.success += 1; + } else { + printf("Error failed to process file: %s\n", + entries[i].path); + stats.failure += 1; + } + } + + result = EXIT_OK; +out: + TALLOC_FREE(lines); + fprintf(stdout, "Successfully processed %d files: " + "Failed processing %d files\n", stats.success, stats.failure); + return result; +} + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc, char *argv[]) +{ + const char **argv_const = discard_const_p(const char *, argv); + char *share; + int opt; + enum acl_mode mode = SMB_ACL_SET; + static char *the_acl = NULL; + enum chown_mode change_mode = REQUEST_NONE; + int result; + char *path; + char *filename = NULL; + poptContext pc; + /* numeric is set when the user wants numeric SIDs and ACEs rather + than going via LSA calls to resolve them */ + int numeric = 0; + struct cli_state *targetcli = NULL; + struct cli_credentials *creds = NULL; + char *targetfile = NULL; + NTSTATUS status; + bool ok; + struct loadparm_context *lp_ctx = NULL; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "delete", + .shortName = 'D', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'D', + .descrip = "Delete an acl", + .argDescrip = "ACL", + }, + { + .longName = "modify", + .shortName = 'M', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'M', + .descrip = "Modify an acl", + .argDescrip = "ACL", + }, + { + .longName = "add", + .shortName = 'a', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'a', + .descrip = "Add an acl", + .argDescrip = "ACL", + }, + { + .longName = "set", + .shortName = 'S', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'S', + .descrip = "Set acls", + .argDescrip = "ACLS", + }, + { + .longName = "chown", + .shortName = 'C', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'C', + .descrip = "Change ownership of a file", + .argDescrip = "USERNAME", + }, + { + .longName = "chgrp", + .shortName = 'G', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'G', + .descrip = "Change group ownership of a file", + .argDescrip = "GROUPNAME", + }, + { + .longName = "inherit", + .shortName = 'I', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'I', + .descrip = "Inherit allow|remove|copy", + }, + { + .longName = "propagate-inheritance", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &inheritance, + .val = 1, + .descrip = "Supports propagation of inheritable ACE(s) when used in conjunction with add, delete, set or modify", + }, + { + .longName = "save", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &save_file, + .val = 1, + .descrip = "stores the DACLs in sddl format of the " + "specified file or folder for later use " + "with restore. SACLS, owner or integrity" + " labels are not stored", + }, + { + .longName = "restore", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &restore_file, + .val = 1, + .descrip = "applies the stored DACLS to files in " + "directory.", + }, + { + .longName = "recurse", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &recurse, + .val = 1, + .descrip = "indicates the operation is performed " + "on directory and all files/directories" + " below. (only applies to save option)", + }, + { + .longName = "numeric", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &numeric, + .val = 1, + .descrip = "Don't resolve sids or masks to names", + }, + { + .longName = "sddl", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &sddl, + .val = 1, + .descrip = "Output and input acls in sddl format", + }, + { + .longName = "query-security-info", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &query_sec_info, + .val = 1, + .descrip = "The security-info flags for queries" + }, + { + .longName = "set-security-info", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &set_sec_info, + .val = 1, + .descrip = "The security-info flags for modifications" + }, + { + .longName = "test-args", + .shortName = 't', + .argInfo = POPT_ARG_NONE, + .arg = &test_args, + .val = 1, + .descrip = "Test arguments" + }, + { + .longName = "domain-sid", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &domain_sid, + .val = 0, + .descrip = "Domain SID for sddl", + .argDescrip = "SID"}, + { + .longName = "maximum-access", + .shortName = 'x', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'x', + .descrip = "Query maximum permissions", + }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_LEGACY_S3 + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + struct cli_state *cli; + TALLOC_CTX *frame = talloc_stackframe(); + const char *owner_username = ""; + char *server; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + /* set default debug level to 1 regardless of what smb.conf sets */ + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + setlinebuf(stdout); + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "//server1/share1 filename\nACLs look like: " + "'ACL:user:[ALLOWED|DENIED]/flags/permissions'"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'S': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_SET; + break; + + case 'D': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_DELETE; + break; + + case 'M': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_MODIFY; + break; + + case 'a': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_ADD; + break; + + case 'C': + owner_username = poptGetOptArg(pc); + change_mode = REQUEST_CHOWN; + break; + + case 'G': + owner_username = poptGetOptArg(pc); + change_mode = REQUEST_CHGRP; + break; + + case 'I': + owner_username = poptGetOptArg(pc); + change_mode = REQUEST_INHERIT; + break; + case 'm': + lpcfg_set_cmdline(lp_ctx, "client max protocol", poptGetOptArg(pc)); + break; + case 'x': + want_mxac = true; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + if (inheritance && !the_acl) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + path = talloc_strdup(frame, poptGetArg(pc)); + if (!path) { + return -1; + } + + if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) { + printf("Invalid argument: %s\n", path); + return -1; + } + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + filename = talloc_strdup(frame, poptGetArg(pc)); + if (!filename) { + return -1; + } + + poptFreeContext(pc); + samba_cmdline_burn(argc, argv); + + string_replace(path,'/','\\'); + + server = talloc_strdup(frame, path+2); + if (!server) { + return -1; + } + share = strchr_m(server,'\\'); + if (share == NULL) { + printf("Invalid argument\n"); + return -1; + } + + *share = 0; + share++; + + creds = samba_cmdline_get_creds(); + + /* Make connection to server */ + if (!test_args) { + cli = connect_one(creds, server, share); + if (!cli) { + exit(EXIT_FAILED); + } + } else { + exit(0); + } + + string_replace(filename, '/', '\\'); + if (filename[0] != '\\') { + filename = talloc_asprintf(frame, + "\\%s", + filename); + if (!filename) { + return -1; + } + } + + status = cli_resolve_path(frame, + "", + creds, + cli, + filename, + &targetcli, + &targetfile); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("cli_resolve_path failed for %s! (%s)\n", filename, nt_errstr(status))); + return -1; + } + + /* Perform requested action */ + + if (change_mode == REQUEST_INHERIT) { + result = inherit(targetcli, targetfile, owner_username); + } else if (change_mode != REQUEST_NONE) { + result = owner_set(targetcli, change_mode, targetfile, owner_username); + } else if (the_acl) { + if (inheritance) { + struct cacl_callback_state cbstate = { + .creds = creds, + .cli = targetcli, + .mode = mode, + .the_acl = the_acl, + .numeric = numeric, + }; + result = inheritance_cacl_set(targetfile, &cbstate); + } else { + result = cacl_set(targetcli, + targetfile, + the_acl, + mode, + numeric); + } + } else { + if (save_file || restore_file) { + sddl = 1; + if (save_file) { + result = cacl_dump_dacl(cli, creds, filename); + } else { + result = cacl_restore(targetcli, + creds, + numeric, restore_file); + } + } else { + result = cacl_dump(targetcli, targetfile, numeric); + } + } + + gfree_all(); + TALLOC_FREE(frame); + + return result; +} diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c new file mode 100644 index 0000000..b318f55 --- /dev/null +++ b/source3/utils/smbcontrol.c @@ -0,0 +1,1870 @@ +/* + Unix SMB/CIFS implementation. + + Send messages to other Samba daemons + + Copyright (C) Tim Potter 2003 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Martin Pool 2001-2002 + Copyright (C) Simo Sorce 2002 + Copyright (C) James Peach 2006 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/util/server_id.h" +#include "lib/cmdline/cmdline.h" +#include "librpc/gen_ndr/spoolss.h" +#include "nt_printing.h" +#include "printing/notify.h" +#include "libsmb/nmblib.h" +#include "messages.h" +#include "util_tdb.h" +#include "../lib/util/pidfile.h" +#include "serverid.h" +#include "lib/util/server_id_db.h" +#include "cmdline_contexts.h" +#include "lib/util/string_wrappers.h" +#include "lib/global_contexts.h" +#include "lib/param/param.h" + +#ifdef HAVE_LIBUNWIND_H +#include <libunwind.h> +#endif + +#ifdef HAVE_LIBUNWIND_PTRACE_H +#include <libunwind-ptrace.h> +#endif + +#ifdef HAVE_SYS_PTRACE_H +#include <sys/ptrace.h> +#endif + +/* Default timeout value when waiting for replies (in seconds) */ + +#define DEFAULT_TIMEOUT 10 + +static int timeout = DEFAULT_TIMEOUT; +static int num_replies; /* Used by message callback fns */ + +/* Send a message to a destination pid. Zero means broadcast smbd. */ + +static bool send_message(struct messaging_context *msg_ctx, + struct server_id pid, int msg_type, + const void *buf, int len) +{ + if (procid_to_pid(&pid) != 0) + return NT_STATUS_IS_OK( + messaging_send_buf(msg_ctx, pid, msg_type, + (const uint8_t *)buf, len)); + + messaging_send_all(msg_ctx, msg_type, buf, len); + + return true; +} + +static void smbcontrol_timeout(struct tevent_context *event_ctx, + struct tevent_timer *te, + struct timeval now, + void *private_data) +{ + bool *timed_out = (bool *)private_data; + TALLOC_FREE(te); + *timed_out = True; +} + +/* Wait for one or more reply messages */ + +static void wait_replies(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + bool multiple_replies) +{ + struct tevent_timer *te; + bool timed_out = False; + + te = tevent_add_timer(ev_ctx, NULL, + timeval_current_ofs(timeout, 0), + smbcontrol_timeout, (void *)&timed_out); + if (te == NULL) { + DEBUG(0, ("tevent_add_timer failed\n")); + return; + } + + while (!timed_out) { + int ret; + if (num_replies > 0 && !multiple_replies) + break; + ret = tevent_loop_once(ev_ctx); + if (ret != 0) { + break; + } + } +} + +/* Message handler callback that displays the PID and a string on stdout */ + +static void print_pid_string_cb(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + struct server_id_buf pidstr; + + printf("PID %s: %.*s", server_id_str_buf(pid, &pidstr), + (int)data->length, (const char *)data->data); + num_replies++; +} + +/* Send no message. Useful for testing. */ + +static bool do_noop(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> noop\n"); + return False; + } + + /* Move along, nothing to see here */ + + return True; +} + +/* Send a debug string */ + +static bool do_debug(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> debug " + "<debug-string>\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_DEBUG, argv[1], + strlen(argv[1]) + 1); +} + + +static bool do_idmap(struct tevent_context *ev, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + static const char* usage = "Usage: " + "smbcontrol <dest> idmap <cmd> [arg]\n" + "\tcmd:" + "\tdelete \"UID <uid>\"|\"GID <gid>\"|<sid>\n" + "\t\tkill \"UID <uid>\"|\"GID <gid>\"|<sid>\n"; + const char* arg = NULL; + int arglen = 0; + int msg_type; + + switch (argc) { + case 2: + break; + case 3: + arg = argv[2]; + arglen = strlen(arg) + 1; + break; + default: + fprintf(stderr, "%s", usage); + return false; + } + + if (strcmp(argv[1], "delete") == 0) { + msg_type = ID_CACHE_DELETE; + } + else if (strcmp(argv[1], "kill") == 0) { + msg_type = ID_CACHE_KILL; + } + else if (strcmp(argv[1], "help") == 0) { + fprintf(stdout, "%s", usage); + return true; + } + else { + fprintf(stderr, "%s", usage); + return false; + } + + return send_message(msg_ctx, pid, msg_type, arg, arglen); +} + + +#if defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) + +/* Return the name of a process given it's PID. This will only work on Linux, + * but that's probably moot since this whole stack tracing implementation is + * Linux-specific anyway. + */ +static const char * procname(pid_t pid, char * buf, size_t bufsz) +{ + char path[64]; + FILE * fp; + + snprintf(path, sizeof(path), "/proc/%llu/cmdline", + (unsigned long long)pid); + if ((fp = fopen(path, "r")) == NULL) { + return NULL; + } + + fgets(buf, bufsz, fp); + + fclose(fp); + return buf; +} + +static void print_stack_trace(pid_t pid, int * count) +{ + void * pinfo = NULL; + unw_addr_space_t aspace = NULL; + unw_cursor_t cursor; + unw_word_t ip, sp; + + char nbuf[256]; + unw_word_t off; + + int ret; + + if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) { + fprintf(stderr, + "Failed to attach to process %llu: %s\n", + (unsigned long long)pid, strerror(errno)); + return; + } + + /* Wait until the attach is complete. */ + waitpid(pid, NULL, 0); + + if (((pinfo = _UPT_create(pid)) == NULL) || + ((aspace = unw_create_addr_space(&_UPT_accessors, 0)) == NULL)) { + /* Probably out of memory. */ + fprintf(stderr, + "Unable to initialize stack unwind for process %llu\n", + (unsigned long long)pid); + goto cleanup; + } + + if ((ret = unw_init_remote(&cursor, aspace, pinfo))) { + fprintf(stderr, + "Unable to unwind stack for process %llu: %s\n", + (unsigned long long)pid, unw_strerror(ret)); + goto cleanup; + } + + if (*count > 0) { + printf("\n"); + } + + if (procname(pid, nbuf, sizeof(nbuf))) { + printf("Stack trace for process %llu (%s):\n", + (unsigned long long)pid, nbuf); + } else { + printf("Stack trace for process %llu:\n", + (unsigned long long)pid); + } + + while (unw_step(&cursor) > 0) { + ip = sp = off = 0; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + unw_get_reg(&cursor, UNW_REG_SP, &sp); + + ret = unw_get_proc_name(&cursor, nbuf, sizeof(nbuf), &off); + if (ret != 0 && ret != -UNW_ENOMEM) { + snprintf(nbuf, sizeof(nbuf), "<unknown symbol>"); + } + printf(" %s + %#llx [ip=%#llx] [sp=%#llx]\n", + nbuf, (long long)off, (long long)ip, + (long long)sp); + } + + (*count)++; + +cleanup: + if (aspace) { + unw_destroy_addr_space(aspace); + } + + if (pinfo) { + _UPT_destroy(pinfo); + } + + ptrace(PTRACE_DETACH, pid, NULL, NULL); +} + +static int stack_trace_server(pid_t pid, void *priv) +{ + print_stack_trace(pid, (int *)priv); + return 0; +} + +static bool do_daemon_stack_trace(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + pid_t dest; + int count = 0; + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> stacktrace\n"); + return False; + } + + dest = procid_to_pid(&pid); + + if (dest != 0) { + /* It would be nice to be able to make sure that this PID is + * the PID of a smbd/winbind/nmbd process, not some random PID + * the user liked the look of. It doesn't seem like it's worth + * the effort at the moment, however. + */ + print_stack_trace(dest, &count); + } else { + messaging_dgm_forall(stack_trace_server, &count); + } + + return True; +} + +#else /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */ + +static bool do_daemon_stack_trace(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + fprintf(stderr, + "Daemon stack tracing is not supported on this platform\n"); + return False; +} + +#endif /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */ + +/* Inject a fault (fatal signal) into a running smbd */ + +static bool do_inject_fault(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> inject " + "<bus|hup|term|internal|segv>\n"); + return False; + } + +#if !defined(DEVELOPER) && !defined(ENABLE_SELFTEST) + fprintf(stderr, "Fault injection is only available in " + "developer and self test builds\n"); + return False; +#else /* DEVELOPER || ENABLE_SELFTEST */ + { + int sig = 0; + + if (strcmp(argv[1], "bus") == 0) { + sig = SIGBUS; + } else if (strcmp(argv[1], "hup") == 0) { + sig = SIGHUP; + } else if (strcmp(argv[1], "term") == 0) { + sig = SIGTERM; + } else if (strcmp(argv[1], "segv") == 0) { + sig = SIGSEGV; + } else if (strcmp(argv[1], "internal") == 0) { + /* Force an internal error, ie. an unclean exit. */ + sig = -1; + } else { + fprintf(stderr, "Unknown signal name '%s'\n", argv[1]); + return False; + } + + return send_message(msg_ctx, pid, MSG_SMB_INJECT_FAULT, + &sig, sizeof(int)); + } +#endif /* DEVELOPER || ENABLE_SELFTEST */ +} + +static bool do_sleep(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ +#if defined(DEVELOPER) || defined(ENABLE_SELFTEST) + unsigned int seconds; + long input; + const long MAX_SLEEP = 60 * 60; /* One hour maximum sleep */ +#endif + + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> sleep seconds\n"); + return False; + } + +#if !defined(DEVELOPER) && !defined(ENABLE_SELFTEST) + fprintf(stderr, "Sleep is only available in " + "developer and self test builds\n"); + return False; +#else /* DEVELOPER || ENABLE_SELFTEST */ + + input = atol(argv[1]); + if (input < 1 || input > MAX_SLEEP) { + fprintf(stderr, + "Invalid duration for sleep '%s'\n" + "It should be at least 1 second and no more than %ld\n", + argv[1], + MAX_SLEEP); + return False; + } + seconds = input; + return send_message(msg_ctx, pid, + MSG_SMB_SLEEP, + &seconds, + sizeof(unsigned int)); +#endif /* DEVELOPER || ENABLE_SELFTEST */ +} + +/* Force a browser election */ + +static bool do_election(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> force-election\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_FORCE_ELECTION, NULL, 0); +} + +/* Ping a samba daemon process */ + +static void pong_cb(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + struct server_id_buf src_string; + printf("PONG from pid %s\n", server_id_str_buf(pid, &src_string)); + num_replies++; +} + +static bool do_ping(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> ping\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(msg_ctx, pid, MSG_PING, NULL, 0)) + return False; + + messaging_register(msg_ctx, NULL, MSG_PONG, pong_cb); + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + messaging_deregister(msg_ctx, MSG_PONG, NULL); + + return num_replies; +} + +/* Set profiling options */ + +static bool do_profile(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + int v; + + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> profile " + "<off|count|on|flush>\n"); + return False; + } + + if (strcmp(argv[1], "off") == 0) { + v = 0; + } else if (strcmp(argv[1], "count") == 0) { + v = 1; + } else if (strcmp(argv[1], "on") == 0) { + v = 2; + } else if (strcmp(argv[1], "flush") == 0) { + v = 3; + } else { + fprintf(stderr, "Unknown profile command '%s'\n", argv[1]); + return False; + } + + return send_message(msg_ctx, pid, MSG_PROFILE, &v, sizeof(int)); +} + +/* Return the profiling level */ + +static void profilelevel_cb(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + int level; + const char *s; + + num_replies++; + + if (data->length != sizeof(int)) { + fprintf(stderr, "invalid message length %ld returned\n", + (unsigned long)data->length); + return; + } + + memcpy(&level, data->data, sizeof(int)); + + switch (level) { + case 0: + s = "not enabled"; + break; + case 1: + s = "off"; + break; + case 3: + s = "count only"; + break; + case 7: + s = "count and time"; + break; + default: + s = "BOGUS"; + break; + } + + printf("Profiling %s on pid %u\n",s,(unsigned int)procid_to_pid(&pid)); +} + +static void profilelevel_rqst(struct messaging_context *msg_ctx, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + int v = 0; + + /* Send back a dummy reply */ + + send_message(msg_ctx, pid, MSG_PROFILELEVEL, &v, sizeof(int)); +} + +static bool do_profilelevel(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> profilelevel\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(msg_ctx, pid, MSG_REQ_PROFILELEVEL, NULL, 0)) + return False; + + messaging_register(msg_ctx, NULL, MSG_PROFILELEVEL, profilelevel_cb); + messaging_register(msg_ctx, NULL, MSG_REQ_PROFILELEVEL, + profilelevel_rqst); + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + messaging_deregister(msg_ctx, MSG_PROFILE, NULL); + + return num_replies; +} + +/* Display debug level settings */ + +static bool do_debuglevel(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> debuglevel\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(msg_ctx, pid, MSG_REQ_DEBUGLEVEL, NULL, 0)) + return False; + + messaging_register(msg_ctx, NULL, MSG_DEBUGLEVEL, print_pid_string_cb); + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + messaging_deregister(msg_ctx, MSG_DEBUGLEVEL, NULL); + + return num_replies; +} + +/* Send a print notify message */ + +static bool do_printnotify(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + const char *cmd; + + /* Check for subcommand */ + + if (argc == 1) { + fprintf(stderr, "Must specify subcommand:\n"); + fprintf(stderr, "\tqueuepause <printername>\n"); + fprintf(stderr, "\tqueueresume <printername>\n"); + fprintf(stderr, "\tjobpause <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobresume <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n"); + fprintf(stderr, "\tprinter <printername> <comment|port|" + "driver> <value>\n"); + + return False; + } + + cmd = argv[1]; + + if (strcmp(cmd, "queuepause") == 0) { + + if (argc != 3) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " queuepause <printername>\n"); + return False; + } + + notify_printer_status_byname(ev_ctx, msg_ctx, argv[2], + PRINTER_STATUS_PAUSED); + + goto send; + + } else if (strcmp(cmd, "queueresume") == 0) { + + if (argc != 3) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " queuereume <printername>\n"); + return False; + } + + notify_printer_status_byname(ev_ctx, msg_ctx, argv[2], + PRINTER_STATUS_OK); + + goto send; + + } else if (strcmp(cmd, "jobpause") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + ev_ctx, msg_ctx, + argv[2], jobid, JOB_STATUS_PAUSED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "jobresume") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + ev_ctx, msg_ctx, + argv[2], jobid, JOB_STATUS_QUEUED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "jobdelete") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + ev_ctx, msg_ctx, + argv[2], jobid, JOB_STATUS_DELETING, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + notify_job_status_byname( + ev_ctx, msg_ctx, + argv[2], jobid, JOB_STATUS_DELETING| + JOB_STATUS_DELETED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "printer") == 0) { + uint32_t attribute; + + if (argc != 5) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify " + "printer <printername> <comment|port|driver> " + "<value>\n"); + return False; + } + + if (strcmp(argv[3], "comment") == 0) { + attribute = PRINTER_NOTIFY_FIELD_COMMENT; + } else if (strcmp(argv[3], "port") == 0) { + attribute = PRINTER_NOTIFY_FIELD_PORT_NAME; + } else if (strcmp(argv[3], "driver") == 0) { + attribute = PRINTER_NOTIFY_FIELD_DRIVER_NAME; + } else { + fprintf(stderr, "Invalid printer command '%s'\n", + argv[3]); + return False; + } + + notify_printer_byname(ev_ctx, msg_ctx, argv[2], attribute, + discard_const_p(char, argv[4])); + + goto send; + } + + fprintf(stderr, "Invalid subcommand '%s'\n", cmd); + return False; + +send: + print_notify_send_messages(msg_ctx, 0); + return True; +} + +/* Close a share */ + +static bool do_closeshare(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> close-share " + "<sharename>\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_SMB_FORCE_TDIS, argv[1], + strlen(argv[1]) + 1); +} + +/* + * Close a share if access denied by now + **/ + +static bool do_close_denied_share( + struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> close-denied-share " + "<sharename>\n"); + return False; + } + + return send_message( + msg_ctx, + pid, + MSG_SMB_FORCE_TDIS_DENIED, + argv[1], + strlen(argv[1]) + 1); +} + +/* Kill a client by IP address */ +static bool do_kill_client_by_ip(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> kill-client-ip " + "<IP address>\n"); + return false; + } + + if (!is_ipaddress_v4(argv[1]) && !is_ipaddress_v6(argv[1])) { + fprintf(stderr, "%s is not a valid IP address!\n", argv[1]); + return false; + } + + return send_message(msg_ctx, pid, MSG_SMB_KILL_CLIENT_IP, + argv[1], strlen(argv[1]) + 1); +} + +/* Tell winbindd an IP got dropped */ + +static bool do_ip_dropped(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> ip-dropped " + "<ip-address>\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_WINBIND_IP_DROPPED, argv[1], + strlen(argv[1]) + 1); +} + +/* Display talloc pool usage */ + +static bool do_poolusage(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id dst, + const int argc, const char **argv) +{ + pid_t pid = procid_to_pid(&dst); + int stdout_fd = 1; + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n"); + return False; + } + + if (pid == 0) { + fprintf(stderr, "Can only send to a specific PID\n"); + return false; + } + + messaging_send_iov( + msg_ctx, + dst, + MSG_REQ_POOL_USAGE, + NULL, + 0, + &stdout_fd, + 1); + + return true; +} + +static bool do_rpc_dump_status( + struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id dst, + const int argc, + const char **argv) +{ + pid_t pid = procid_to_pid(&dst); + int stdout_fd = 1; + + if (argc != 1) { + fprintf(stderr, + "Usage: smbcontrol <dest> rpc-dump-status\n"); + return False; + } + + if (pid == 0) { + fprintf(stderr, "Can only send to a specific PID\n"); + return false; + } + + messaging_send_iov( + msg_ctx, + dst, + MSG_RPC_DUMP_STATUS, + NULL, + 0, + &stdout_fd, + 1); + + return true; +} + +/* Fetch and print the ringbuf log */ + +static void print_ringbuf_log_cb(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + printf("%s", (const char *)data->data); + num_replies++; +} + +static bool do_ringbuflog(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> ringbuf-log\n"); + return false; + } + + messaging_register(msg_ctx, NULL, MSG_RINGBUF_LOG, + print_ringbuf_log_cb); + + /* Send a message and register our interest in a reply */ + + if (!send_message(msg_ctx, pid, MSG_REQ_RINGBUF_LOG, NULL, 0)) { + return false; + } + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) { + printf("No replies received\n"); + } + + messaging_deregister(msg_ctx, MSG_RINGBUF_LOG, NULL); + + return num_replies != 0; +} + +/* Perform a dmalloc mark */ + +static bool do_dmalloc_mark(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> dmalloc-mark\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_REQ_DMALLOC_MARK, NULL, 0); +} + +/* Perform a dmalloc changed */ + +static bool do_dmalloc_changed(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> " + "dmalloc-log-changed\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_REQ_DMALLOC_LOG_CHANGED, + NULL, 0); +} + +static void print_uint32_cb(struct messaging_context *msg, void *private_data, + uint32_t msg_type, struct server_id pid, + DATA_BLOB *data) +{ + uint32_t num_children; + + if (data->length != sizeof(uint32_t)) { + printf("Invalid response: %d bytes long\n", + (int)data->length); + goto done; + } + num_children = IVAL(data->data, 0); + printf("%u children\n", (unsigned)num_children); +done: + num_replies++; +} + +static bool do_num_children(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> num-children\n"); + return False; + } + + messaging_register(msg_ctx, NULL, MSG_SMB_NUM_CHILDREN, + print_uint32_cb); + + /* Send a message and register our interest in a reply */ + + if (!send_message(msg_ctx, pid, MSG_SMB_TELL_NUM_CHILDREN, NULL, 0)) + return false; + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + messaging_deregister(msg_ctx, MSG_SMB_NUM_CHILDREN, NULL); + + return num_replies; +} + +static bool do_msg_cleanup(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + int ret; + + ret = messaging_cleanup(msg_ctx, pid.pid); + + printf("cleanup(%u) returned %s\n", (unsigned)pid.pid, + ret ? strerror(ret) : "ok"); + + return (ret == 0); +} + +/* Shutdown a server process */ + +static bool do_shutdown(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> shutdown\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_SHUTDOWN, NULL, 0); +} + +/* Notify a driver upgrade */ + +static bool do_drvupgrade(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> drvupgrade " + "<driver-name>\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_PRINTER_DRVUPGRADE, argv[1], + strlen(argv[1]) + 1); +} + +static bool do_winbind_online(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + TDB_CONTEXT *tdb; + char *db_path; + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol winbindd online\n"); + return False; + } + + db_path = state_path(talloc_tos(), "winbindd_cache.tdb"); + if (db_path == NULL) { + return false; + } + + /* Remove the entry in the winbindd_cache tdb to tell a later + starting winbindd that we're online. */ + + tdb = tdb_open_log(db_path, 0, TDB_DEFAULT, O_RDWR, 0600); + if (!tdb) { + fprintf(stderr, "Cannot open the tdb %s for writing.\n", + db_path); + TALLOC_FREE(db_path); + return False; + } + + TALLOC_FREE(db_path); + tdb_delete_bystring(tdb, "WINBINDD_OFFLINE"); + tdb_close(tdb); + + return send_message(msg_ctx, pid, MSG_WINBIND_ONLINE, NULL, 0); +} + +static bool do_winbind_offline(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + TDB_CONTEXT *tdb; + bool ret = False; + int retry = 0; + char *db_path; + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol winbindd offline\n"); + return False; + } + + db_path = state_path(talloc_tos(), "winbindd_cache.tdb"); + if (db_path == NULL) { + return false; + } + + /* Create an entry in the winbindd_cache tdb to tell a later + starting winbindd that we're offline. We may actually create + it here... */ + + tdb = tdb_open_log(db_path, + WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE, + TDB_DEFAULT|TDB_INCOMPATIBLE_HASH /* TDB_CLEAR_IF_FIRST */, + O_RDWR|O_CREAT, 0600); + + if (!tdb) { + fprintf(stderr, "Cannot open the tdb %s for writing.\n", + db_path); + TALLOC_FREE(db_path); + return False; + } + TALLOC_FREE(db_path); + + /* There's a potential race condition that if a child + winbindd detects a domain is online at the same time + we're trying to tell it to go offline that it might + delete the record we add between us adding it and + sending the message. Minimize this by retrying up to + 5 times. */ + + for (retry = 0; retry < 5; retry++) { + uint8_t buf[4]; + TDB_DATA d = { .dptr = buf, .dsize = sizeof(buf) }; + + SIVAL(buf, 0, time(NULL)); + + tdb_store_bystring(tdb, "WINBINDD_OFFLINE", d, TDB_INSERT); + + ret = send_message(msg_ctx, pid, MSG_WINBIND_OFFLINE, + NULL, 0); + + /* Check that the entry "WINBINDD_OFFLINE" still exists. */ + d = tdb_fetch_bystring( tdb, "WINBINDD_OFFLINE" ); + if (d.dptr != NULL && d.dsize == 4) { + SAFE_FREE(d.dptr); + break; + } + + SAFE_FREE(d.dptr); + DEBUG(10,("do_winbind_offline: offline state not set - retrying.\n")); + } + + tdb_close(tdb); + return ret; +} + +static bool do_winbind_onlinestatus(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol winbindd onlinestatus\n"); + return False; + } + + messaging_register(msg_ctx, NULL, MSG_WINBIND_ONLINESTATUS, + print_pid_string_cb); + + if (!send_message(msg_ctx, pid, MSG_WINBIND_ONLINESTATUS, NULL, 0)) { + return False; + } + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + messaging_deregister(msg_ctx, MSG_WINBIND_ONLINESTATUS, NULL); + + return num_replies; +} + +static bool do_winbind_dump_domain_list(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + const char *domain = NULL; + int domain_len = 0; + + if (argc < 1 || argc > 2) { + fprintf(stderr, "Usage: smbcontrol <dest> dump-domain-list " + "<domain>\n"); + return false; + } + + if (argc == 2) { + domain = argv[1]; + domain_len = strlen(argv[1]) + 1; + } + + messaging_register(msg_ctx, NULL, MSG_WINBIND_DUMP_DOMAIN_LIST, + print_pid_string_cb); + + if (!send_message(msg_ctx, pid, MSG_WINBIND_DUMP_DOMAIN_LIST, + domain, domain_len)) + { + return false; + } + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) { + printf("No replies received\n"); + } + + messaging_deregister(msg_ctx, MSG_WINBIND_DUMP_DOMAIN_LIST, NULL); + + return num_replies; +} + +static bool do_msg_disconnect_dc(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> disconnect-dc\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_WINBIND_DISCONNECT_DC, NULL, 0); +} + +static void winbind_validate_cache_cb(struct messaging_context *msg, + void *private_data, + uint32_t msg_type, + struct server_id pid, + DATA_BLOB *data) +{ + struct server_id_buf src_string; + printf("Winbindd cache is %svalid. (answer from pid %s)\n", + (*(data->data) == 0 ? "" : "NOT "), + server_id_str_buf(pid, &src_string)); + num_replies++; +} + +static bool do_winbind_validate_cache(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + struct server_id myid; + + myid = messaging_server_id(msg_ctx); + + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol winbindd validate-cache\n"); + return False; + } + + messaging_register(msg_ctx, NULL, MSG_WINBIND_VALIDATE_CACHE, + winbind_validate_cache_cb); + + if (!send_message(msg_ctx, pid, MSG_WINBIND_VALIDATE_CACHE, &myid, + sizeof(myid))) { + return False; + } + + wait_replies(ev_ctx, msg_ctx, procid_to_pid(&pid) == 0); + + if (num_replies == 0) { + printf("No replies received\n"); + } + + messaging_deregister(msg_ctx, MSG_WINBIND_VALIDATE_CACHE, NULL); + + return num_replies; +} + +static bool do_reload_certs(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol ldap_server reload-certs \n"); + return false; + } + + return send_message(msg_ctx, pid, MSG_RELOAD_TLS_CERTIFICATES, NULL, 0); +} +static bool do_reload_config(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> reload-config\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_SMB_CONF_UPDATED, NULL, 0); +} + +static bool do_reload_printers(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> reload-printers\n"); + return False; + } + + return send_message(msg_ctx, pid, MSG_PRINTER_PCAP, NULL, 0); +} + +static void my_make_nmb_name( struct nmb_name *n, const char *name, int type) +{ + fstring unix_name; + memset( (char *)n, '\0', sizeof(struct nmb_name) ); + fstrcpy(unix_name, name); + (void)strupper_m(unix_name); + push_ascii(n->name, unix_name, sizeof(n->name), STR_TERMINATE); + n->name_type = (unsigned int)type & 0xFF; + push_ascii(n->scope, lp_netbios_scope(), 64, STR_TERMINATE); +} + +static bool do_nodestatus(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + struct packet_struct p; + + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol nmbd nodestatus <ip>\n"); + return False; + } + + ZERO_STRUCT(p); + + p.ip = interpret_addr2(argv[1]); + p.port = 137; + p.packet_type = NMB_PACKET; + + p.packet.nmb.header.name_trn_id = 10; + p.packet.nmb.header.opcode = 0; + p.packet.nmb.header.response = False; + p.packet.nmb.header.nm_flags.bcast = False; + p.packet.nmb.header.nm_flags.recursion_available = False; + p.packet.nmb.header.nm_flags.recursion_desired = False; + p.packet.nmb.header.nm_flags.trunc = False; + p.packet.nmb.header.nm_flags.authoritative = False; + p.packet.nmb.header.rcode = 0; + p.packet.nmb.header.qdcount = 1; + p.packet.nmb.header.ancount = 0; + p.packet.nmb.header.nscount = 0; + p.packet.nmb.header.arcount = 0; + my_make_nmb_name(&p.packet.nmb.question.question_name, "*", 0x00); + p.packet.nmb.question.question_type = 0x21; + p.packet.nmb.question.question_class = 0x1; + + return send_message(msg_ctx, pid, MSG_SEND_PACKET, &p, sizeof(p)); +} + +static bool do_notify_cleanup(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol smbd notify-cleanup\n"); + return false; + } + return send_message(msg_ctx, pid, MSG_SMB_NOTIFY_CLEANUP, NULL, 0); +} + +/* A list of message type supported */ + +static const struct { + const char *name; /* Option name */ + bool (*fn)(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + const struct server_id pid, + const int argc, const char **argv); + const char *help; /* Short help text */ +} msg_types[] = { + { + .name = "debug", + .fn = do_debug, + .help = "Set debuglevel", + }, + { + .name = "idmap", + .fn = do_idmap, + .help = "Manipulate idmap cache", + }, + { + .name = "force-election", + .fn = do_election, + .help = "Force a browse election", + }, + { + .name = "ping", + .fn = do_ping, + .help = "Elicit a response", + }, + { + .name = "profile", + .fn = do_profile, + .help = "", + }, + { + .name = "inject", + .fn = do_inject_fault, + .help = "Inject a fatal signal into a running smbd"}, + { + .name = "stacktrace", + .fn = do_daemon_stack_trace, + .help = "Display a stack trace of a daemon", + }, + { + .name = "profilelevel", + .fn = do_profilelevel, + .help = "", + }, + { + .name = "debuglevel", + .fn = do_debuglevel, + .help = "Display current debuglevels", + }, + { + .name = "printnotify", + .fn = do_printnotify, + .help = "Send a print notify message", + }, + { + .name = "close-share", + .fn = do_closeshare, + .help = "Forcibly disconnect a share", + }, + { + .name = "close-denied-share", + .fn = do_close_denied_share, + .help = "Forcibly disconnect users from shares disallowed now", + }, + { + .name = "kill-client-ip", + .fn = do_kill_client_by_ip, + .help = "Forcibly disconnect a client with a specific IP address", + }, + { + .name = "ip-dropped", + .fn = do_ip_dropped, + .help = "Tell winbind that an IP got dropped", + }, + { + .name = "pool-usage", + .fn = do_poolusage, + .help = "Display talloc memory usage", + }, + { + .name = "rpc-dump-status", + .fn = do_rpc_dump_status, + .help = "Display rpc status", + }, + { + .name = "ringbuf-log", + .fn = do_ringbuflog, + .help = "Display ringbuf log", + }, + { + .name = "dmalloc-mark", + .fn = do_dmalloc_mark, + .help = "", + }, + { + .name = "dmalloc-log-changed", + .fn = do_dmalloc_changed, + .help = "", + }, + { + .name = "shutdown", + .fn = do_shutdown, + .help = "Shut down daemon", + }, + { + .name = "drvupgrade", + .fn = do_drvupgrade, + .help = "Notify a printer driver has changed", + }, + { + .name = "reload-certs", + .fn = do_reload_certs, + .help = "Reload TLS certificates" + }, + { + .name = "reload-config", + .fn = do_reload_config, + .help = "Force smbd or winbindd to reload config file"}, + { + .name = "reload-printers", + .fn = do_reload_printers, + .help = "Force smbd to reload printers"}, + { + .name = "nodestatus", + .fn = do_nodestatus, + .help = "Ask nmbd to do a node status request"}, + { + .name = "online", + .fn = do_winbind_online, + .help = "Ask winbind to go into online state"}, + { + .name = "offline", + .fn = do_winbind_offline, + .help = "Ask winbind to go into offline state"}, + { + .name = "onlinestatus", + .fn = do_winbind_onlinestatus, + .help = "Request winbind online status"}, + { + .name = "validate-cache" , + .fn = do_winbind_validate_cache, + .help = "Validate winbind's credential cache", + }, + { + .name = "dump-domain-list", + .fn = do_winbind_dump_domain_list, + .help = "Dump winbind domain list"}, + { + .name = "disconnect-dc", + .fn = do_msg_disconnect_dc, + }, + { + .name = "notify-cleanup", + .fn = do_notify_cleanup, + }, + { + .name = "num-children", + .fn = do_num_children, + .help = "Print number of smbd child processes", + }, + { + .name = "msg-cleanup", + .fn = do_msg_cleanup, + }, + { + .name = "noop", + .fn = do_noop, + .help = "Do nothing", + }, + { + .name = "sleep", + .fn = do_sleep, + .help = "Cause the target process to sleep", + }, + { .name = NULL, }, +}; + +/* Display usage information */ + +static void usage(poptContext pc) +{ + int i; + + poptPrintHelp(pc, stderr, 0); + + fprintf(stderr, "\n"); + fprintf(stderr, "<destination> is one of \"nmbd\", \"smbd\", \"winbindd\", " + "\"ldap_server\" or a process ID\n"); + + fprintf(stderr, "\n"); + fprintf(stderr, "<message-type> is one of:\n"); + + for (i = 0; msg_types[i].name; i++) { + const char *help = msg_types[i].help; + if (help == NULL) { + help = ""; + } + fprintf(stderr, "\t%-30s%s\n", msg_types[i].name, help); + } + + fprintf(stderr, "\n"); + + exit(1); +} + +/* Return the pid number for a string destination */ + +static struct server_id parse_dest(struct messaging_context *msg, + const char *dest) +{ + struct server_id result = { + .pid = (uint64_t)-1, + }; + pid_t pid; + struct server_id_db *names_db = NULL; + bool ok; + + /* Zero is a special return value for broadcast to all processes */ + + if (strequal(dest, "all")) { + return interpret_pid(MSG_BROADCAST_PID_STR); + } + + /* Try self - useful for testing */ + + if (strequal(dest, "self")) { + return messaging_server_id(msg); + } + + /* Fix winbind typo. */ + if (strequal(dest, "winbind")) { + dest = "winbindd"; + } + + /* Check for numeric pid number */ + result = interpret_pid(dest); + + /* Zero isn't valid if not "all". */ + if (result.pid && procid_valid(&result)) { + return result; + } + + /* Look up other destinations in pidfile directory */ + + if ((pid = pidfile_pid(lp_pid_directory(), dest)) != 0) { + return pid_to_procid(pid); + } + + names_db = messaging_names_db(msg); + if (names_db == NULL) { + goto fail; + } + ok = server_id_db_lookup_one(names_db, dest, &result); + if (ok) { + return result; + } + +fail: + fprintf(stderr,"Can't find pid for destination '%s'\n", dest); + + return result; +} + +/* Execute smbcontrol command */ + +static bool do_command(struct tevent_context *ev_ctx, + struct messaging_context *msg_ctx, + int argc, const char **argv) +{ + const char *dest = argv[0], *command = argv[1]; + struct server_id pid; + int i; + + /* Check destination */ + + pid = parse_dest(msg_ctx, dest); + if (!procid_valid(&pid)) { + return False; + } + + /* Check command */ + + for (i = 0; msg_types[i].name; i++) { + if (strequal(command, msg_types[i].name)) + return msg_types[i].fn(ev_ctx, msg_ctx, pid, + argc - 1, argv + 1); + } + + fprintf(stderr, "smbcontrol: unknown command '%s'\n", command); + + return False; +} + +static void smbcontrol_help(poptContext pc, + enum poptCallbackReason preason, + struct poptOption * poption, + const char * parg, + void * pdata) +{ + if (poption->shortName != '?') { + poptPrintUsage(pc, stdout, 0); + } else { + usage(pc); + } + + exit(0); +} + +struct poptOption help_options[] = { + { NULL, '\0', POPT_ARG_CALLBACK, (void *)&smbcontrol_help, '\0', + NULL, NULL }, + { "help", '?', 0, NULL, '?', "Show this help message", NULL }, + { "usage", '\0', 0, NULL, 'u', "Display brief usage message", NULL }, + {0} +} ; + +/* Main program */ + +int main(int argc, const char **argv) +{ + poptContext pc; + int opt; + struct tevent_context *evt_ctx; + struct messaging_context *msg_ctx; + + struct poptOption long_options[] = { + /* POPT_AUTOHELP */ + { NULL, '\0', POPT_ARG_INCLUDE_TABLE, help_options, + 0, "Help options:", NULL }, + { "timeout", 't', POPT_ARG_INT, &timeout, 't', + "Set timeout value in seconds", "TIMEOUT" }, + + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + int ret = 0; + bool ok; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + + /* Parse command line arguments using popt */ + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "[OPTION...] <destination> <message-type> " + "<parameters>"); + + if (argc == 1) + usage(pc); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 't': /* --timeout */ + break; + default: + fprintf(stderr, "Invalid option\n"); + poptPrintHelp(pc, stderr, 0); + break; + } + } + + /* We should now have the remaining command line arguments in + argv. The argc parameter should have been decremented to the + correct value in the above switch statement. */ + + argv = (const char **)poptGetArgs(pc); + argc = 0; + if (argv != NULL) { + while (argv[argc] != NULL) { + argc++; + } + } + + if (argc <= 1) + usage(pc); + + msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + if (msg_ctx == NULL) { + fprintf(stderr, + "Could not init messaging context, not root?\n"); + TALLOC_FREE(frame); + exit(1); + } + + evt_ctx = global_event_context(); + + /* Need to invert sense of return code -- samba + * routines mostly return True==1 for success, but + * shell needs 0. */ + + ret = !do_command(evt_ctx, msg_ctx, argc, argv); + + cmdline_messaging_context_free(); + gfree_all(); + poptFreeContext(pc); + TALLOC_FREE(frame); + return ret; +} diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c new file mode 100644 index 0000000..655da7d --- /dev/null +++ b/source3/utils/smbcquotas.c @@ -0,0 +1,832 @@ +/* + Unix SMB/CIFS implementation. + QUOTA get/set utility + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Stefan (metze) Metzmacher 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_lsa.h" +#include "rpc_client/cli_lsarpc.h" +#include "fake_file.h" +#include "../libcli/security/security.h" +#include "libsmb/libsmb.h" +#include "lib/param/param.h" + +static char *server; + +/* numeric is set when the user wants numeric SIDs and ACEs rather + than going via LSA calls to resolve them */ +static bool numeric; +static bool verbose; + +enum todo_values {NOOP_QUOTA=0,FS_QUOTA,USER_QUOTA,LIST_QUOTA,SET_QUOTA}; +enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; + +static struct cli_state *cli_ipc; +static struct rpc_pipe_client *global_pipe_hnd; +static struct policy_handle pol; +static bool got_policy_hnd; + +static struct cli_state *connect_one(const char *share); + +/* Open cli connection and policy handle */ + +static bool cli_open_policy_hnd(void) +{ + /* Initialise cli LSA connection */ + + if (!cli_ipc) { + NTSTATUS ret; + cli_ipc = connect_one("IPC$"); + ret = cli_rpc_pipe_open_noauth(cli_ipc, + &ndr_table_lsarpc, + &global_pipe_hnd); + if (!NT_STATUS_IS_OK(ret)) { + return False; + } + } + + /* Open policy handle */ + + if (!got_policy_hnd) { + + /* Some systems don't support SEC_FLAG_MAXIMUM_ALLOWED, + but NT sends 0x2000000 so we might as well do it too. */ + + if (!NT_STATUS_IS_OK(rpccli_lsa_open_policy(global_pipe_hnd, talloc_tos(), True, + GENERIC_EXECUTE_ACCESS, &pol))) { + return False; + } + + got_policy_hnd = True; + } + + return True; +} + +/* convert a SID to a string, either numeric or username/group */ +static void SidToString(fstring str, struct dom_sid *sid, bool _numeric) +{ + char **domains = NULL; + char **names = NULL; + enum lsa_SidType *types = NULL; + + sid_to_fstring(str, sid); + + if (_numeric) return; + + /* Ask LSA to convert the sid to a name */ + + if (!cli_open_policy_hnd() || + !NT_STATUS_IS_OK(rpccli_lsa_lookup_sids(global_pipe_hnd, talloc_tos(), + &pol, 1, sid, &domains, + &names, &types)) || + !domains || !domains[0] || !names || !names[0]) { + return; + } + + /* Converted OK */ + + slprintf(str, sizeof(fstring) - 1, "%s%s%s", + domains[0], lp_winbind_separator(), + names[0]); +} + +/* convert a string to a SID, either numeric or username/group */ +static bool StringToSid(struct dom_sid *sid, const char *str) +{ + enum lsa_SidType *types = NULL; + struct dom_sid *sids = NULL; + bool result = True; + + if (string_to_sid(sid, str)) { + return true; + } + + if (!cli_open_policy_hnd() || + !NT_STATUS_IS_OK(rpccli_lsa_lookup_names(global_pipe_hnd, talloc_tos(), + &pol, 1, &str, NULL, 1, &sids, + &types))) { + result = False; + goto done; + } + + sid_copy(sid, &sids[0]); + done: + + return result; +} + +#define QUOTA_GET 1 +#define QUOTA_SETLIM 2 +#define QUOTA_SETFLAGS 3 +#define QUOTA_LIST 4 + +enum {PARSE_FLAGS,PARSE_LIM}; + +static int parse_quota_set(TALLOC_CTX *ctx, + char *set_str, + char **pp_username_str, + enum SMB_QUOTA_TYPE *qtype, + int *cmd, + SMB_NTQUOTA_STRUCT *pqt) +{ + char *p = set_str,*p2; + int todo; + bool stop = False; + bool enable = False; + bool deny = False; + + *pp_username_str = NULL; + if (strnequal(set_str,"UQLIM:",6)) { + p += 6; + *qtype = SMB_USER_QUOTA_TYPE; + *cmd = QUOTA_SETLIM; + todo = PARSE_LIM; + if ((p2=strstr(p,":"))==NULL) { + return -1; + } + + *p2 = '\0'; + p2++; + + *pp_username_str = talloc_strdup(ctx, p); + p = p2; + } else if (strnequal(set_str,"FSQLIM:",7)) { + p +=7; + *qtype = SMB_USER_FS_QUOTA_TYPE; + *cmd = QUOTA_SETLIM; + todo = PARSE_LIM; + } else if (strnequal(set_str,"FSQFLAGS:",9)) { + p +=9; + todo = PARSE_FLAGS; + *qtype = SMB_USER_FS_QUOTA_TYPE; + *cmd = QUOTA_SETFLAGS; + } else { + return -1; + } + + switch (todo) { + case PARSE_LIM: + if (sscanf(p,"%"SCNu64"/%"SCNu64,&pqt->softlim, + &pqt->hardlim) != 2) + { + return -1; + } + + break; + case PARSE_FLAGS: + while (!stop) { + + if ((p2=strstr(p,"/"))==NULL) { + stop = True; + } else { + *p2 = '\0'; + p2++; + } + + if (strnequal(p,"QUOTA_ENABLED",13)) { + enable = True; + } else if (strnequal(p,"DENY_DISK",9)) { + deny = True; + } else if (strnequal(p,"LOG_SOFTLIMIT",13)) { + pqt->qflags |= QUOTAS_LOG_THRESHOLD; + } else if (strnequal(p,"LOG_HARDLIMIT",13)) { + pqt->qflags |= QUOTAS_LOG_LIMIT; + } else { + return -1; + } + + p=p2; + } + + if (deny) { + pqt->qflags |= QUOTAS_DENY_DISK; + } else if (enable) { + pqt->qflags |= QUOTAS_ENABLED; + } + + break; + } + + return 0; +} + + +static const char *quota_str_static(uint64_t val, bool special, bool _numeric) +{ + const char *result; + + if (!_numeric && special && val == 0) { + return "NO LIMIT"; + } + result = talloc_asprintf(talloc_tos(), "%"PRIu64, val); + SMB_ASSERT(result != NULL); + return result; +} + +static void dump_ntquota(SMB_NTQUOTA_STRUCT *qt, bool _verbose, + bool _numeric, + void (*_sidtostring)(fstring str, + struct dom_sid *sid, + bool _numeric)) +{ + TALLOC_CTX *frame = talloc_stackframe(); + + if (!qt) { + smb_panic("dump_ntquota() called with NULL pointer"); + } + + switch (qt->qtype) { + case SMB_USER_FS_QUOTA_TYPE: + { + d_printf("File System QUOTAS:\n"); + d_printf("Limits:\n"); + d_printf(" Default Soft Limit: %15s\n", + quota_str_static(qt->softlim,True,_numeric)); + d_printf(" Default Hard Limit: %15s\n", + quota_str_static(qt->hardlim,True,_numeric)); + d_printf("Quota Flags:\n"); + d_printf(" Quotas Enabled: %s\n", + ((qt->qflags"AS_ENABLED) + ||(qt->qflags"AS_DENY_DISK))?"On":"Off"); + d_printf(" Deny Disk: %s\n", + (qt->qflags"AS_DENY_DISK)?"On":"Off"); + d_printf(" Log Soft Limit: %s\n", + (qt->qflags"AS_LOG_THRESHOLD)?"On":"Off"); + d_printf(" Log Hard Limit: %s\n", + (qt->qflags"AS_LOG_LIMIT)?"On":"Off"); + } + break; + case SMB_USER_QUOTA_TYPE: + { + fstring username_str = {0}; + + if (_sidtostring) { + _sidtostring(username_str,&qt->sid,_numeric); + } else { + sid_to_fstring(username_str, &qt->sid); + } + + if (_verbose) { + d_printf("Quotas for User: %s\n",username_str); + d_printf("Used Space: %15s\n", + quota_str_static(qt->usedspace,False, + _numeric)); + d_printf("Soft Limit: %15s\n", + quota_str_static(qt->softlim,True, + _numeric)); + d_printf("Hard Limit: %15s\n", + quota_str_static(qt->hardlim,True,_numeric)); + } else { + d_printf("%-30s: ",username_str); + d_printf("%15s/",quota_str_static( + qt->usedspace,False,_numeric)); + d_printf("%15s/",quota_str_static( + qt->softlim,True,_numeric)); + d_printf("%15s\n",quota_str_static( + qt->hardlim,True,_numeric)); + } + } + break; + default: + d_printf("dump_ntquota() invalid qtype(%d)\n",qt->qtype); + } + TALLOC_FREE(frame); + return; +} + +static void dump_ntquota_list(SMB_NTQUOTA_LIST **qtl, bool _verbose, + bool _numeric, + void (*_sidtostring)(fstring str, + struct dom_sid *sid, + bool _numeric)) +{ + SMB_NTQUOTA_LIST *cur; + + for (cur = *qtl;cur;cur = cur->next) { + if (cur->quotas) + dump_ntquota(cur->quotas,_verbose,_numeric, + _sidtostring); + } +} + +static int do_quota(struct cli_state *cli, + enum SMB_QUOTA_TYPE qtype, + uint16_t cmd, + const char *username_str, + SMB_NTQUOTA_STRUCT *pqt) +{ + uint32_t fs_attrs = 0; + uint16_t quota_fnum = 0; + SMB_NTQUOTA_LIST *qtl = NULL; + TALLOC_CTX *qtl_ctx = NULL; + SMB_NTQUOTA_STRUCT qt; + NTSTATUS status; + + ZERO_STRUCT(qt); + + status = cli_get_fs_attr_info(cli, &fs_attrs); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Failed to get the filesystem attributes %s.\n", + nt_errstr(status)); + return -1; + } + + if (!(fs_attrs & FILE_VOLUME_QUOTAS)) { + d_printf("Quotas are not supported by the server.\n"); + return 0; + } + + status = cli_get_quota_handle(cli, "a_fnum); + if (!NT_STATUS_IS_OK(status)) { + d_printf("Quotas are not enabled on this share.\n"); + d_printf("Failed to open %s %s.\n", + FAKE_FILE_NAME_QUOTA_WIN32, + nt_errstr(status)); + return -1; + } + + switch(qtype) { + case SMB_USER_QUOTA_TYPE: + if (!StringToSid(&qt.sid, username_str)) { + d_printf("StringToSid() failed for [%s]\n",username_str); + return -1; + } + + switch(cmd) { + case QUOTA_GET: + status = cli_get_user_quota( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_user_quota %s\n", + nt_errstr(status), + username_str); + return -1; + } + dump_ntquota(&qt,verbose,numeric,SidToString); + break; + case QUOTA_SETLIM: + pqt->sid = qt.sid; + if ((qtl_ctx = talloc_init( + "SMB_USER_QUOTA_SET")) == + NULL) { + return -1; + } + + if (!add_record_to_ntquota_list( + qtl_ctx, pqt, &qtl)) { + TALLOC_FREE(qtl_ctx); + return -1; + } + + status = cli_set_user_quota( + cli, quota_fnum, qtl); + free_ntquota_list(&qtl); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_set_user_quota %s\n", + nt_errstr(status), + username_str); + return -1; + } + status = cli_get_user_quota( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_user_quota %s\n", + nt_errstr(status), + username_str); + return -1; + } + dump_ntquota(&qt,verbose,numeric,SidToString); + break; + case QUOTA_LIST: + status = cli_list_user_quota( + cli, quota_fnum, &qtl); + if (!NT_STATUS_IS_OK(status)) { + d_printf( + "%s cli_list_user_quota\n", + nt_errstr(status)); + return -1; + } + dump_ntquota_list(&qtl,verbose,numeric,SidToString); + free_ntquota_list(&qtl); + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + break; + case SMB_USER_FS_QUOTA_TYPE: + switch(cmd) { + case QUOTA_GET: + status = cli_get_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + case QUOTA_SETLIM: + status = cli_get_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + qt.softlim = pqt->softlim; + qt.hardlim = pqt->hardlim; + status = cli_set_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_set_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + status = cli_get_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + case QUOTA_SETFLAGS: + status = cli_get_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + qt.qflags = pqt->qflags; + status = cli_set_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_set_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + status = cli_get_fs_quota_info( + cli, quota_fnum, &qt); + if (!NT_STATUS_IS_OK(status)) { + d_printf("%s cli_get_fs_quota_info\n", + nt_errstr(status)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + + cli_close(cli, quota_fnum); + + return 0; +} + +/***************************************************** + Return a connection to a server. +*******************************************************/ + +static struct cli_state *connect_one(const char *share) +{ + struct cli_state *c; + NTSTATUS nt_status; + uint32_t flags = 0; + + nt_status = cli_full_connection_creds(&c, lp_netbios_name(), server, + NULL, 0, + share, "?????", + samba_cmdline_get_creds(), + flags); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); + return NULL; + } + + return c; +} + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc, char *argv[]) +{ + const char **argv_const = discard_const_p(const char *, argv); + char *share; + int opt; + int result; + int todo = 0; + char *username_str = NULL; + char *path = NULL; + char *set_str = NULL; + enum SMB_QUOTA_TYPE qtype = SMB_INVALID_QUOTA_TYPE; + int cmd = 0; + static bool test_args = False; + struct cli_state *cli; + bool fix_user = False; + SMB_NTQUOTA_STRUCT qt; + TALLOC_CTX *frame = talloc_stackframe(); + poptContext pc; + struct cli_credentials *creds = NULL; + bool ok; + struct loadparm_context *lp_ctx = NULL; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "quota-user", + .shortName = 'u', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'u', + .descrip = "Show quotas for user", + .argDescrip = "USER", + }, + { + .longName = "list", + .shortName = 'L', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'L', + .descrip = "List user quotas", + }, + { + .longName = "fs", + .shortName = 'F', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'F', + .descrip = "Show filesystem quotas", + }, + { + .longName = "set", + .shortName = 'S', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'S', + .descrip = "Set acls\n" + "SETSTRING:\n" + "UQLIM:<username>/<softlimit>/<hardlimit> for user quotas\n" + "FSQLIM:<softlimit>/<hardlimit> for filesystem defaults\n" + "FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", + .argDescrip = "SETSTRING", + }, + { + .longName = "numeric", + .shortName = 'n', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'n', + .descrip = "Don't resolve sids or limits to names", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'v', + .descrip = "be verbose", + }, + { + .longName = "test-args", + .shortName = 't', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 't', + .descrip = "Test arguments" + }, + { + .longName = "max-protocol", + .shortName = 'm', + .argInfo = POPT_ARG_STRING, + .arg = NULL, + .val = 'm', + .descrip = "Set the max protocol level", + .argDescrip = "LEVEL" + }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_LEGACY_S3 + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + smb_init_locale(); + + ZERO_STRUCT(qt); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + /* set default debug level to 1 regardless of what smb.conf sets */ + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + setlinebuf(stdout); + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + poptSetOtherOptionHelp(pc, "//server1/share1"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'n': + numeric = true; + break; + case 'v': + verbose = true; + break; + case 't': + test_args = true; + break; + case 'L': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + todo = LIST_QUOTA; + break; + + case 'F': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + todo = FS_QUOTA; + break; + + case 'u': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + username_str = talloc_strdup(frame, poptGetOptArg(pc)); + if (!username_str) { + exit(EXIT_PARSE_ERROR); + } + todo = USER_QUOTA; + fix_user = True; + break; + + case 'S': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + set_str = talloc_strdup(frame, poptGetOptArg(pc)); + if (!set_str) { + exit(EXIT_PARSE_ERROR); + } + todo = SET_QUOTA; + break; + case 'm': + lpcfg_set_cmdline(lp_ctx, + "client max protocol", + poptGetOptArg(pc)); + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + creds = samba_cmdline_get_creds(); + + if (todo == 0) + todo = USER_QUOTA; + + if (!fix_user) { + const char *user = cli_credentials_get_username(creds); + if (user == NULL) { + exit(EXIT_PARSE_ERROR); + } + + username_str = talloc_strdup(frame, user); + if (!username_str) { + exit(EXIT_PARSE_ERROR); + } + } + + /* Make connection to server */ + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(EXIT_PARSE_ERROR); + } + + path = talloc_strdup(frame, poptGetArg(pc)); + if (!path) { + printf("Out of memory\n"); + exit(EXIT_PARSE_ERROR); + } + + if (strncmp(path, "\\\\", 2) && strncmp(path, "//", 2)) { + printf("Invalid argument: %s\n", path); + return -1; + } + + poptFreeContext(pc); + samba_cmdline_burn(argc, argv); + + string_replace(path, '/', '\\'); + + server = SMB_STRDUP(path+2); + if (!server) { + printf("Out of memory\n"); + exit(EXIT_PARSE_ERROR); + } + share = strchr_m(server,'\\'); + if (share == NULL) { + printf("Invalid argument\n"); + exit(EXIT_PARSE_ERROR); + } + + *share = 0; + share++; + + if (todo == SET_QUOTA) { + if (parse_quota_set(talloc_tos(), set_str, &username_str, &qtype, &cmd, &qt)) { + printf("Invalid argument: -S %s\n", set_str); + exit(EXIT_PARSE_ERROR); + } + } + + if (!test_args) { + cli = connect_one(share); + if (!cli) { + exit(EXIT_FAILED); + } + } else { + exit(EXIT_OK); + } + + + /* Perform requested action */ + + switch (todo) { + case FS_QUOTA: + result = do_quota(cli,SMB_USER_FS_QUOTA_TYPE, QUOTA_GET, username_str, NULL); + break; + case LIST_QUOTA: + result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_LIST, username_str, NULL); + break; + case USER_QUOTA: + result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_GET, username_str, NULL); + break; + case SET_QUOTA: + result = do_quota(cli, qtype, cmd, username_str, &qt); + break; + default: + result = EXIT_FAILED; + break; + } + + gfree_all(); + talloc_free(frame); + + return result; +} diff --git a/source3/utils/smbfilter.c b/source3/utils/smbfilter.c new file mode 100644 index 0000000..ebf2809 --- /dev/null +++ b/source3/utils/smbfilter.c @@ -0,0 +1,356 @@ +/* + Unix SMB/CIFS implementation. + SMB filter/socket plugin + Copyright (C) Andrew Tridgell 1999 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "system/filesys.h" +#include "system/select.h" +#include "libsmb/namequery.h" +#include "../lib/util/select.h" +#include "libsmb/nmblib.h" +#include "lib/util/sys_rw_data.h" + +#define SECURITY_MASK 0 +#define SECURITY_SET 0 + +/* this forces non-unicode */ +#define CAPABILITY_MASK 0 +#define CAPABILITY_SET 0 + +/* and non-unicode for the client too */ +#define CLI_CAPABILITY_MASK 0 +#define CLI_CAPABILITY_SET 0 + +static char *netbiosname; + +static void save_file(const char *fname, void *ppacket, size_t length) +{ + int fd; + fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd == -1) { + perror(fname); + return; + } + if (write(fd, ppacket, length) != length) { + fprintf(stderr,"Failed to write %s\n", fname); + close(fd); + return; + } + close(fd); + printf("Wrote %ld bytes to %s\n", (unsigned long)length, fname); +} + +static void filter_reply(char *buf) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + unsigned x; + + if (msg_type) return; + + switch (type) { + + case SMBnegprot: + /* force the security bits */ + x = CVAL(buf, smb_vwv1); + x = (x | SECURITY_SET) & ~SECURITY_MASK; + SCVAL(buf, smb_vwv1, x); + + /* force the capabilities */ + x = IVAL(buf,smb_vwv9+1); + x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK; + SIVAL(buf, smb_vwv9+1, x); + break; + + } +} + +static void filter_request(char *buf, size_t buf_len) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + unsigned x; + fstring name1,name2; + int name_len1 = 0; + int name_len2; + int name_type1, name_type2; + int ret; + + if (msg_type) { + /* it's a netbios special */ + switch (msg_type) + case 0x81: + /* session request */ + /* inbuf_size is guaranteed to be at least 4. */ + name_len1 = name_len((unsigned char *)(buf+4), + buf_len - 4); + if (name_len1 <= 0 || name_len1 > buf_len - 4) { + DEBUG(0,("Invalid name length in session request\n")); + return; + } + name_len2 = name_len((unsigned char *)(buf+4+name_len1), + buf_len - 4 - name_len1); + if (name_len2 <= 0 || name_len2 > buf_len - 4 - name_len1) { + DEBUG(0,("Invalid name length in session request\n")); + return; + } + + name_type1 = name_extract((unsigned char *)buf, + buf_len,(unsigned int)4,name1); + name_type2 = name_extract((unsigned char *)buf, + buf_len,(unsigned int)(4 + name_len1),name2); + + if (name_type1 == -1 || name_type2 == -1) { + DEBUG(0,("Invalid name type in session request\n")); + return; + } + + d_printf("session_request: %s -> %s\n", + name1, name2); + if (netbiosname) { + char *mangled = name_mangle( + talloc_tos(), netbiosname, 0x20); + if (mangled != NULL) { + /* replace the destination netbios + * name */ + memcpy(buf+4, mangled, + name_len((unsigned char *)mangled, + talloc_get_size(mangled))); + TALLOC_FREE(mangled); + } + } + return; + } + + /* it's an ordinary SMB request */ + switch (type) { + case SMBsesssetupX: + /* force the client capabilities */ + x = IVAL(buf,smb_vwv11); + d_printf("SMBsesssetupX cap=0x%08x\n", x); + d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8)); + ret = system("mv sessionsetup.dat sessionsetup1.dat"); + if (ret == -1) { + DBG_ERR("failed to call mv command\n"); + } + save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7)); + x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK; + SIVAL(buf, smb_vwv11, x); + break; + } +} + +/**************************************************************************** + Send an smb to a fd. +****************************************************************************/ + +static bool send_smb(int fd, char *buffer) +{ + size_t len; + size_t nwritten=0; + ssize_t ret; + + len = smb_len(buffer) + 4; + + while (nwritten < len) { + ret = write_data(fd,buffer+nwritten,len - nwritten); + if (ret <= 0) { + DEBUG(0,("Error writing %d bytes to client. %d. (%s)\n", + (int)len,(int)ret, strerror(errno) )); + return false; + } + nwritten += ret; + } + + return true; +} + +static void filter_child(int c, struct sockaddr_storage *dest_ss) +{ + NTSTATUS status; + int s = -1; + char packet[128*1024]; + + /* we have a connection from a new client, now connect to the server */ + status = open_socket_out(dest_ss, TCP_SMB_PORT, LONG_CONNECT_TIMEOUT, &s); + if (!NT_STATUS_IS_OK(status)) { + char addr[INET6_ADDRSTRLEN]; + if (dest_ss) { + print_sockaddr(addr, sizeof(addr), dest_ss); + } + + d_printf("Unable to connect to %s (%s)\n", + dest_ss?addr:"NULL", nt_errstr(status)); + exit(1); + } + + while (c != -1 || s != -1) { + struct pollfd fds[2]; + int num_fds, ret; + + memset(fds, 0, sizeof(struct pollfd) * 2); + fds[0].fd = -1; + fds[1].fd = -1; + num_fds = 0; + + if (s != -1) { + fds[num_fds].fd = s; + fds[num_fds].events = POLLIN|POLLHUP; + num_fds += 1; + } + if (c != -1) { + fds[num_fds].fd = c; + fds[num_fds].events = POLLIN|POLLHUP; + num_fds += 1; + } + + ret = sys_poll_intr(fds, num_fds, -1); + if (ret <= 0) { + continue; + } + + /* + * find c in fds and see if it's readable + */ + if ((c != -1) && + (((fds[0].fd == c) + && (fds[0].revents & (POLLIN|POLLHUP|POLLERR))) || + ((fds[1].fd == c) + && (fds[1].revents & (POLLIN|POLLHUP|POLLERR))))) { + size_t len; + if (!NT_STATUS_IS_OK(receive_smb_raw( + c, packet, sizeof(packet), + 0, 0, &len))) { + d_printf("client closed connection\n"); + exit(0); + } + filter_request(packet, len); + if (!send_smb(s, packet)) { + d_printf("server is dead\n"); + exit(1); + } + } + + /* + * find s in fds and see if it's readable + */ + if ((s != -1) && + (((fds[0].fd == s) + && (fds[0].revents & (POLLIN|POLLHUP|POLLERR))) || + ((fds[1].fd == s) + && (fds[1].revents & (POLLIN|POLLHUP|POLLERR))))) { + size_t len; + if (!NT_STATUS_IS_OK(receive_smb_raw( + s, packet, sizeof(packet), + 0, 0, &len))) { + d_printf("server closed connection\n"); + exit(0); + } + filter_reply(packet); + if (!send_smb(c, packet)) { + d_printf("client is dead\n"); + exit(1); + } + } + } + d_printf("Connection closed\n"); + exit(0); +} + + +static void start_filter(char *desthost) +{ + int s, c; + struct sockaddr_storage dest_ss; + struct sockaddr_storage my_ss; + + CatchChild(); + + /* start listening on port 445 locally */ + + zero_sockaddr(&my_ss); + s = open_socket_in(SOCK_STREAM, &my_ss, TCP_SMB_PORT, true); + + if (s < 0) { + d_printf("bind failed: %s\n", strerror(-s)); + exit(1); + } + + if (listen(s, 5) == -1) { + d_printf("listen failed\n"); + } + + if (!resolve_name(desthost, &dest_ss, 0x20, false)) { + d_printf("Unable to resolve host %s\n", desthost); + exit(1); + } + + while (1) { + int num, revents; + struct sockaddr_storage ss; + socklen_t in_addrlen = sizeof(ss); + + num = poll_intr_one_fd(s, POLLIN|POLLHUP, -1, &revents); + if ((num > 0) && (revents & (POLLIN|POLLHUP|POLLERR))) { + c = accept(s, (struct sockaddr *)&ss, &in_addrlen); + if (c != -1) { + smb_set_close_on_exec(c); + if (fork() == 0) { + close(s); + filter_child(c, &dest_ss); + exit(0); + } else { + close(c); + } + } + } + } +} + + +int main(int argc, char *argv[]) +{ + char *desthost; + const char *configfile; + TALLOC_CTX *frame = talloc_stackframe(); + + smb_init_locale(); + + setup_logging(argv[0], DEBUG_STDOUT); + + configfile = get_dyn_CONFIGFILE(); + + if (argc < 2) { + fprintf(stderr,"smbfilter <desthost> <netbiosname>\n"); + exit(1); + } + + desthost = argv[1]; + if (argc > 2) { + netbiosname = argv[2]; + } + + if (!lp_load_global(configfile)) { + d_printf("Unable to load config file\n"); + } + + start_filter(desthost); + gfree_all(); + TALLOC_FREE(frame); + return 0; +} diff --git a/source3/utils/smbget.c b/source3/utils/smbget.c new file mode 100644 index 0000000..67ea259 --- /dev/null +++ b/source3/utils/smbget.c @@ -0,0 +1,1073 @@ +/* + smbget: a wget-like utility with support for recursive downloading of + smb:// urls + Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "lib/param/param.h" +#include "libsmbclient.h" +#include "cmdline_contexts.h" +#include "auth/credentials/credentials.h" +#include "auth/gensec/gensec.h" + +static int columns = 0; + +static time_t total_start_time = 0; +static off_t total_bytes = 0; + +#define SMB_MAXPATHLEN MAXPATHLEN + +/* + * Number of bytes to read when checking whether local and remote file + * are really the same file + */ +#define RESUME_CHECK_SIZE 512 +#define RESUME_DOWNLOAD_OFFSET 1024 +#define RESUME_CHECK_OFFSET (RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE) +/* Number of bytes to read at once */ +#define SMB_DEFAULT_BLOCKSIZE 64000 + +struct opt { + char *outputfile; + size_t blocksize; + + int quiet; + int dots; + int verbose; + int send_stdout; + int update; + unsigned limit_rate; +}; +static struct opt opt = { .blocksize = SMB_DEFAULT_BLOCKSIZE }; + +static bool smb_download_file(const char *base, const char *name, + bool recursive, bool resume, bool toplevel, + char *outfile); + +static int get_num_cols(void) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) { + return 0; + } + return ws.ws_col; +#else +#warning No support for TIOCGWINSZ + char *cols = getenv("COLUMNS"); + if (!cols) { + return 0; + } + return atoi(cols); +#endif +} + +static void change_columns(int sig) +{ + columns = get_num_cols(); +} + +static void human_readable(off_t s, char *buffer, int l) +{ + if (s > 1024 * 1024 * 1024) { + snprintf(buffer, l, "%.2fGB", 1.0 * s / (1024 * 1024 * 1024)); + } else if (s > 1024 * 1024) { + snprintf(buffer, l, "%.2fMB", 1.0 * s / (1024 * 1024)); + } else if (s > 1024) { + snprintf(buffer, l, "%.2fkB", 1.0 * s / 1024); + } else { + snprintf(buffer, l, "%jdb", (intmax_t)s); + } +} + +/* + * Authentication callback for libsmbclient. + * + * The command line parser will take care asking for a password interactively! + */ +static void get_auth_data_with_context_fn(SMBCCTX *ctx, + const char *srv, + const char *shr, + char *dom, + int dom_len, + char *usr, + int usr_len, + char *pwd, + int pwd_len) +{ + struct cli_credentials *creds = samba_cmdline_get_creds(); + const char *username = NULL; + const char *password = NULL; + const char *domain = NULL; + enum credentials_obtained obtained = CRED_UNINITIALISED; + + domain = cli_credentials_get_domain_and_obtained(creds, &obtained); + if (domain != NULL) { + bool overwrite = false; + if (dom[0] == '\0') { + overwrite = true; + } + if (obtained >= CRED_CALLBACK_RESULT) { + overwrite = true; + } + if (overwrite) { + strncpy(dom, domain, dom_len - 1); + } + } + cli_credentials_set_domain(creds, dom, obtained); + + username = cli_credentials_get_username_and_obtained(creds, &obtained); + if (username != NULL) { + bool overwrite = false; + if (usr[0] == '\0') { + overwrite = true; + } + if (obtained >= CRED_CALLBACK_RESULT) { + overwrite = true; + } + if (overwrite) { + strncpy(usr, username, usr_len - 1); + } + } + cli_credentials_set_username(creds, usr, obtained); + + password = cli_credentials_get_password_and_obtained(creds, &obtained); + if (password != NULL) { + bool overwrite = false; + if (pwd[0] == '\0') { + overwrite = true; + } + if (obtained >= CRED_CALLBACK_RESULT) { + overwrite = true; + } + if (overwrite) { + strncpy(pwd, password, pwd_len - 1); + } + } + cli_credentials_set_password(creds, pwd, obtained); + + smbc_set_credentials_with_fallback(ctx, dom, usr, pwd); + + if (!opt.quiet) { + if (usr[0] == '\0') { + printf("Using guest user\n"); + } else if (dom[0] == '\0') { + printf("Using user: %s\n", usr); + } else { + printf("Using domain: %s, user: %s\n", dom, usr); + } + } +} + +static bool smb_download_dir(const char *base, const char *name, int resume) +{ + char path[SMB_MAXPATHLEN]; + int dirhandle; + struct smbc_dirent *dirent; + const char *relname = name; + char *tmpname; + bool ok = false; + + snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, + (base[0] && name[0] && name[0] != '/' && + base[strlen(base)-1] != '/') ? "/" : "", + name); + + /* List files in directory and call smb_download_file on them */ + dirhandle = smbc_opendir(path); + if (dirhandle < 1) { + if (errno == ENOTDIR) { + return smb_download_file(base, name, true, resume, + false, NULL); + } + fprintf(stderr, "Can't open directory %s: %s\n", path, + strerror(errno)); + return false; + } + + while (*relname == '/') { + relname++; + } + + if (strlen(relname) > 0) { + int rc = mkdir(relname, 0755); + if (rc == -1 && errno != EEXIST) { + fprintf(stderr, "Can't create directory %s: %s\n", + relname, strerror(errno)); + return false; + } + } + + tmpname = SMB_STRDUP(name); + + while ((dirent = smbc_readdir(dirhandle))) { + char *newname; + if (!strcmp(dirent->name, ".") || !strcmp(dirent->name, "..")) { + ok = true; + continue; + } + if (asprintf(&newname, "%s/%s", tmpname, dirent->name) == -1) { + free(tmpname); + return false; + } + switch (dirent->smbc_type) { + case SMBC_DIR: + ok = smb_download_dir(base, newname, resume); + break; + + case SMBC_WORKGROUP: + ok = smb_download_dir("smb://", dirent->name, resume); + break; + + case SMBC_SERVER: + ok = smb_download_dir("smb://", dirent->name, resume); + break; + + case SMBC_FILE: + ok = smb_download_file(base, newname, true, resume, + false, NULL); + break; + + case SMBC_FILE_SHARE: + ok = smb_download_dir(base, newname, resume); + break; + + case SMBC_PRINTER_SHARE: + if (!opt.quiet) { + printf("Ignoring printer share %s\n", + dirent->name); + } + break; + + case SMBC_COMMS_SHARE: + if (!opt.quiet) { + printf("Ignoring comms share %s\n", + dirent->name); + } + break; + + case SMBC_IPC_SHARE: + if (!opt.quiet) { + printf("Ignoring ipc$ share %s\n", + dirent->name); + } + break; + + default: + fprintf(stderr, "Ignoring file '%s' of type '%d'\n", + newname, dirent->smbc_type); + break; + } + + if (!ok) { + fprintf(stderr, "Failed to download %s: %s\n", + newname, strerror(errno)); + free(newname); + free(tmpname); + return false; + } + free(newname); + } + free(tmpname); + + smbc_closedir(dirhandle); + return ok; +} + +static char *print_time(long t) +{ + static char buffer[100]; + int secs, mins, hours; + if (t < -1) { + strncpy(buffer, "Unknown", sizeof(buffer)); + return buffer; + } + + secs = (int)t % 60; + mins = (int)t / 60 % 60; + hours = (int)t / (60 * 60); + snprintf(buffer, sizeof(buffer) - 1, "%02d:%02d:%02d", hours, mins, + secs); + return buffer; +} + +static void print_progress(const char *name, time_t start, time_t now, + off_t start_pos, off_t pos, off_t total) +{ + double avg = 0.0; + long eta = -1; + double prcnt = 0.0; + char hpos[22], htotal[22], havg[22]; + char *status, *filename; + int len; + if (now - start) { + avg = 1.0 * (pos - start_pos) / (now - start); + } + eta = (total - pos) / avg; + if (total) { + prcnt = 100.0 * pos / total; + } + + human_readable(pos, hpos, sizeof(hpos)); + human_readable(total, htotal, sizeof(htotal)); + human_readable(avg, havg, sizeof(havg)); + + len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos, + htotal, prcnt, havg, print_time(eta)); + if (len == -1) { + return; + } + + if (columns) { + int required = strlen(name), + available = columns - len - strlen("[] "); + if (required > available) { + if (asprintf(&filename, "...%s", + name + required - available + 3) == -1) { + return; + } + } else { + filename = SMB_STRNDUP(name, available); + } + } else { + filename = SMB_STRDUP(name); + } + + fprintf(stderr, "\r[%s] %s", filename, status); + + free(filename); + free(status); +} + +/* Return false on error, true on success. */ + +static bool smb_download_file(const char *base, const char *name, + bool recursive, bool resume, bool toplevel, + char *outfile) +{ + int remotehandle, localhandle; + time_t start_time = time_mono(NULL); + const char *newpath; + char path[SMB_MAXPATHLEN]; + char checkbuf[2][RESUME_CHECK_SIZE]; + char *readbuf = NULL; + off_t offset_download = 0, offset_check = 0, curpos = 0, + start_offset = 0; + struct stat localstat, remotestat; + clock_t start_of_bucket_ticks = 0; + size_t bytes_in_bucket = 0; + size_t bucket_size = 0; + clock_t ticks_to_fill_bucket = 0; + + snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, + (*base && *name && name[0] != '/' && + base[strlen(base)-1] != '/') ? "/" : "", + name); + + remotehandle = smbc_open(path, O_RDONLY, 0755); + + if (remotehandle < 0) { + switch (errno) { + case EISDIR: + if (!recursive) { + fprintf(stderr, + "%s is a directory. Specify -R " + "to download recursively\n", + path); + return false; + } + return smb_download_dir(base, name, resume); + + case ENOENT: + fprintf(stderr, + "%s can't be found on the remote server\n", + path); + return false; + + case ENOMEM: + fprintf(stderr, "Not enough memory\n"); + return false; + + case ENODEV: + fprintf(stderr, + "The share name used in %s does not exist\n", + path); + return false; + + case EACCES: + fprintf(stderr, "You don't have enough permissions " + "to access %s\n", + path); + return false; + + default: + perror("smbc_open"); + return false; + } + } + + if (smbc_fstat(remotehandle, &remotestat) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno)); + return false; + } + + if (outfile) { + newpath = outfile; + } else if (!name[0]) { + newpath = strrchr(base, '/'); + if (newpath) { + newpath++; + } else { + newpath = base; + } + } else { + newpath = name; + } + + if (!toplevel && (newpath[0] == '/')) { + newpath++; + } + + /* Open local file according to the mode */ + if (opt.update) { + /* if it is up-to-date, skip */ + if (stat(newpath, &localstat) == 0 && + localstat.st_mtime >= remotestat.st_mtime) { + if (opt.verbose) { + printf("%s is up-to-date, skipping\n", newpath); + } + smbc_close(remotehandle); + return true; + } + /* else open it for writing and truncate if it exists */ + localhandle = open( + newpath, O_CREAT | O_NONBLOCK | O_RDWR | O_TRUNC, 0775); + if (localhandle < 0) { + fprintf(stderr, "Can't open %s : %s\n", newpath, + strerror(errno)); + smbc_close(remotehandle); + return false; + } + /* no offset */ + } else if (!opt.send_stdout) { + localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | + (!resume ? O_EXCL : 0), + 0755); + if (localhandle < 0) { + fprintf(stderr, "Can't open %s: %s\n", newpath, + strerror(errno)); + smbc_close(remotehandle); + return false; + } + + if (fstat(localhandle, &localstat) != 0) { + fprintf(stderr, "Can't fstat %s: %s\n", newpath, + strerror(errno)); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + start_offset = localstat.st_size; + + if (localstat.st_size && + localstat.st_size == remotestat.st_size) { + if (opt.verbose) { + fprintf(stderr, "%s is already downloaded " + "completely.\n", + path); + } else if (!opt.quiet) { + fprintf(stderr, "%s\n", path); + } + smbc_close(remotehandle); + close(localhandle); + return true; + } + + if (localstat.st_size > RESUME_CHECK_OFFSET && + remotestat.st_size > RESUME_CHECK_OFFSET) { + offset_download = + localstat.st_size - RESUME_DOWNLOAD_OFFSET; + offset_check = localstat.st_size - RESUME_CHECK_OFFSET; + if (opt.verbose) { + printf("Trying to start resume of %s at %jd\n" + "At the moment %jd of %jd bytes have " + "been retrieved\n", + newpath, (intmax_t)offset_check, + (intmax_t)localstat.st_size, + (intmax_t)remotestat.st_size); + } + } + + if (offset_check) { + off_t off1, off2; + /* First, check all bytes from offset_check to + * offset_download */ + off1 = lseek(localhandle, offset_check, SEEK_SET); + if (off1 < 0) { + fprintf(stderr, + "Can't seek to %jd in local file %s\n", + (intmax_t)offset_check, newpath); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET); + if (off2 < 0) { + fprintf(stderr, + "Can't seek to %jd in remote file %s\n", + (intmax_t)offset_check, newpath); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + if (off1 != off2) { + fprintf(stderr, "Offset in local and remote " + "files are different " + "(local: %jd, remote: %jd)\n", + (intmax_t)off1, (intmax_t)off2); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + if (smbc_read(remotehandle, checkbuf[0], + RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) { + fprintf(stderr, "Can't read %d bytes from " + "remote file %s\n", + RESUME_CHECK_SIZE, path); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + if (read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != + RESUME_CHECK_SIZE) { + fprintf(stderr, "Can't read %d bytes from " + "local file %s\n", + RESUME_CHECK_SIZE, name); + smbc_close(remotehandle); + close(localhandle); + return false; + } + + if (memcmp(checkbuf[0], checkbuf[1], + RESUME_CHECK_SIZE) == 0) { + if (opt.verbose) { + printf("Current local and remote file " + "appear to be the same. " + "Starting download from " + "offset %jd\n", + (intmax_t)offset_download); + } + } else { + fprintf(stderr, "Local and remote file appear " + "to be different, not " + "doing resume for %s\n", + path); + smbc_close(remotehandle); + close(localhandle); + return false; + } + } + } else { + localhandle = STDOUT_FILENO; + start_offset = 0; + offset_download = 0; + offset_check = 0; + } + + /* We implement rate limiting by filling up a bucket with bytes and + * checking, once the bucket is filled, if it was filled too fast. + * If so, we sleep for some time to get an average transfer rate that + * equals to the one set by the user. + * + * The bucket size directly affects the traffic characteristics. + * The smaller the bucket the more frequent the pause/resume cycle. + * A large bucket can result in burst of high speed traffic and large + * pauses. A cycle of 100ms looks like a good value. This value (in + * ticks) is held in `ticks_to_fill_bucket`. The `bucket_size` is + * calculated as: + * `limit_rate * 1024 * / (CLOCKS_PER_SEC / ticks_to_fill_bucket)` + * + * After selecting the bucket size we also need to check the blocksize + * of the transfer, since this is the minimum unit of traffic that we + * can observe. Achieving a ~10% precision requires a blocksize with a + * maximum size of `bucket_size / 10`. + */ + if (opt.limit_rate > 0) { + unsigned max_block_size; + /* This is the time that the bucket should take to fill. */ + ticks_to_fill_bucket = 100 /*ms*/ * CLOCKS_PER_SEC / 1000; + /* This is the size of the bucket in bytes. + * If we fill the bucket too quickly we should pause */ + bucket_size = opt.limit_rate * 1024 / (CLOCKS_PER_SEC / ticks_to_fill_bucket); + max_block_size = bucket_size / 10; + max_block_size = max_block_size > 0 ? max_block_size : 1; + if (opt.blocksize > max_block_size) { + if (opt.blocksize != SMB_DEFAULT_BLOCKSIZE) { + fprintf(stderr, + "Warning: Overriding block size to %d " + "due to limit-rate", max_block_size); + } + opt.blocksize = max_block_size; + } + start_of_bucket_ticks = clock(); + } + + readbuf = (char *)SMB_MALLOC(opt.blocksize); + if (!readbuf) { + fprintf(stderr, "Failed to allocate %zu bytes for read " + "buffer (%s)", opt.blocksize, strerror(errno)); + if (localhandle != STDOUT_FILENO) { + close(localhandle); + } + return false; + } + + /* Now, download all bytes from offset_download to the end */ + for (curpos = offset_download; curpos < remotestat.st_size; + curpos += opt.blocksize) { + ssize_t bytesread; + ssize_t byteswritten; + + /* Rate limiting. This pauses the transfer to limit traffic. */ + if (opt.limit_rate > 0) { + if (bytes_in_bucket > bucket_size) { + clock_t now_ticks = clock(); + clock_t diff_ticks = now_ticks + - start_of_bucket_ticks; + /* Check if the bucket filled up too fast. */ + if (diff_ticks < ticks_to_fill_bucket) { + /* Pause until `ticks_to_fill_bucket` */ + double sleep_us + = (ticks_to_fill_bucket - diff_ticks) + * 1000000.0 / CLOCKS_PER_SEC; + usleep(sleep_us); + } + /* Reset the byte counter and the ticks. */ + bytes_in_bucket = 0; + start_of_bucket_ticks = clock(); + } + } + + bytesread = smbc_read(remotehandle, readbuf, opt.blocksize); + if (opt.limit_rate > 0) { + bytes_in_bucket += bytesread; + } + if(bytesread < 0) { + fprintf(stderr, + "Can't read %zu bytes at offset %jd, file %s\n", + opt.blocksize, (intmax_t)curpos, path); + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) { + close(localhandle); + } + free(readbuf); + return false; + } + + total_bytes += bytesread; + + byteswritten = write(localhandle, readbuf, bytesread); + if (byteswritten != bytesread) { + fprintf(stderr, + "Can't write %zd bytes to local file %s at " + "offset %jd\n", bytesread, path, + (intmax_t)curpos); + free(readbuf); + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) { + close(localhandle); + } + return false; + } + + if (opt.dots) { + fputc('.', stderr); + } else if (!opt.quiet) { + print_progress(newpath, start_time, time_mono(NULL), + start_offset, curpos, + remotestat.st_size); + } + } + + free(readbuf); + + if (opt.dots) { + fputc('\n', stderr); + printf("%s downloaded\n", path); + } else if (!opt.quiet) { + int i; + fprintf(stderr, "\r%s", path); + if (columns) { + for (i = strlen(path); i < columns; i++) { + fputc(' ', stderr); + } + } + fputc('\n', stderr); + } + + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) { + close(localhandle); + } + return true; +} + +static void clean_exit(void) +{ + char bs[100]; + human_readable(total_bytes, bs, sizeof(bs)); + if (!opt.quiet) { + fprintf(stderr, "Downloaded %s in %lu seconds\n", bs, + (unsigned long)(time_mono(NULL) - total_start_time)); + } + exit(0); +} + +static void signal_quit(int v) +{ + clean_exit(); +} + +int main(int argc, char **argv) +{ + int c = 0; + const char *file = NULL; + int smb_encrypt = false; + int resume = 0, recursive = 0; + TALLOC_CTX *frame = talloc_stackframe(); + bool ok = false; + const char **argv_const = discard_const_p(const char *, argv); + struct poptOption long_options[] = { + POPT_AUTOHELP + + { + .longName = "guest", + .shortName = 'a', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'a', + .descrip = "Work as user guest" + }, + { + .longName = "encrypt", + .shortName = 'e', + .argInfo = POPT_ARG_NONE, + .arg = &smb_encrypt, + .val = 1, + .descrip = "Encrypt SMB transport" + }, + { + .longName = "resume", + .shortName = 'r', + .argInfo = POPT_ARG_NONE, + .arg = &resume, + .val = 1, + .descrip = "Automatically resume aborted files" + }, + { + .longName = "update", + .shortName = 'u', + .argInfo = POPT_ARG_NONE, + .arg = &opt.update, + .val = 1, + .descrip = "Download only when remote file is " + "newer than local file or local file " + "is missing" + }, + { + .longName = "recursive", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &recursive, + .val = true, + .descrip = "Recursively download files" + }, + { + .longName = "blocksize", + .shortName = 'b', + .argInfo = POPT_ARG_INT, + .arg = &opt.blocksize, + .val = 'b', + .descrip = "Change number of bytes in a block" + }, + + { + .longName = "outputfile", + .shortName = 'o', + .argInfo = POPT_ARG_STRING, + .arg = &opt.outputfile, + .val = 'o', + .descrip = "Write downloaded data to specified file" + }, + { + .longName = "stdout", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &opt.send_stdout, + .val = true, + .descrip = "Write data to stdout" + }, + { + .longName = "dots", + .shortName = 'D', + .argInfo = POPT_ARG_NONE, + .arg = &opt.dots, + .val = 1, + .descrip = "Show dots as progress indication" + }, + { + .longName = "quiet", + .shortName = 'q', + .argInfo = POPT_ARG_NONE, + .arg = &opt.quiet, + .val = 1, + .descrip = "Be quiet" + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &opt.verbose, + .val = 1, + .descrip = "Be verbose" + }, + { + .longName = "limit-rate", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &opt.limit_rate, + .val = 'l', + .descrip = "Limit download speed to this many KB/s" + }, + + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_LEGACY_S3 + POPT_COMMON_VERSION + POPT_TABLEEND + }; + poptContext pc = NULL; + struct cli_credentials *creds = NULL; + enum smb_encryption_setting encryption_state = SMB_ENCRYPTION_DEFAULT; + enum credentials_use_kerberos use_kerberos = CRED_USE_KERBEROS_DESIRED; + smbc_smb_encrypt_level encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT; +#if 0 + enum smb_signing_setting signing_state = SMB_SIGNING_DEFAULT; + const char *use_signing = "auto"; +#endif + bool is_nt_hash = false; + uint32_t gensec_features; + bool use_wbccache = false; + SMBCCTX *smb_ctx = NULL; + int dbg_lvl = -1; + int rc; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false); + if (!ok) { + goto done; + } + +#ifdef SIGWINCH + signal(SIGWINCH, change_columns); +#endif + signal(SIGINT, signal_quit); + signal(SIGTERM, signal_quit); + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + 0); + if (pc == NULL) { + ok = false; + goto done; + } + + creds = samba_cmdline_get_creds(); + + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) { + case 'a': + cli_credentials_set_anonymous(creds); + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(c)); + poptPrintUsage(pc, stderr, 0); + ok = false; + goto done; + } + + if (c < -1) { + fprintf(stderr, "%s: %s\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + ok = false; + goto done; + } + } + + if ((opt.send_stdout || resume || opt.outputfile) && opt.update) { + fprintf(stderr, "The -o, -R or -O and -U options can not be " + "used together.\n"); + ok = true; + goto done; + } + if ((opt.send_stdout || opt.outputfile) && recursive) { + fprintf(stderr, "The -o or -O and -R options can not be " + "used together.\n"); + ok = true; + goto done; + } + + if (opt.outputfile && opt.send_stdout) { + fprintf(stderr, "The -o and -O options can not be " + "used together.\n"); + ok = true; + goto done; + } + + samba_cmdline_burn(argc, argv); + + /* smbc_new_context() will set the log level to 0 */ + dbg_lvl = debuglevel_get(); + + smb_ctx = smbc_new_context(); + if (smb_ctx == NULL) { + fprintf(stderr, "Unable to initialize libsmbclient\n"); + ok = false; + goto done; + } + smbc_setDebug(smb_ctx, dbg_lvl); + + rc = smbc_setConfiguration(smb_ctx, lp_default_path()); + if (rc < 0) { + ok = false; + goto done; + } + + smbc_setFunctionAuthDataWithContext(smb_ctx, + get_auth_data_with_context_fn); + + ok = smbc_init_context(smb_ctx); + if (!ok) { + goto done; + } + smbc_set_context(smb_ctx); + + encryption_state = cli_credentials_get_smb_encryption(creds); + switch (encryption_state) { + case SMB_ENCRYPTION_REQUIRED: + encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE; + break; + case SMB_ENCRYPTION_DESIRED: + case SMB_ENCRYPTION_IF_REQUIRED: + encrypt_level = SMBC_ENCRYPTLEVEL_REQUEST; + break; + case SMB_ENCRYPTION_OFF: + encrypt_level = SMBC_ENCRYPTLEVEL_NONE; + break; + case SMB_ENCRYPTION_DEFAULT: + encrypt_level = SMBC_ENCRYPTLEVEL_DEFAULT; + break; + } + if (smb_encrypt) { + encrypt_level = SMBC_ENCRYPTLEVEL_REQUIRE; + } + smbc_setOptionSmbEncryptionLevel(smb_ctx, encrypt_level); + +#if 0 + signing_state = cli_credentials_get_smb_signing(creds); + if (encryption_state >= SMB_ENCRYPTION_DESIRED) { + signing_state = SMB_SIGNING_REQUIRED; + } + switch (signing_state) { + case SMB_SIGNING_REQUIRED: + use_signing = "required"; + break; + case SMB_SIGNING_DEFAULT: + case SMB_SIGNING_DESIRED: + case SMB_SIGNING_IF_REQUIRED: + use_signing = "yes"; + break; + case SMB_SIGNING_OFF: + use_signing = "off"; + break; + default: + use_signing = "auto"; + break; + } + /* FIXME: There is no libsmbclient function to set signing state */ +#endif + + use_kerberos = cli_credentials_get_kerberos_state(creds); + switch (use_kerberos) { + case CRED_USE_KERBEROS_REQUIRED: + smbc_setOptionUseKerberos(smb_ctx, true); + smbc_setOptionFallbackAfterKerberos(smb_ctx, false); + break; + case CRED_USE_KERBEROS_DESIRED: + smbc_setOptionUseKerberos(smb_ctx, true); + smbc_setOptionFallbackAfterKerberos(smb_ctx, true); + break; + case CRED_USE_KERBEROS_DISABLED: + smbc_setOptionUseKerberos(smb_ctx, false); + break; + } + + /* Check if the password supplied is an NT hash */ + is_nt_hash = cli_credentials_is_password_nt_hash(creds); + smbc_setOptionUseNTHash(smb_ctx, is_nt_hash); + + /* Check if we should use the winbind ccache */ + gensec_features = cli_credentials_get_gensec_features(creds); + use_wbccache = (gensec_features & GENSEC_FEATURE_NTLM_CCACHE); + smbc_setOptionUseCCache(smb_ctx, use_wbccache); + + columns = get_num_cols(); + + total_start_time = time_mono(NULL); + + while ((file = poptGetArg(pc))) { + if (!recursive) { + ok = smb_download_file(file, "", recursive, resume, + true, opt.outputfile); + } else { + ok = smb_download_dir(file, "", resume); + } + } + +done: + gfree_all(); + poptFreeContext(pc); + TALLOC_FREE(frame); + if (ok) { + clean_exit(); + } + return ok ? 0 : 1; +} diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c new file mode 100644 index 0000000..f2c5dfd --- /dev/null +++ b/source3/utils/smbpasswd.c @@ -0,0 +1,678 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Jeremy Allison 1995-1998 + * Copyright (C) Tim Potter 2001 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, see <http://www.gnu.org/licenses/>. */ + +#include "includes.h" +#include "system/passwd.h" +#include "secrets.h" +#include "../librpc/gen_ndr/samr.h" +#include "../lib/util/util_pw.h" +#include "libsmb/proto.h" +#include "passdb.h" +#include "cmdline_contexts.h" +#include "passwd_proto.h" +#include "lib/util/string_wrappers.h" +#include "lib/param/param.h" +#include "lib/util/memcache.h" + +/* + * Next two lines needed for SunOS and don't + * hurt anything else... + */ +extern char *optarg; +extern int optind; + +/* forced running in root-mode */ +static bool got_username = False; +static bool stdin_passwd_get = False; +static fstring user_name; +static char *new_passwd = NULL; +static const char *remote_machine = NULL; + +static fstring ldap_secret; + + +/********************************************************* + Print command usage on stderr and die. +**********************************************************/ +static void usage(void) +{ + printf("When run by root:\n"); + printf(" smbpasswd [options] [username]\n"); + printf("otherwise:\n"); + printf(" smbpasswd [options]\n\n"); + + printf("options:\n"); + printf(" -L local mode (must be first option)\n"); + printf(" -h print this usage message\n"); + printf(" -s use stdin for password prompt\n"); + printf(" -c smb.conf file Use the given path to the smb.conf file\n"); + printf(" -D LEVEL debug level\n"); + printf(" -r MACHINE remote machine\n"); + printf(" -U USER remote username (e.g. SAM/user)\n"); + + printf("extra options when run by root or in local mode:\n"); + printf(" -a add user\n"); + printf(" -d disable user\n"); + printf(" -e enable user\n"); + printf(" -i interdomain trust account\n"); + printf(" -m machine trust account\n"); + printf(" -n set no password\n"); + printf(" -W use stdin ldap admin password\n"); + printf(" -w PASSWORD ldap admin password\n"); + printf(" -x delete user\n"); + printf(" -R ORDER name resolve order\n"); + + exit(1); +} + +static void set_line_buffering(FILE *f) +{ + setvbuf(f, NULL, _IOLBF, 0); +} + +/******************************************************************* + Process command line options + ******************************************************************/ + +static int process_options(int argc, char **argv, int local_flags, + struct loadparm_context *lp_ctx) +{ + int ch; + const char *configfile = get_dyn_CONFIGFILE(); + + local_flags |= LOCAL_SET_PASSWORD; + + ZERO_STRUCT(user_name); + + user_name[0] = '\0'; + + while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:LWS:")) != EOF) { + switch(ch) { + case 'L': + if (getuid() != 0) { + fprintf(stderr, "smbpasswd -L can only be used by root.\n"); + exit(1); + } + local_flags |= LOCAL_AM_ROOT; + break; + case 'c': + configfile = optarg; + set_dyn_CONFIGFILE(optarg); + break; + case 'a': + local_flags |= LOCAL_ADD_USER; + break; + case 'x': + local_flags |= LOCAL_DELETE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'd': + local_flags |= LOCAL_DISABLE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'e': + local_flags |= LOCAL_ENABLE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'm': + local_flags |= LOCAL_TRUST_ACCOUNT; + break; + case 'i': + local_flags |= LOCAL_INTERDOM_ACCOUNT; + break; + case 'j': + d_printf("See 'net join' for this functionality\n"); + exit(1); + break; + case 'n': + local_flags |= LOCAL_SET_NO_PASSWORD; + local_flags &= ~LOCAL_SET_PASSWORD; + SAFE_FREE(new_passwd); + new_passwd = smb_xstrdup("NO PASSWORD"); + break; + case 'r': + remote_machine = optarg; + break; + case 's': + set_line_buffering(stdin); + set_line_buffering(stdout); + set_line_buffering(stderr); + stdin_passwd_get = True; + break; + case 'w': + local_flags |= LOCAL_SET_LDAP_ADMIN_PW; + fstrcpy(ldap_secret, optarg); + break; + case 'R': + lpcfg_set_cmdline(lp_ctx, "name resolve order", optarg); + break; + case 'D': + lpcfg_set_cmdline(lp_ctx, "log level", optarg); + break; + case 'U': { + got_username = True; + fstrcpy(user_name, optarg); + break; + case 'W': + local_flags |= LOCAL_SET_LDAP_ADMIN_PW; + *ldap_secret = '\0'; + break; + } + case 'h': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (!got_username) + fstrcpy(user_name, ""); + break; + case 1: + if (!(local_flags & LOCAL_AM_ROOT)) { + usage(); + } else { + if (got_username) { + usage(); + } else { + fstrcpy(user_name, argv[0]); + } + } + break; + default: + usage(); + } + + if (!lp_load_global(configfile)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", + configfile); + exit(1); + } + + return local_flags; +} + +/************************************************************* + Utility function to prompt for new password. +*************************************************************/ +static char *prompt_for_new_password(bool stdin_get) +{ + char *p; + fstring new_pw; + + ZERO_ARRAY(new_pw); + + p = get_pass("New SMB password:", stdin_get); + if (p == NULL) { + return NULL; + } + + fstrcpy(new_pw, p); + SAFE_FREE(p); + + p = get_pass("Retype new SMB password:", stdin_get); + if (p == NULL) { + return NULL; + } + + if (strcmp(p, new_pw)) { + fprintf(stderr, "Mismatch - password unchanged.\n"); + ZERO_ARRAY(new_pw); + SAFE_FREE(p); + return NULL; + } + + return p; +} + + +/************************************************************* + Change a password either locally or remotely. +*************************************************************/ + +static NTSTATUS password_change(const char *remote_mach, + const char *domain, const char *username, + const char *old_passwd, const char *new_pw, + int local_flags) +{ + NTSTATUS ret; + char *err_str = NULL; + char *msg_str = NULL; + + if (remote_mach != NULL) { + if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER| + LOCAL_DISABLE_USER|LOCAL_ENABLE_USER| + LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) { + /* these things can't be done remotely yet */ + fprintf(stderr, "Invalid remote operation!\n"); + return NT_STATUS_UNSUCCESSFUL; + } + ret = remote_password_change(remote_mach, + domain, username, + old_passwd, new_pw, &err_str); + } else { + ret = local_password_change(username, local_flags, new_pw, + &err_str, &msg_str); + } + + if (msg_str) { + printf("%s", msg_str); + } + if (err_str) { + fprintf(stderr, "%s", err_str); + } + if (!NT_STATUS_IS_OK(ret) && !err_str) { + fprintf(stderr, "Failed to change password!\n"); + } + + SAFE_FREE(msg_str); + SAFE_FREE(err_str); + return ret; +} + +/******************************************************************* + Store the LDAP admin password in secrets.tdb + ******************************************************************/ +static bool store_ldap_admin_pw (char* pw) +{ + if (!pw) + return False; + + if (!secrets_init()) + return False; + + return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw); +} + + +/************************************************************* + Handle password changing for root. +*************************************************************/ + +static int process_root(int local_flags) +{ + struct passwd *pwd; + int result = 0; + char *old_passwd = NULL; + + if (local_flags & LOCAL_SET_LDAP_ADMIN_PW) { + const char *ldap_admin_dn = lp_ldap_admin_dn(); + if ( ! *ldap_admin_dn ) { + DEBUG(0,("ERROR: 'ldap admin dn' not defined! Please check your smb.conf\n")); + goto done; + } + + printf("Setting stored password for \"%s\" in secrets.tdb\n", ldap_admin_dn); + if ( ! *ldap_secret ) { + new_passwd = prompt_for_new_password(stdin_passwd_get); + if (new_passwd == NULL) { + fprintf(stderr, "Failed to read new password!\n"); + exit(1); + } + fstrcpy(ldap_secret, new_passwd); + } + if (!store_ldap_admin_pw(ldap_secret)) { + DEBUG(0,("ERROR: Failed to store the ldap admin password!\n")); + } + goto done; + } + + /* Ensure passdb startup(). */ + if(!initialize_password_db(False, NULL)) { + DEBUG(0, ("Failed to open passdb!\n")); + exit(1); + } + + /* Ensure we have a SAM sid. */ + get_global_sam_sid(); + + /* + * Ensure both add/delete user are not set + * Ensure add/delete user and either remote machine or join domain are + * not both set. + */ + if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) || + ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) && + (remote_machine != NULL))) { + usage(); + } + + /* Only load interfaces if we are doing network operations. */ + + if (remote_machine) { + load_interfaces(); + } + + if (!user_name[0] && (pwd = getpwuid_alloc(talloc_tos(), geteuid()))) { + fstrcpy(user_name, pwd->pw_name); + TALLOC_FREE(pwd); + } + + if (!user_name[0]) { + fprintf(stderr,"You must specify a username\n"); + exit(1); + } + + if (local_flags & LOCAL_TRUST_ACCOUNT) { + /* add the $ automatically */ + size_t user_name_len = strlen(user_name); + + if (user_name[user_name_len - 1] == '$') { + user_name_len--; + } else { + if (user_name_len + 2 > sizeof(user_name)) { + fprintf(stderr, "machine name too long\n"); + exit(1); + } + user_name[user_name_len] = '$'; + user_name[user_name_len + 1] = '\0'; + } + + if (local_flags & LOCAL_ADD_USER) { + SAFE_FREE(new_passwd); + + /* + * Remove any trailing '$' before we + * generate the initial machine password. + */ + new_passwd = smb_xstrndup(user_name, user_name_len); + if (!strlower_m(new_passwd)) { + fprintf(stderr, "strlower_m %s failed\n", + new_passwd); + exit(1); + } + } + } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) { + size_t user_name_len = strlen(user_name); + + if (user_name[user_name_len - 1] != '$') { + if (user_name_len + 2 > sizeof(user_name)) { + fprintf(stderr, "machine name too long\n"); + exit(1); + } + user_name[user_name_len] = '$'; + user_name[user_name_len + 1] = '\0'; + } + + if ((local_flags & LOCAL_ADD_USER) && (new_passwd == NULL)) { + /* + * Prompt for trusting domain's account password + */ + new_passwd = prompt_for_new_password(stdin_passwd_get); + if(!new_passwd) { + fprintf(stderr, "Unable to get newpassword.\n"); + exit(1); + } + } + } else { + + if (remote_machine != NULL) { + old_passwd = get_pass("Old SMB password:",stdin_passwd_get); + if(!old_passwd) { + fprintf(stderr, "Unable to get old password.\n"); + exit(1); + } + } + + if (!(local_flags & LOCAL_SET_PASSWORD)) { + + /* + * If we are trying to enable a user, first we need to find out + * if they are using a modern version of the smbpasswd file that + * disables a user by just writing a flag into the file. If so + * then we can re-enable a user without prompting for a new + * password. If not (ie. they have a no stored password in the + * smbpasswd file) then we need to prompt for a new password. + */ + + if(local_flags & LOCAL_ENABLE_USER) { + struct samu *sampass = NULL; + + sampass = samu_new( NULL ); + if (!sampass) { + fprintf(stderr, "talloc fail for struct samu.\n"); + exit(1); + } + if (!pdb_getsampwnam(sampass, user_name)) { + fprintf(stderr, "Failed to find user %s in passdb backend.\n", + user_name ); + exit(1); + } + + if(pdb_get_nt_passwd(sampass) == NULL) { + local_flags |= LOCAL_SET_PASSWORD; + } + TALLOC_FREE(sampass); + } + } + + if((local_flags & LOCAL_SET_PASSWORD) && (new_passwd == NULL)) { + + new_passwd = prompt_for_new_password(stdin_passwd_get); + if(!new_passwd) { + fprintf(stderr, "Unable to get new password.\n"); + exit(1); + } + } + } + + if (!NT_STATUS_IS_OK(password_change(remote_machine, + NULL, user_name, + old_passwd, new_passwd, + local_flags))) { + result = 1; + goto done; + } + + if(remote_machine) { + printf("Password changed for user %s on %s.\n", user_name, remote_machine ); + } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) { + struct samu *sampass = NULL; + + sampass = samu_new( NULL ); + if (!sampass) { + fprintf(stderr, "talloc fail for struct samu.\n"); + exit(1); + } + + if (!pdb_getsampwnam(sampass, user_name)) { + fprintf(stderr, "Failed to find user %s in passdb backend.\n", + user_name ); + exit(1); + } + + printf("Password changed for user %s.", user_name ); + if(pdb_get_acct_ctrl(sampass)&ACB_DISABLED) { + printf(" User has disabled flag set."); + } + if(pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) { + printf(" User has no password flag set."); + } + printf("\n"); + TALLOC_FREE(sampass); + } + + done: + SAFE_FREE(old_passwd); + SAFE_FREE(new_passwd); + return result; +} + + +/************************************************************* + Handle password changing for non-root. +*************************************************************/ + +static int process_nonroot(int local_flags) +{ + struct passwd *pwd = NULL; + int result = 0; + char *old_pw = NULL; + char *new_pw = NULL; + const char *username = user_name; + const char *domain = NULL; + char *p = NULL; + + if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) { + /* Extra flags that we can't honor non-root */ + usage(); + } + + if (!user_name[0]) { + pwd = getpwuid_alloc(talloc_tos(), getuid()); + if (pwd) { + fstrcpy(user_name,pwd->pw_name); + TALLOC_FREE(pwd); + } else { + fprintf(stderr, "smbpasswd: cannot lookup user name for uid %u\n", (unsigned int)getuid()); + exit(1); + } + } + + /* Allow domain as part of the username */ + if ((p = strchr_m(user_name, '\\')) || + (p = strchr_m(user_name, '/')) || + (p = strchr_m(user_name, *lp_winbind_separator()))) { + *p = '\0'; + username = p + 1; + domain = user_name; + } + + /* + * A non-root user is always setting a password + * via a remote machine (even if that machine is + * localhost). + */ + + load_interfaces(); /* Delayed from main() */ + + if (remote_machine != NULL) { + if (!is_ipaddress(remote_machine)) { + domain = remote_machine; + } + } else { + remote_machine = "127.0.0.1"; + + /* + * If we deal with a local user, change the password for the + * user in our SAM. + */ + domain = get_global_sam_name(); + } + + old_pw = get_pass("Old SMB password:",stdin_passwd_get); + if (old_pw == NULL) { + fprintf(stderr, "Unable to get old password.\n"); + exit(1); + } + + if (!new_passwd) { + new_pw = prompt_for_new_password(stdin_passwd_get); + } + else + new_pw = smb_xstrdup(new_passwd); + + if (!new_pw) { + fprintf(stderr, "Unable to get new password.\n"); + exit(1); + } + + if (!NT_STATUS_IS_OK(password_change(remote_machine, + domain, username, + old_pw, new_pw, 0))) { + result = 1; + goto done; + } + + printf("Password changed for user %s\n", username); + + done: + SAFE_FREE(old_pw); + SAFE_FREE(new_pw); + + return result; +} + + + +/********************************************************* + Start here. +**********************************************************/ +int main(int argc, char **argv) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + struct memcache *mcache = NULL; + int local_flags = 0; + int ret; + + mcache = memcache_init(NULL, 0); + if (mcache == NULL) { + fprintf(stderr, "%s: memcache_init failed\n", __location__); + return 1; + } + memcache_set_global(mcache); + +#if defined(HAVE_SET_AUTH_PARAMETERS) + set_auth_parameters(argc, argv); +#endif /* HAVE_SET_AUTH_PARAMETERS */ + + if (getuid() == 0) { + local_flags = LOCAL_AM_ROOT; + } + + smb_init_locale(); + + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers()); + if (lp_ctx == NULL) { + fprintf(stderr, + "Failed to initialise the global parameter structure.\n"); + return 1; + } + + local_flags = process_options(argc, argv, local_flags, lp_ctx); + + setup_logging("smbpasswd", DEBUG_STDERR); + + /* Check the effective uid - make sure we are not setuid */ + if (is_setuid_root()) { + fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n"); + exit(1); + } + + if (local_flags & LOCAL_AM_ROOT) { + bool ok; + + ok = secrets_init(); + if (!ok) { + return 1; + } + ret = process_root(local_flags); + } else { + ret = process_nonroot(local_flags); + } + + gfree_all(); + + TALLOC_FREE(frame); + return ret; +} diff --git a/source3/utils/smbtree.c b/source3/utils/smbtree.c new file mode 100644 index 0000000..80dfa0a --- /dev/null +++ b/source3/utils/smbtree.c @@ -0,0 +1,295 @@ +/* + Unix SMB/CIFS implementation. + Network neighbourhood browser. + + Copyright (C) Tim Potter 2000 + Copyright (C) Jelmer Vernooij 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/cmdline/cmdline.h" +#include "rpc_client/cli_pipe.h" +#include "../librpc/gen_ndr/ndr_srvsvc_c.h" +#include "libsmb/libsmb.h" +#include "libsmb/namequery.h" +#include "libsmb/clirap.h" +#include "../libcli/smb/smbXcli_base.h" +#include "nameserv.h" +#include "libsmbclient.h" + +/* How low can we go? */ + +enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE}; +static enum tree_level level = LEV_SHARE; + +static void get_auth_data_with_context_fn( + SMBCCTX *context, + const char *server, + const char *share, + char *domain, + int domain_len, + char *user, + int user_len, + char *password, + int password_len) +{ + struct cli_credentials *creds = samba_cmdline_get_creds(); + size_t len; + + len = strlcpy(domain, cli_credentials_get_domain(creds), domain_len); + if ((int)len >= domain_len) { + return; + } + len = strlcpy( + user, cli_credentials_get_username(creds), user_len); + if ((int)len >= user_len) { + return; + } + len = strlcpy( + password, cli_credentials_get_password(creds), password_len); + if ((int)len >= password_len) { + /* pointless, but what can you do... */ + return; + } +} + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc, char *argv[]) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char **argv_const = discard_const_p(const char *, argv); + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "domains", + .shortName = 'D', + .argInfo = POPT_ARG_VAL, + .arg = &level, + .val = LEV_WORKGROUP, + .descrip = "List only domains (workgroups) of tree" , + }, + { + .longName = "servers", + .shortName = 'S', + .argInfo = POPT_ARG_VAL, + .arg = &level, + .val = LEV_SERVER, + .descrip = "List domains(workgroups) and servers of tree" , + }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_TABLEEND + }; + poptContext pc; + SMBCCTX *ctx = NULL; + SMBCFILE *workgroups = NULL; + struct smbc_dirent *dirent = NULL; + bool ok; + int ret, result = 1; + int opt; + int debuglevel; + + /* Initialise samba stuff */ + smb_init_locale(); + + setlinebuf(stdout); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv_const, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + samba_cmdline_burn(argc, argv); + + debuglevel = DEBUGLEVEL; + + ctx = smbc_new_context(); + if (ctx == NULL) { + perror("smbc_new_context"); + goto fail; + } + ret = smbc_setConfiguration(ctx, get_dyn_CONFIGFILE()); + if (ret == -1) { + perror("smbc_setConfiguration"); + goto fail; + } + smbc_setDebug(ctx, debuglevel); + ok = smbc_setOptionProtocols(ctx, NULL, "NT1"); + if (!ok) { + perror("smbc_setOptionProtocols"); + goto fail; + } + smbc_setFunctionAuthDataWithContext( + ctx, get_auth_data_with_context_fn); + + ok = smbc_init_context(ctx); + if (!ok) { + perror("smbc_init_context"); + goto fail; + } + + workgroups = smbc_getFunctionOpendir(ctx)(ctx, "smb://"); + if (workgroups == NULL) { + DBG_ERR("This is utility doesn't work if netbios name " + "resolution is not configured.\n" + "If you are using SMB2 or SMB3, network browsing uses " + "WSD/LLMNR, which is not yet supported by Samba. SMB1 " + "is disabled by default on the latest Windows versions " + "for security reasons. It is still possible to access " + "the Samba resources directly via \\name or " + "\\ip.address.\n"); + goto fail; + } + + while ((dirent = smbc_getFunctionReaddir(ctx)(ctx, workgroups)) + != NULL) { + char *url = NULL; + SMBCFILE *servers = NULL; + + if (dirent->smbc_type != SMBC_WORKGROUP) { + continue; + } + + printf("%s\n", dirent->name); + + if (level == LEV_WORKGROUP) { + continue; + } + + url = talloc_asprintf( + talloc_tos(), "smb://%s/", dirent->name); + if (url == NULL) { + perror("talloc_asprintf"); + goto fail; + } + + servers = smbc_getFunctionOpendir(ctx)(ctx, url); + if (servers == NULL) { + perror("smbc_opendir"); + goto fail; + } + TALLOC_FREE(url); + + while ((dirent = smbc_getFunctionReaddir(ctx)(ctx, servers)) + != NULL) { + SMBCFILE *shares = NULL; + char *servername = NULL; + + if (dirent->smbc_type != SMBC_SERVER) { + continue; + } + + printf("\t\\\\%-15s\t\t%s\n", + dirent->name, + dirent->comment); + + if (level == LEV_SERVER) { + continue; + } + + /* + * The subsequent readdir for shares will + * overwrite the "server" readdir + */ + servername = talloc_strdup(talloc_tos(), dirent->name); + if (servername == NULL) { + continue; + } + + url = talloc_asprintf( + talloc_tos(), "smb://%s/", servername); + if (url == NULL) { + perror("talloc_asprintf"); + goto fail; + } + + shares = smbc_getFunctionOpendir(ctx)(ctx, url); + if (shares == NULL) { + perror("smbc_opendir"); + goto fail; + } + + while ((dirent = smbc_getFunctionReaddir( + ctx)(ctx, shares)) + != NULL) { + printf("\t\t\\\\%s\\%-15s\t%s\n", + servername, + dirent->name, + dirent->comment); + } + + ret = smbc_getFunctionClosedir(ctx)(ctx, shares); + if (ret == -1) { + perror("smbc_closedir"); + goto fail; + } + + TALLOC_FREE(servername); + TALLOC_FREE(url); + } + + ret = smbc_getFunctionClosedir(ctx)(ctx, servers); + if (ret == -1) { + perror("smbc_closedir"); + goto fail; + } + } + + ret = smbc_getFunctionClosedir(ctx)(ctx, workgroups); + if (ret == -1) { + perror("smbc_closedir"); + goto fail; + } + + result = 0; +fail: + if (ctx != NULL) { + smbc_free_context(ctx, 0); + ctx = NULL; + } + gfree_all(); + poptFreeContext(pc); + TALLOC_FREE(frame); + return result; +} diff --git a/source3/utils/status.c b/source3/utils/status.c new file mode 100644 index 0000000..4102b41 --- /dev/null +++ b/source3/utils/status.c @@ -0,0 +1,1241 @@ +/* + Unix SMB/CIFS implementation. + status reporting + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + Revision History: + + 12 aug 96: Erik.Devriendt@te6.siemens.be + added support for shared memory implementation of share mode locking + + 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe) + Added -L (locks only) -S (shares only) flags and code + +*/ + +/* + * This program reports current SMB connections + */ + +#include "includes.h" +#include "lib/util/server_id.h" +#include "smbd/globals.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "dbwrap/dbwrap.h" +#include "dbwrap/dbwrap_open.h" +#include "../libcli/security/security.h" +#include "session.h" +#include "locking/share_mode_lock.h" +#include "locking/proto.h" +#include "messages.h" +#include "librpc/gen_ndr/open_files.h" +#include "smbd/smbd.h" +#include "librpc/gen_ndr/notify.h" +#include "conn_tdb.h" +#include "serverid.h" +#include "status_profile.h" +#include "status.h" +#include "status_json.h" +#include "smbd/notifyd/notifyd_db.h" +#include "cmdline_contexts.h" +#include "locking/leases_db.h" +#include "lib/util/string_wrappers.h" +#include "lib/param/param.h" + +#ifdef HAVE_JANSSON +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#include "auth/common_auth.h" +#endif /* HAVE_JANSSON */ + +#define SMB_MAXPIDS 2048 +static uid_t Ucrit_uid = 0; /* added by OH */ +static struct server_id Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */ +static int Ucrit_MaxPid=0; /* added by OH */ +static unsigned int Ucrit_IsActive = 0; /* added by OH */ + +static bool verbose, brief; +static bool shares_only; /* Added by RJS */ +static bool locks_only; /* Added by RJS */ +static bool processes_only; +static bool show_brl; +static bool numeric_only; +static bool do_checks = true; + +const char *username = NULL; + +/* added by OH */ +static void Ucrit_addUid(uid_t uid) +{ + Ucrit_uid = uid; + Ucrit_IsActive = 1; +} + +static unsigned int Ucrit_checkUid(uid_t uid) +{ + if ( !Ucrit_IsActive ) + return 1; + + if ( uid == Ucrit_uid ) + return 1; + + return 0; +} + +static unsigned int Ucrit_checkPid(struct server_id pid) +{ + int i; + + if ( !Ucrit_IsActive ) + return 1; + + for (i=0;i<Ucrit_MaxPid;i++) { + if (server_id_equal(&pid, &Ucrit_pid[i])) { + return 1; + } + } + + return 0; +} + +static bool Ucrit_addPid( struct server_id pid ) +{ + if ( !Ucrit_IsActive ) + return True; + + if ( Ucrit_MaxPid >= SMB_MAXPIDS ) { + fprintf(stderr, "ERROR: More than %d pids for user %s!\n", + SMB_MAXPIDS, uidtoname(Ucrit_uid)); + + return False; + } + + Ucrit_pid[Ucrit_MaxPid++] = pid; + + return True; +} + +static int print_share_mode_stdout(struct traverse_state *state, + const char *pid, + const char *user_name, + const char *denymode, + int access_mask, + const char *rw, + const char *oplock, + const char *servicepath, + const char *filename, + const char *timestr) +{ + if (state->first) { + d_printf("\nLocked files:\n"); + d_printf("Pid User(ID) DenyMode Access R/W Oplock SharePath Name Time\n"); + d_printf("--------------------------------------------------------------------------------------------------\n"); + + state->first = false; + } + + d_printf("%-11s %-9s %-10s 0x%-8x %-10s %-14s %s %s %s", + pid, user_name, denymode, access_mask, rw, oplock, + servicepath, filename, timestr); + return 0; +} + +static int prepare_share_mode(struct traverse_state *state) +{ + if (!state->json_output) { + /* only print header line if there are open files */ + state->first = true; + } else { + add_section_to_json(state, "open_files"); + } + return 0; +} + +static uint32_t map_share_mode_to_deny_mode( + uint32_t share_access, uint32_t private_options) +{ + switch (share_access & ~FILE_SHARE_DELETE) { + case FILE_SHARE_NONE: + return DENY_ALL; + case FILE_SHARE_READ: + return DENY_WRITE; + case FILE_SHARE_WRITE: + return DENY_READ; + case FILE_SHARE_READ|FILE_SHARE_WRITE: + return DENY_NONE; + } + if (private_options & NTCREATEX_FLAG_DENY_DOS) { + return DENY_DOS; + } else if (private_options & NTCREATEX_FLAG_DENY_FCB) { + return DENY_FCB; + } + + return (uint32_t)-1; +} + +static int print_share_mode(struct file_id fid, + const struct share_mode_data *d, + const struct share_mode_entry *e, + void *private_data) +{ + const char *denymode = NULL; + uint denymode_int; + const char *oplock = NULL; + const char *pid = NULL; + const char *rw = NULL; + const char *filename = NULL; + const char *timestr = NULL; + const char *user_str = NULL; + uint32_t lstate; + struct traverse_state *state = (struct traverse_state *)private_data; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + if (do_checks && !is_valid_share_mode_entry(e)) { + TALLOC_FREE(tmp_ctx); + return 0; + } + + if (do_checks && !serverid_exists(&e->pid)) { + /* the process for this entry does not exist any more */ + TALLOC_FREE(tmp_ctx); + return 0; + } + + if (Ucrit_checkPid(e->pid)) { + struct server_id_buf tmp; + pid = server_id_str_buf(e->pid, &tmp); + if (state->resolve_uids) { + user_str = talloc_asprintf(tmp_ctx, "%s", uidtoname(e->uid)); + } else { + user_str = talloc_asprintf(tmp_ctx, "%u", (unsigned int)e->uid); + } + if (user_str == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + + denymode_int = map_share_mode_to_deny_mode(e->share_access, + e->private_options); + switch (denymode_int) { + case DENY_NONE: + denymode = "DENY_NONE"; + break; + case DENY_ALL: + denymode = "DENY_ALL"; + break; + case DENY_DOS: + denymode = "DENY_DOS"; + break; + case DENY_READ: + denymode = "DENY_READ"; + break; + case DENY_WRITE: + denymode = "DENY_WRITE"; + break; + case DENY_FCB: + denymode = "DENY_FCB"; + break; + default: { + denymode = talloc_asprintf(tmp_ctx, + "UNKNOWN(0x%08x)", + denymode_int); + if (denymode == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + fprintf(stderr, + "unknown-please report ! " + "e->share_access = 0x%x, " + "e->private_options = 0x%x\n", + (unsigned int)e->share_access, + (unsigned int)e->private_options); + break; + } + } + filename = talloc_asprintf(tmp_ctx, + "%s%s", + d->base_name, + (d->stream_name != NULL) ? d->stream_name : ""); + if (filename == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + if ((e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))== + (FILE_READ_DATA|FILE_WRITE_DATA)) { + rw = "RDWR"; + } else if (e->access_mask & FILE_WRITE_DATA) { + rw = "WRONLY"; + } else { + rw = "RDONLY"; + } + + if (e->op_type & BATCH_OPLOCK) { + oplock = "BATCH"; + } else if (e->op_type & EXCLUSIVE_OPLOCK) { + oplock = "EXCLUSIVE"; + } else if (e->op_type & LEVEL_II_OPLOCK) { + oplock = "LEVEL_II"; + } else if (e->op_type == LEASE_OPLOCK) { + NTSTATUS status; + + status = leases_db_get( + &e->client_guid, + &e->lease_key, + &d->id, + &lstate, /* current_state */ + NULL, /* breaking */ + NULL, /* breaking_to_requested */ + NULL, /* breaking_to_required */ + NULL, /* lease_version */ + NULL); /* epoch */ + + if (NT_STATUS_IS_OK(status)) { + oplock = talloc_asprintf(tmp_ctx, "LEASE(%s%s%s)%s%s%s", + (lstate & SMB2_LEASE_READ)?"R":"", + (lstate & SMB2_LEASE_WRITE)?"W":"", + (lstate & SMB2_LEASE_HANDLE)?"H":"", + (lstate & SMB2_LEASE_READ)?"":" ", + (lstate & SMB2_LEASE_WRITE)?"":" ", + (lstate & SMB2_LEASE_HANDLE)?"":" "); + } else { + oplock = "LEASE STATE UNKNOWN"; + } + } else { + oplock = "NONE"; + } + + timestr = time_to_asc((time_t)e->time.tv_sec); + + if (!state->json_output) { + print_share_mode_stdout(state, + pid, + user_str, + denymode, + (unsigned int)e->access_mask, + rw, + oplock, + d->servicepath, + filename, + timestr); + } else { + print_share_mode_json(state, + d, + e, + fid, + user_str, + oplock, + lstate, + filename); + } + } + TALLOC_FREE(tmp_ctx); + return 0; +} + +static void print_brl_stdout(struct traverse_state *state, + char *pid, + char *id, + const char *desc, + intmax_t start, + intmax_t size, + const char *sharepath, + char *fname) +{ + if (state->first) { + d_printf("Byte range locks:\n"); + d_printf("Pid dev:inode R/W start size SharePath Name\n"); + d_printf("--------------------------------------------------------------------------------\n"); + + state->first = false; + } + d_printf("%-10s %-15s %-4s %-9jd %-9jd %-24s %-24s\n", + pid, id, desc, start, size, sharepath, fname); +} + +static int prepare_brl(struct traverse_state *state) +{ + if (!state->json_output) { + /* only print header line if there are locked files */ + state->first = true; + } else { + add_section_to_json(state, "byte_range_locks"); + } + return 0; +} + +static void print_brl(struct file_id id, + struct server_id pid, + enum brl_type lock_type, + enum brl_flavour lock_flav, + br_off start, + br_off size, + void *private_data) +{ + unsigned int i; + static const struct { + enum brl_type lock_type; + const char *desc; + } lock_types[] = { + { READ_LOCK, "R" }, + { WRITE_LOCK, "W" }, + { UNLOCK_LOCK, "U" } + }; + const char *desc="X"; + const char *sharepath = ""; + char *fname = NULL; + struct share_mode_lock *share_mode; + struct server_id_buf tmp; + struct file_id_buf ftmp; + struct traverse_state *state = (struct traverse_state *)private_data; + + share_mode = fetch_share_mode_unlocked(NULL, id); + if (share_mode) { + fname = share_mode_filename(NULL, share_mode); + sharepath = share_mode_servicepath(share_mode); + } else { + fname = talloc_strdup(NULL, ""); + if (fname == NULL) { + return; + } + } + + for (i=0;i<ARRAY_SIZE(lock_types);i++) { + if (lock_type == lock_types[i].lock_type) { + desc = lock_types[i].desc; + } + } + + if (!state->json_output) { + print_brl_stdout(state, + server_id_str_buf(pid, &tmp), + file_id_str_buf(id, &ftmp), + desc, + (intmax_t)start, + (intmax_t)size, + sharepath, + fname); + } else { + print_brl_json(state, + pid, + id, + desc, + lock_flav, + (intmax_t)start, + (intmax_t)size, + sharepath, + fname); + + } + + TALLOC_FREE(fname); + TALLOC_FREE(share_mode); +} + +static const char *session_dialect_str(uint16_t dialect) +{ + static fstring unknown_dialect; + + switch(dialect){ + case SMB2_DIALECT_REVISION_000: + return "NT1"; + case SMB2_DIALECT_REVISION_202: + return "SMB2_02"; + case SMB2_DIALECT_REVISION_210: + return "SMB2_10"; + case SMB2_DIALECT_REVISION_222: + return "SMB2_22"; + case SMB2_DIALECT_REVISION_224: + return "SMB2_24"; + case SMB3_DIALECT_REVISION_300: + return "SMB3_00"; + case SMB3_DIALECT_REVISION_302: + return "SMB3_02"; + case SMB3_DIALECT_REVISION_310: + return "SMB3_10"; + case SMB3_DIALECT_REVISION_311: + return "SMB3_11"; + } + + fstr_sprintf(unknown_dialect, "Unknown (0x%04x)", dialect); + return unknown_dialect; +} + +static int traverse_connections_stdout(struct traverse_state *state, + const char *servicename, + char *server_id, + const char *machine, + const char *timestr, + const char *encryption, + const char *signing) +{ + d_printf("%-12s %-7s %-13s %-32s %-12s %-12s\n", + servicename, server_id, machine, timestr, encryption, signing); + + return 0; +} + +static int prepare_connections(struct traverse_state *state) +{ + if (!state->json_output) { + /* always print header line */ + d_printf("\n%-12s %-7s %-13s %-32s %-12s %-12s\n", "Service", "pid", "Machine", "Connected at", "Encryption", "Signing"); + d_printf("---------------------------------------------------------------------------------------------\n"); + } else { + add_section_to_json(state, "tcons"); + } + return 0; +} + +static int traverse_connections(const struct connections_data *crec, + void *private_data) +{ + struct server_id_buf tmp; + char *timestr = NULL; + int result = 0; + const char *encryption = "-"; + enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE; + const char *signing = "-"; + enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE; + struct traverse_state *state = (struct traverse_state *)private_data; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + if (crec->cnum == TID_FIELD_INVALID) { + TALLOC_FREE(tmp_ctx); + return 0; + } + + if (do_checks && + (!process_exists(crec->pid) || !Ucrit_checkUid(crec->uid))) { + TALLOC_FREE(tmp_ctx); + return 0; + } + + timestr = timestring(tmp_ctx, nt_time_to_unix(crec->start)); + if (timestr == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (smbXsrv_is_encrypted(crec->encryption_flags)) { + switch (crec->cipher) { + case SMB_ENCRYPTION_GSSAPI: + encryption = "GSSAPI"; + break; + case SMB2_ENCRYPTION_AES128_CCM: + encryption = "AES-128-CCM"; + break; + case SMB2_ENCRYPTION_AES128_GCM: + encryption = "AES-128-GCM"; + break; + default: + encryption = "???"; + break; + } + encryption_degree = CRYPTO_DEGREE_FULL; + } + + if (smbXsrv_is_signed(crec->signing_flags)) { + switch (crec->signing) { + case SMB2_SIGNING_MD5_SMB1: + signing = "HMAC-MD5"; + break; + case SMB2_SIGNING_HMAC_SHA256: + signing = "HMAC-SHA256"; + break; + case SMB2_SIGNING_AES128_CMAC: + signing = "AES-128-CMAC"; + break; + case SMB2_SIGNING_AES128_GMAC: + signing = "AES-128-GMAC"; + break; + default: + signing = "???"; + break; + } + signing_degree = CRYPTO_DEGREE_FULL; + } + + if (!state->json_output) { + result = traverse_connections_stdout(state, + crec->servicename, + server_id_str_buf(crec->pid, &tmp), + crec->machine, + timestr, + encryption, + signing); + } else { + result = traverse_connections_json(state, + crec, + encryption, + encryption_degree, + signing, + signing_degree); + } + + TALLOC_FREE(timestr); + TALLOC_FREE(tmp_ctx); + + return result; +} + +static int traverse_sessionid_stdout(struct traverse_state *state, + char *server_id, + char *uid_gid_str, + char *machine_hostname, + const char *dialect, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree) +{ + fstring encryption; + fstring signing; + + if (encryption_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(encryption, "%s", encryption_cipher); + } else if (encryption_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(encryption, "partial(%s)", encryption_cipher); + } else { + fstr_sprintf(encryption, "-"); + } + if (signing_degree == CRYPTO_DEGREE_FULL) { + fstr_sprintf(signing, "%s", signing_cipher); + } else if (signing_degree == CRYPTO_DEGREE_PARTIAL) { + fstr_sprintf(signing, "partial(%s)", signing_cipher); + } else { + fstr_sprintf(signing, "-"); + } + + d_printf("%-7s %-25s %-41s %-17s %-20s %-21s\n", + server_id, uid_gid_str, machine_hostname, dialect, encryption, + signing); + + return 0; +} + +static int prepare_sessionid(struct traverse_state *state) +{ + if (!state->json_output) { + /* always print header line */ + d_printf("\nSamba version %s\n",samba_version_string()); + d_printf("%-7s %-12s %-12s %-41s %-17s %-20s %-21s\n", "PID", "Username", "Group", "Machine", "Protocol Version", "Encryption", "Signing"); + d_printf("----------------------------------------------------------------------------------------------------------------------------------------\n"); + } else { + add_section_to_json(state, "sessions"); + } + return 0; + +} + +static int traverse_sessionid(const char *key, struct sessionid *session, + void *private_data) +{ + fstring uid_gid_str; + fstring uid_str; + fstring gid_str; + struct server_id_buf tmp; + char *machine_hostname = NULL; + int result = 0; + const char *encryption = "-"; + enum crypto_degree encryption_degree = CRYPTO_DEGREE_NONE; + const char *signing = "-"; + enum crypto_degree signing_degree = CRYPTO_DEGREE_NONE; + struct traverse_state *state = (struct traverse_state *)private_data; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + if (do_checks && + (!process_exists(session->pid) || + !Ucrit_checkUid(session->uid))) { + TALLOC_FREE(tmp_ctx); + return 0; + } + + Ucrit_addPid(session->pid); + + if (numeric_only) { + fstr_sprintf(gid_str, "%u", (unsigned int)session->gid); + fstr_sprintf(uid_str, "%u", (unsigned int)session->uid); + fstr_sprintf(uid_gid_str, "%-12u %-12u", + (unsigned int)session->uid, + (unsigned int)session->gid); + } else { + if (session->uid == -1 && session->gid == -1) { + /* + * The session is not fully authenticated yet. + */ + fstrcpy(uid_gid_str, "(auth in progress)"); + fstrcpy(gid_str, "(auth in progress)"); + fstrcpy(uid_str, "(auth in progress)"); + } else { + /* + * In theory it should not happen that one of + * session->uid and session->gid is valid (ie != -1) + * while the other is not (ie = -1), so we a check for + * that case that bails out would be reasonable. + */ + const char *uid_name = "-1"; + const char *gid_name = "-1"; + + if (session->uid != -1) { + uid_name = uidtoname(session->uid); + if (uid_name == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + } + if (session->gid != -1) { + gid_name = gidtoname(session->gid); + if (gid_name == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + } + fstr_sprintf(gid_str, "%s", gid_name); + fstr_sprintf(uid_str, "%s", uid_name); + fstr_sprintf(uid_gid_str, "%-12s %-12s", + uid_name, gid_name); + } + } + + machine_hostname = talloc_asprintf(tmp_ctx, "%s (%s)", + session->remote_machine, + session->hostname); + if (machine_hostname == NULL) { + TALLOC_FREE(tmp_ctx); + return -1; + } + + if (smbXsrv_is_encrypted(session->encryption_flags) || + smbXsrv_is_partially_encrypted(session->encryption_flags)) { + switch (session->cipher) { + case SMB2_ENCRYPTION_AES128_CCM: + encryption = "AES-128-CCM"; + break; + case SMB2_ENCRYPTION_AES128_GCM: + encryption = "AES-128-GCM"; + break; + case SMB2_ENCRYPTION_AES256_CCM: + encryption = "AES-256-CCM"; + break; + case SMB2_ENCRYPTION_AES256_GCM: + encryption = "AES-256-GCM"; + break; + default: + encryption = "???"; + result = -1; + break; + } + if (smbXsrv_is_encrypted(session->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_encrypted(session->encryption_flags)) { + encryption_degree = CRYPTO_DEGREE_PARTIAL; + } + } + + if (smbXsrv_is_signed(session->signing_flags) || + smbXsrv_is_partially_signed(session->signing_flags)) { + switch (session->signing) { + case SMB2_SIGNING_MD5_SMB1: + signing = "HMAC-MD5"; + break; + case SMB2_SIGNING_HMAC_SHA256: + signing = "HMAC-SHA256"; + break; + case SMB2_SIGNING_AES128_CMAC: + signing = "AES-128-CMAC"; + break; + case SMB2_SIGNING_AES128_GMAC: + signing = "AES-128-GMAC"; + break; + default: + signing = "???"; + result = -1; + break; + } + if (smbXsrv_is_signed(session->signing_flags)) { + signing_degree = CRYPTO_DEGREE_FULL; + } else if (smbXsrv_is_partially_signed(session->signing_flags)) { + signing_degree = CRYPTO_DEGREE_PARTIAL; + } + } + + + if (!state->json_output) { + traverse_sessionid_stdout(state, + server_id_str_buf(session->pid, &tmp), + uid_gid_str, + machine_hostname, + session_dialect_str(session->connection_dialect), + encryption, + encryption_degree, + signing, + signing_degree); + } else { + result = traverse_sessionid_json(state, + session, + uid_str, + gid_str, + encryption, + encryption_degree, + signing, + signing_degree, + session_dialect_str(session->connection_dialect)); + } + + TALLOC_FREE(machine_hostname); + TALLOC_FREE(tmp_ctx); + + return result; +} + + +static bool print_notify_rec_stdout(struct traverse_state *state, + const char *path, + char *server_id_str, + unsigned filter, + unsigned subdir_filter) +{ + d_printf("%s\\%s\\%x\\%x\n", path, server_id_str, + filter, subdir_filter); + + return true; +} + +static int prepare_notify(struct traverse_state *state) +{ + if (!state->json_output) { + /* don't print header line */ + } else { + add_section_to_json(state, "notifies"); + } + return 0; +} + +static bool print_notify_rec(const char *path, struct server_id server, + const struct notify_instance *instance, + void *private_data) +{ + struct server_id_buf idbuf; + struct traverse_state *state = (struct traverse_state *)private_data; + bool result; + + if (!state->json_output) { + result = print_notify_rec_stdout(state, + path, + server_id_str_buf(server, &idbuf), + (unsigned)instance->filter, + (unsigned)instance->subdir_filter); + + } else { + result = print_notify_rec_json(state, + instance, + server, + path); + } + + return result; +} + +enum { + OPT_RESOLVE_UIDS = 1000, +}; + +int main(int argc, const char *argv[]) +{ + int c; + int profile_only = 0; + bool show_processes, show_locks, show_shares; + bool show_notify = false; + poptContext pc = NULL; + struct traverse_state state = {0}; + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "processes", + .shortName = 'p', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'p', + .descrip = "Show processes only", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'v', + .descrip = "Be verbose", + }, + { + .longName = "locks", + .shortName = 'L', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'L', + .descrip = "Show locks only", + }, + { + .longName = "shares", + .shortName = 'S', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'S', + .descrip = "Show shares only", + }, + { + .longName = "notify", + .shortName = 'N', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'N', + .descrip = "Show notifies", + }, + { + .longName = "user", + .shortName = 'u', + .argInfo = POPT_ARG_STRING, + .arg = &username, + .val = 'u', + .descrip = "Switch to user", + }, + { + .longName = "brief", + .shortName = 'b', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'b', + .descrip = "Be brief", + }, + { + .longName = "profile", + .shortName = 'P', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'P', + .descrip = "Do profiling", + }, + { + .longName = "profile-rates", + .shortName = 'R', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'R', + .descrip = "Show call rates", + }, + { + .longName = "byterange", + .shortName = 'B', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'B', + .descrip = "Include byte range locks" + }, + { + .longName = "numeric", + .shortName = 'n', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'n', + .descrip = "Numeric uid/gid" + }, + { + .longName = "json", + .shortName = 'j', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'j', + .descrip = "JSON output" + }, + { + .longName = "fast", + .shortName = 'f', + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = 'f', + .descrip = "Skip checks if processes still exist" + }, + { + .longName = "resolve-uids", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = NULL, + .val = OPT_RESOLVE_UIDS, + .descrip = "Try to resolve UIDs to usernames" + }, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + POPT_TABLEEND + }; + TALLOC_CTX *frame = talloc_stackframe(); + int ret = 0; + struct messaging_context *msg_ctx = NULL; + char *db_path; + bool ok; + struct loadparm_context *lp_ctx = NULL; + + state.first = true; + state.json_output = false; + state.resolve_uids = false; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(frame); + exit(1); + } + lp_ctx = samba_cmdline_get_lp_ctx(); + lpcfg_set_cmdline(lp_ctx, "log level", "0"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + POPT_CONTEXT_KEEP_FIRST); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(frame); + exit(1); + } + + while ((c = poptGetNextOpt(pc)) != -1) { + switch (c) { + case 'p': + processes_only = true; + break; + case 'v': + verbose = true; + break; + case 'L': + locks_only = true; + break; + case 'S': + shares_only = true; + break; + case 'N': + show_notify = true; + break; + case 'b': + brief = true; + break; + case 'u': + Ucrit_addUid(nametouid(poptGetOptArg(pc))); + break; + case 'P': + case 'R': + profile_only = c; + break; + case 'B': + show_brl = true; + break; + case 'n': + numeric_only = true; + break; + case 'j': + state.json_output = true; + break; + case 'f': + do_checks = false; + break; + case OPT_RESOLVE_UIDS: + state.resolve_uids = true; + break; + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(c)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + sec_init(); + +#ifdef HAVE_JANSSON + state.root_json = json_new_object(); + if (!json_is_invalid(&state.root_json)) { + add_general_information_to_json(&state); + } +#else /* HAVE_JANSSON */ + if (state.json_output) { + fprintf(stderr, "JSON support not available, please install lib Jansson\n"); + goto done; + } +#endif /* HAVE_JANSSON */ + + if (getuid() != geteuid()) { + fprintf(stderr, "smbstatus should not be run setuid\n"); + ret = 1; + goto done; + } + + if (getuid() != 0) { + fprintf(stderr, "smbstatus only works as root!\n"); + ret = 1; + goto done; + } + + /* setup the flags based on the possible combincations */ + + show_processes = !(shares_only || locks_only || profile_only) || processes_only; + show_locks = !(shares_only || processes_only || profile_only) || locks_only; + show_shares = !(processes_only || locks_only || profile_only) || shares_only; + + if ( username ) + Ucrit_addUid( nametouid(username) ); + + if (verbose && !state.json_output) { + d_printf("using configfile = %s\n", get_dyn_CONFIGFILE()); + } + + msg_ctx = cmdline_messaging_context(get_dyn_CONFIGFILE()); + if (msg_ctx == NULL) { + fprintf(stderr, "Could not initialize messaging, not root?\n"); + ret = -1; + goto done; + } + + switch (profile_only) { + case 'P': + /* Dump profile data */ + ok = status_profile_dump(verbose, &state); + ret = ok ? 0 : 1; + goto done; + case 'R': + /* Continuously display rate-converted data */ + if (!state.json_output) { + ok = status_profile_rates(verbose); + ret = ok ? 0 : 1; + } else { + fprintf(stderr, "Call rates not available in a json output.\n"); + ret = 1; + } + goto done; + default: + break; + } + + if ( show_processes ) { + prepare_sessionid(&state); + sessionid_traverse_read(traverse_sessionid, &state); + + if (processes_only) { + goto done; + } + } + + if ( show_shares ) { + if (brief) { + goto done; + } + prepare_connections(&state); + connections_forall_read(traverse_connections, &state); + + if (!state.json_output) { + d_printf("\n"); + } + + if ( shares_only ) { + goto done; + } + } + + if ( show_locks ) { + int result; + struct db_context *db; + + db_path = lock_path(talloc_tos(), "locking.tdb"); + if (db_path == NULL) { + fprintf(stderr, "Out of memory - exiting\n"); + ret = -1; + goto done; + } + + db = db_open(NULL, db_path, 0, + TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDONLY, 0, + DBWRAP_LOCK_ORDER_1, DBWRAP_FLAG_NONE); + + if (!db) { + fprintf(stderr, "%s not initialised\n", db_path); + fprintf(stderr, "This is normal if an SMB client has never " + "connected to your server.\n"); + TALLOC_FREE(db_path); + ret = 0; + goto done; + } else { + TALLOC_FREE(db); + TALLOC_FREE(db_path); + } + + if (!locking_init_readonly()) { + fprintf(stderr, "Can't initialise locking module - exiting\n"); + ret = 1; + goto done; + } + + prepare_share_mode(&state); + result = share_entry_forall(print_share_mode, &state); + + if (result == 0 && !state.json_output) { + fprintf(stderr, "No locked files\n"); + } else if (result < 0 && !state.json_output) { + fprintf(stderr, "locked file list truncated\n"); + } + + if (!state.json_output) { + d_printf("\n"); + } + + if (show_brl) { + prepare_brl(&state); + brl_forall(print_brl, &state); + } + + locking_end(); + } + + if (show_notify) { + prepare_notify(&state); + notify_walk(msg_ctx, print_notify_rec, &state); + } + +done: + cmdline_messaging_context_free(); + poptFreeContext(pc); +#ifdef HAVE_JANSSON + if (state.json_output) { + d_printf("%s\n", json_to_string(frame, &state.root_json)); + } + json_free(&state.root_json); +#endif /* HAVE_JANSSON */ + TALLOC_FREE(frame); + return ret; +} diff --git a/source3/utils/status.h b/source3/utils/status.h new file mode 100644 index 0000000..c08aba4 --- /dev/null +++ b/source3/utils/status.h @@ -0,0 +1,44 @@ +/* + * Samba Unix/Linux SMB client library + * State struct + * Copyright (C) Jule Anger 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_JANSSON +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#include "auth/common_auth.h" +#endif /* HAVE_JANSSON */ + +#ifndef STATUS_H +#define STATUS_H + +struct traverse_state { + bool json_output; + bool first; + bool resolve_uids; +#ifdef HAVE_JANSSON + struct json_object root_json; +#endif /* HAVE_JANSSON */ +}; + +enum crypto_degree { + CRYPTO_DEGREE_NONE, + CRYPTO_DEGREE_PARTIAL, + CRYPTO_DEGREE_FULL +}; + +#endif diff --git a/source3/utils/status_json.c b/source3/utils/status_json.c new file mode 100644 index 0000000..ee24a3b --- /dev/null +++ b/source3/utils/status_json.c @@ -0,0 +1,1386 @@ +/* + * Samba Unix/Linux SMB client library + * Json output + * Copyright (C) Jule Anger 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbprofile.h" +#include "lib/util/time_basic.h" +#include "conn_tdb.h" +#include "session.h" +#include "librpc/gen_ndr/smbXsrv.h" +#include "librpc/gen_ndr/open_files.h" +#include "status_json.h" +#include "../libcli/security/security.h" +#include "status.h" +#include "lib/util/server_id.h" +#include "lib/util/string_wrappers.h" + +#include <jansson.h> +#include "audit_logging.h" /* various JSON helpers */ +#include "auth/common_auth.h" + +int add_general_information_to_json(struct traverse_state *state) +{ + int result; + + result = json_add_timestamp(&state->root_json); + if (result < 0) { + return -1; + } + + result = json_add_string(&state->root_json, "version", samba_version_string()); + if (result < 0) { + return -1; + } + + result = json_add_string(&state->root_json, "smb_conf", get_dyn_CONFIGFILE()); + if (result < 0) { + return -1; + } + + return 0; +} + +static int add_server_id_to_json(struct json_object *parent_json, + const struct server_id server_id) +{ + struct json_object sub_json; + char *pid_str = NULL; + char *task_id_str = NULL; + char *vnn_str = NULL; + char *unique_id_str = NULL; + int result; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + pid_str = talloc_asprintf( + tmp_ctx, "%lu", (unsigned long)server_id.pid); + result = json_add_string(&sub_json, "pid", pid_str); + if (result < 0) { + goto failure; + } + task_id_str = talloc_asprintf(tmp_ctx, "%u", server_id.task_id); + result = json_add_string(&sub_json, "task_id", task_id_str); + if (result < 0) { + goto failure; + } + vnn_str = talloc_asprintf(tmp_ctx, "%u", server_id.vnn); + result = json_add_string(&sub_json, "vnn", vnn_str); + if (result < 0) { + goto failure; + } + unique_id_str = talloc_asprintf( + tmp_ctx, "%"PRIu64, server_id.unique_id); + result = json_add_string(&sub_json, "unique_id", unique_id_str); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, "server_id", &sub_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +struct mask2txt { + uint32_t mask; + const char *string_desc; +}; + +/* + * Convert a mask of some sort (access, oplock, leases), + * to key/value pairs in a JSON object. + */ +static int map_mask_to_json(struct json_object *root_json, + uint32_t tomap, + const struct mask2txt *table) +{ + const struct mask2txt *a = NULL; + int result = 0; + + for (a = table; a->string_desc != 0; a++) { + result = json_add_bool(root_json, a->string_desc, + (tomap & a->mask) ? true : false); + + if (result < 0) { + return result; + } + tomap &= ~a->mask; + } + + /* Assert we know about all requested "tomap" values */ + SMB_ASSERT(tomap == 0); + + return 0; +} + +static const struct mask2txt access_mask[] = { + {FILE_READ_DATA, "READ_DATA"}, + {FILE_WRITE_DATA, "WRITE_DATA"}, + {FILE_APPEND_DATA, "APPEND_DATA"}, + {FILE_READ_EA, "READ_EA"}, + {FILE_WRITE_EA, "WRITE_EA"}, + {FILE_EXECUTE, "EXECUTE"}, + {FILE_READ_ATTRIBUTES, "READ_ATTRIBUTES"}, + {FILE_WRITE_ATTRIBUTES, "WRITE_ATTRIBUTES"}, + {FILE_DELETE_CHILD, "DELETE_CHILD"}, + {SEC_STD_DELETE, "DELETE"}, + {SEC_STD_READ_CONTROL, "READ_CONTROL"}, + {SEC_STD_WRITE_DAC, "WRITE_DAC"}, + {SEC_STD_SYNCHRONIZE, "SYNCHRONIZE"}, + {SEC_FLAG_SYSTEM_SECURITY, "ACCESS_SYSTEM_SECURITY"}, + {0, NULL} +}; + +static const struct mask2txt oplock_mask[] = { + {EXCLUSIVE_OPLOCK, "EXCLUSIVE"}, + {BATCH_OPLOCK, "BATCH"}, + {LEVEL_II_OPLOCK, "LEVEL_II"}, + {LEASE_OPLOCK, "LEASE"}, + {0, NULL} +}; + +static const struct mask2txt sharemode_mask[] = { + {FILE_SHARE_READ, "READ"}, + {FILE_SHARE_WRITE, "WRITE"}, + {FILE_SHARE_DELETE, "DELETE"}, + {0, NULL} +}; + +static const struct mask2txt lease_mask[] = { + {SMB2_LEASE_READ, "READ"}, + {SMB2_LEASE_WRITE, "WRITE"}, + {SMB2_LEASE_HANDLE, "HANDLE"}, + {0, NULL} +}; + +int add_profile_item_to_json(struct traverse_state *state, + const char *section, + const char *subsection, + const char *key, + uintmax_t value) +{ + struct json_object section_json = { + .valid = false, + }; + struct json_object subsection_json = { + .valid = false, + }; + int result = 0; + + section_json = json_get_object(&state->root_json, section); + if (json_is_invalid(§ion_json)) { + goto failure; + } + subsection_json = json_get_object(§ion_json, subsection); + if (json_is_invalid(&subsection_json)) { + goto failure; + } + + result = json_add_int(&subsection_json, key, value); + if (result < 0) { + goto failure; + } + + result = json_update_object(§ion_json, subsection, &subsection_json); + if (result < 0) { + goto failure; + } + result = json_update_object(&state->root_json, section, §ion_json); + if (result < 0) { + goto failure; + } + + return 0; +failure: + json_free(§ion_json); + json_free(&subsection_json); + return -1; +} + +int add_section_to_json(struct traverse_state *state, + const char *key) +{ + struct json_object empty_json; + int result; + + empty_json = json_new_object(); + if (json_is_invalid(&empty_json)) { + return -1; + } + + result = json_add_object(&state->root_json, key, &empty_json); + if (result < 0) { + return -1; + } + + return result; +} + +static int add_crypto_to_json(struct json_object *parent_json, + const char *key, + const char *cipher, + enum crypto_degree degree) +{ + struct json_object sub_json; + const char *degree_str; + int result; + + if (degree == CRYPTO_DEGREE_NONE) { + degree_str = "none"; + } else if (degree == CRYPTO_DEGREE_PARTIAL) { + degree_str = "partial"; + } else { + degree_str = "full"; + } + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + result = json_add_string(&sub_json, "cipher", cipher); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "degree", degree_str); + if (result < 0) { + goto failure; + } + result = json_add_object(parent_json, key, &sub_json); + if (result < 0) { + goto failure; + } + + return 0; +failure: + json_free(&sub_json); + return -1; +} + +static int add_channel_to_json(struct json_object *parent_json, + const struct smbXsrv_channel_global0 *channel) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct json_object sub_json; + char *id_str = NULL; + struct timeval tv; + struct timeval_buf tv_buf; + char *time_str = NULL; + int result; + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + id_str = talloc_asprintf(frame, "%"PRIu64"", channel->channel_id); + if (id_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "channel_id", id_str); + if (result < 0) { + goto failure; + } + nttime_to_timeval(&tv, channel->creation_time); + time_str = timeval_str_buf(&tv, true, true, &tv_buf); + if (time_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "creation_time", time_str); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "local_address", channel->local_address); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "remote_address", channel->remote_address); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, id_str, &sub_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(frame); + return 0; +failure: + json_free(&sub_json); + TALLOC_FREE(frame); + return -1; +} + +static int add_channels_to_json(struct json_object *parent_json, + const struct smbXsrv_session_global0 *global) +{ + struct json_object sub_json; + uint32_t i; + int result; + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + for (i = 0; i < global->num_channels; i++) { + const struct smbXsrv_channel_global0 *c = &global->channels[i]; + + result = add_channel_to_json(&sub_json, c); + if (result < 0) { + goto failure; + } + } + + result = json_add_object(parent_json, "channels", &sub_json); + if (result < 0) { + goto failure; + } + + return 0; +failure: + json_free(&sub_json); + return -1; +} + +int traverse_connections_json(struct traverse_state *state, + const struct connections_data *crec, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree) +{ + struct json_object sub_json; + struct json_object connections_json; + struct timeval tv; + struct timeval_buf tv_buf; + char *time = NULL; + int result = 0; + char *sess_id_str = NULL; + char *tcon_id_str = NULL; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + connections_json = json_get_object(&state->root_json, "tcons"); + if (json_is_invalid(&connections_json)) { + goto failure; + } + + result = json_add_string(&sub_json, "service", crec->servicename); + if (result < 0) { + goto failure; + } + result = add_server_id_to_json(&sub_json, crec->pid); + if (result < 0) { + goto failure; + } + tcon_id_str = talloc_asprintf(tmp_ctx, "%u", crec->cnum); + if (tcon_id_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "tcon_id", tcon_id_str); + if (result < 0) { + goto failure; + } + sess_id_str = talloc_asprintf(tmp_ctx, "%u", crec->sess_id); + if (sess_id_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "session_id", sess_id_str); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "machine", crec->machine); + if (result < 0) { + goto failure; + } + nttime_to_timeval(&tv, crec->start); + time = timeval_str_buf(&tv, true, true, &tv_buf); + if (time == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "connected_at", time); + if (result < 0) { + goto failure; + } + result = add_crypto_to_json(&sub_json, "encryption", + encryption_cipher, encryption_degree); + if (result < 0) { + goto failure; + } + result = add_crypto_to_json(&sub_json, "signing", + signing_cipher, signing_degree); + if (result < 0) { + goto failure; + } + + result = json_add_object(&connections_json, tcon_id_str, &sub_json); + if (result < 0) { + goto failure; + } + + result = json_update_object(&state->root_json, "tcons", &connections_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +int traverse_sessionid_json(struct traverse_state *state, + struct sessionid *session, + char *uid_str, + char *gid_str, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree, + const char *connection_dialect) +{ + struct json_object sub_json; + struct json_object session_json; + int result = 0; + char *id_str = NULL; + struct timeval tv; + struct timeval_buf tv_buf; + char *time_str = NULL; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + session_json = json_get_object(&state->root_json, "sessions"); + if (json_is_invalid(&session_json)) { + goto failure; + } + + id_str = talloc_asprintf(tmp_ctx, "%u", session->id_num); + result = json_add_string(&sub_json, "session_id", id_str); + if (result < 0) { + goto failure; + } + result = add_server_id_to_json(&sub_json, session->pid); + if (result < 0) { + goto failure; + } + result = json_add_int(&sub_json, "uid", session->uid); + if (result < 0) { + goto failure; + } + result = json_add_int(&sub_json, "gid", session->gid); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "username", uid_str); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "groupname", gid_str); + if (result < 0) { + goto failure; + } + + nttime_to_timeval(&tv, session->global->creation_time); + time_str = timeval_str_buf(&tv, true, true, &tv_buf); + if (time_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "creation_time", time_str); + if (result < 0) { + goto failure; + } + + nttime_to_timeval(&tv, session->global->expiration_time); + time_str = timeval_str_buf(&tv, true, true, &tv_buf); + if (time_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "expiration_time", time_str); + if (result < 0) { + goto failure; + } + + nttime_to_timeval(&tv, session->global->auth_time); + time_str = timeval_str_buf(&tv, true, true, &tv_buf); + if (time_str == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "auth_time", time_str); + if (result < 0) { + goto failure; + } + + result = json_add_string(&sub_json, "remote_machine", session->remote_machine); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "hostname", session->hostname); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "session_dialect", connection_dialect); + if (result < 0) { + goto failure; + } + result = json_add_guid(&sub_json, + "client_guid", + &session->global->client_guid); + if (result < 0) { + goto failure; + } + result = add_crypto_to_json(&sub_json, "encryption", + encryption_cipher, encryption_degree); + if (result < 0) { + goto failure; + } + result = add_crypto_to_json(&sub_json, "signing", + signing_cipher, signing_degree); + if (result < 0) { + goto failure; + } + + result = add_channels_to_json(&sub_json, session->global); + if (result < 0) { + goto failure; + } + + result = json_add_object(&session_json, id_str, &sub_json); + if (result < 0) { + goto failure; + } + + result = json_update_object(&state->root_json, "sessions", &session_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_access_mode_to_json(struct json_object *parent_json, + int access_int) +{ + struct json_object access_json; + char *access_hex = NULL; + const char *access_str = NULL; + int result; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + access_json = json_new_object(); + if (json_is_invalid(&access_json)) { + goto failure; + } + + access_hex = talloc_asprintf(tmp_ctx, "0x%08x", access_int); + result = json_add_string(&access_json, "hex", access_hex); + if (result < 0) { + goto failure; + } + result = map_mask_to_json(&access_json, access_int, access_mask); + if (result < 0) { + goto failure; + } + + access_str = talloc_asprintf(tmp_ctx, "%s%s", + (access_int & FILE_READ_DATA)?"R":"", + (access_int & (FILE_WRITE_DATA|FILE_APPEND_DATA))?"W":""); + result = json_add_string(&access_json, "text", access_str); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, "access_mask", &access_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&access_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_caching_to_json(struct json_object *parent_json, + int op_type, + int lease_type) +{ + struct json_object caching_json; + char *hex = NULL; + char *caching_text = NULL; + int caching_type = 0; + int result; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + caching_json = json_new_object(); + if (json_is_invalid(&caching_json)) { + goto failure; + } + + if (op_type & LEASE_OPLOCK) { + caching_type = lease_type; + } else { + if (op_type & LEVEL_II_OPLOCK) { + caching_type = SMB2_LEASE_READ; + } else if (op_type & EXCLUSIVE_OPLOCK) { + caching_type = SMB2_LEASE_READ + SMB2_LEASE_WRITE; + } else if (op_type & BATCH_OPLOCK) { + caching_type = SMB2_LEASE_READ + SMB2_LEASE_WRITE + SMB2_LEASE_HANDLE; + } + } + result = map_mask_to_json(&caching_json, caching_type, lease_mask); + if (result < 0) { + goto failure; + } + + hex = talloc_asprintf(tmp_ctx, "0x%08x", caching_type); + if (hex == NULL) { + goto failure; + } + result = json_add_string(&caching_json, "hex", hex); + if (result < 0) { + goto failure; + } + + caching_text = talloc_asprintf(tmp_ctx, "%s%s%s", + (caching_type & SMB2_LEASE_READ)?"R":"", + (caching_type & SMB2_LEASE_WRITE)?"W":"", + (caching_type & SMB2_LEASE_HANDLE)?"H":""); + if (caching_text == NULL) { + return -1; + } + + result = json_add_string(&caching_json, "text", caching_text); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, "caching", &caching_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&caching_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_oplock_to_json(struct json_object *parent_json, + uint16_t op_type, + const char *op_str) +{ + struct json_object oplock_json; + int result; + + oplock_json = json_new_object(); + if (json_is_invalid(&oplock_json)) { + goto failure; + } + + if (op_type != 0) { + result = map_mask_to_json(&oplock_json, op_type, oplock_mask); + if (result < 0) { + goto failure; + } + result = json_add_string(&oplock_json, "text", op_str); + if (result < 0) { + goto failure; + } + } + + result = json_add_object(parent_json, "oplock", &oplock_json); + if (result < 0) { + goto failure; + } + + return 0; +failure: + json_free(&oplock_json); + return -1; +} + +static int lease_key_to_str(struct smb2_lease_key lease_key, + char *lease_str) +{ + uint8_t _buf[16] = {0}; + DATA_BLOB blob = data_blob_const(_buf, sizeof(_buf)); + struct GUID guid; + NTSTATUS status; + char *tmp = NULL; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + PUSH_LE_U64(_buf, 0, lease_key.data[0]); + PUSH_LE_U64(_buf, 8, lease_key.data[1]); + + status = GUID_from_ndr_blob(&blob, &guid); + if (!NT_STATUS_IS_OK(status)) { + goto failure; + } + tmp = GUID_string(tmp_ctx, &guid); + if (tmp == NULL) { + goto failure; + } + fstrcpy(lease_str, tmp); + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_lease_to_json(struct json_object *parent_json, + int lease_type, + struct smb2_lease_key lease_key, + bool add_lease) +{ + struct json_object lease_json; + char *lease_hex = NULL; + char *lease_text = NULL; + fstring lease_key_str; + int result; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + lease_json = json_new_object(); + if (json_is_invalid(&lease_json)) { + goto failure; + } + + + if (add_lease) { + result = lease_key_to_str(lease_key, lease_key_str); + if (result < 0) { + goto failure; + } + result = json_add_string(&lease_json, "lease_key", lease_key_str); + if (result < 0) { + goto failure; + } + lease_hex = talloc_asprintf(tmp_ctx, "0x%08x", lease_type); + result = json_add_string(&lease_json, "hex", lease_hex); + if (result < 0) { + goto failure; + } + if (lease_type > (SMB2_LEASE_WRITE + SMB2_LEASE_HANDLE + SMB2_LEASE_READ)) { + result = json_add_bool(&lease_json, "UNKNOWN", true); + if (result < 0) { + goto failure; + } + } else { + result = map_mask_to_json(&lease_json, lease_type, lease_mask); + if (result < 0) { + goto failure; + } + } + lease_text = talloc_asprintf(tmp_ctx, "%s%s%s", + (lease_type & SMB2_LEASE_READ)?"R":"", + (lease_type & SMB2_LEASE_WRITE)?"W":"", + (lease_type & SMB2_LEASE_HANDLE)?"H":""); + + result = json_add_string(&lease_json, "text", lease_text); + if (result < 0) { + goto failure; + } + } + + result = json_add_object(parent_json, "lease", &lease_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&lease_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_sharemode_to_json(struct json_object *parent_json, + int sharemode) +{ + struct json_object sharemode_json; + char *hex = NULL; + char *text = NULL; + int result; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + sharemode_json = json_new_object(); + if (json_is_invalid(&sharemode_json)) { + goto failure; + } + + hex = talloc_asprintf(tmp_ctx, "0x%08x", sharemode); + if (hex == NULL) { + goto failure; + } + result = json_add_string(&sharemode_json, "hex", hex); + if (result < 0) { + goto failure; + } + result = map_mask_to_json(&sharemode_json, sharemode, sharemode_mask); + if (result < 0) { + goto failure; + } + + text = talloc_asprintf(tmp_ctx, "%s%s%s", + (sharemode & FILE_SHARE_READ)?"R":"", + (sharemode & FILE_SHARE_WRITE)?"W":"", + (sharemode & FILE_SHARE_DELETE)?"D":""); + if (text == NULL) { + goto failure; + } + result = json_add_string(&sharemode_json, "text", text); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, "sharemode", &sharemode_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&sharemode_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_open_to_json(struct json_object *parent_json, + const struct share_mode_entry *e, + bool resolve_uids, + const char *op_str, + uint32_t lease_type, + const char *uid_str) +{ + struct json_object sub_json = { + .valid = false, + }; + struct json_object opens_json = { + .valid = false, + }; + struct timeval_buf tv_buf; + int result = 0; + char *timestr; + bool add_lease = false; + char *key = NULL; + char *share_file_id = NULL; + char *pid = NULL; + struct server_id_buf tmp; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + opens_json = json_get_object(parent_json, "opens"); + if (json_is_invalid(&opens_json)) { + goto failure; + } + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + + result = add_server_id_to_json(&sub_json, e->pid); + if (result < 0) { + goto failure; + } + if (resolve_uids) { + result = json_add_string(&sub_json, "username", uid_str); + if (result < 0) { + goto failure; + } + } + result = json_add_int(&sub_json, "uid", e->uid); + if (result < 0) { + goto failure; + } + share_file_id = talloc_asprintf(tmp_ctx, "%"PRIu64, e->share_file_id); + result = json_add_string(&sub_json, "share_file_id", share_file_id); + if (result < 0) { + goto failure; + } + result = add_sharemode_to_json(&sub_json, e->share_access); + if (result < 0) { + goto failure; + } + result = add_access_mode_to_json(&sub_json, e->access_mask); + if (result < 0) { + goto failure; + } + result = add_caching_to_json(&sub_json, e->op_type, lease_type); + if (result < 0) { + goto failure; + } + result = add_oplock_to_json(&sub_json, e->op_type, op_str); + if (result < 0) { + goto failure; + } + add_lease = e->op_type & LEASE_OPLOCK; + result = add_lease_to_json(&sub_json, lease_type, e->lease_key, add_lease); + if (result < 0) { + goto failure; + } + + timestr = timeval_str_buf(&e->time, true, true, &tv_buf); + if (timestr == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "opened_at", timestr); + if (result < 0) { + goto failure; + } + + pid = server_id_str_buf(e->pid, &tmp); + key = talloc_asprintf(tmp_ctx, "%s/%"PRIu64, pid, e->share_file_id); + result = json_add_object(&opens_json, key, &sub_json); + if (result < 0) { + goto failure; + } + result = json_update_object(parent_json, "opens", &opens_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&opens_json); + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_fileid_to_json(struct json_object *parent_json, + struct file_id fid) +{ + struct json_object fid_json; + int result; + + fid_json = json_new_object(); + if (json_is_invalid(&fid_json)) { + goto failure; + } + + result = json_add_int(&fid_json, "devid", fid.devid); + if (result < 0) { + goto failure; + } + result = json_add_int(&fid_json, "inode", fid.inode); + if (result < 0) { + goto failure; + } + result = json_add_int(&fid_json, "extid", fid.extid); + if (result < 0) { + goto failure; + } + + result = json_add_object(parent_json, "fileid", &fid_json); + if (result < 0) { + goto failure; + } + + return 0; +failure: + json_free(&fid_json); + return -1; +} + +int print_share_mode_json(struct traverse_state *state, + const struct share_mode_data *d, + const struct share_mode_entry *e, + struct file_id fid, + const char *uid_str, + const char *op_str, + uint32_t lease_type, + const char *filename) +{ + struct json_object locks_json = { + .valid = false, + }; + struct json_object file_json = { + .valid = false, + }; + char *key = NULL; + int result = 0; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + if (d->servicepath[strlen(d->servicepath)-1] == '/') { + key = talloc_asprintf(tmp_ctx, "%s%s", d->servicepath, filename); + } else { + key = talloc_asprintf(tmp_ctx, "%s/%s", d->servicepath, filename); + } + + locks_json = json_get_object(&state->root_json, "open_files"); + if (json_is_invalid(&locks_json)) { + goto failure; + } + file_json = json_get_object(&locks_json, key); + if (json_is_invalid(&file_json)) { + goto failure; + } + + result = json_add_string(&file_json, "service_path", d->servicepath); + if (result < 0) { + goto failure; + } + result = json_add_string(&file_json, "filename", filename); + if (result < 0) { + goto failure; + } + result = add_fileid_to_json(&file_json, fid); + if (result < 0) { + goto failure; + } + result = json_add_int(&file_json, "num_pending_deletes", d->num_delete_tokens); + if (result < 0) { + goto failure; + } + + result = add_open_to_json(&file_json, + e, + state->resolve_uids, + op_str, + lease_type, + uid_str); + if (result < 0) { + goto failure; + } + + result = json_update_object(&locks_json, key, &file_json); + if (result < 0) { + goto failure; + } + result = json_update_object(&state->root_json, "open_files", &locks_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&file_json); + json_free(&locks_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +static int add_lock_to_json(struct json_object *parent_json, + struct server_id server_id, + const char *type, + enum brl_flavour flavour, + intmax_t start, + intmax_t size) +{ + struct json_object sub_json = { + .valid = false, + }; + struct json_object locks_json = { + .valid = false, + }; + const char *flavour_str; + int result = 0; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + locks_json = json_get_array(parent_json, "locks"); + if (json_is_invalid(&locks_json)) { + goto failure; + } + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + goto failure; + } + + result = add_server_id_to_json(&sub_json, server_id); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "type", type); + if (result < 0) { + goto failure; + } + flavour_str = talloc_asprintf(tmp_ctx, "%s%s", + (flavour == WINDOWS_LOCK)?"Windows":"", + (flavour == POSIX_LOCK)?"Posix":""); + result = json_add_string(&sub_json, "flavour", flavour_str); + if (result < 0) { + goto failure; + } + result = json_add_int(&sub_json, "start", start); + if (result < 0) { + goto failure; + } + result = json_add_int(&sub_json, "size", size); + if (result < 0) { + goto failure; + } + + result = json_add_object(&locks_json, NULL, &sub_json); + if (result < 0) { + goto failure; + } + result = json_update_object(parent_json, "locks", &locks_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&locks_json); + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +int print_brl_json(struct traverse_state *state, + const struct server_id server_id, + struct file_id fid, + const char *type, + enum brl_flavour flavour, + intmax_t start, + intmax_t size, + const char *sharepath, + const char *filename) +{ + struct json_object file_json = { + .valid = false, + }; + struct json_object brl_json = { + .valid = false, + }; + int result = 0; + char *key; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + if (sharepath[strlen(sharepath)-1] == '/') { + key = talloc_asprintf(tmp_ctx, "%s%s", sharepath, filename); + } else { + key = talloc_asprintf(tmp_ctx, "%s/%s", sharepath, filename); + } + if (key == NULL) { + goto failure; + } + + brl_json = json_get_object(&state->root_json, "byte_range_locks"); + if (json_is_invalid(&brl_json)) { + goto failure; + } + file_json = json_get_object(&brl_json, key); + if (json_is_invalid(&file_json)) { + goto failure; + } + + result = add_fileid_to_json(&file_json, fid); + if (result < 0) { + goto failure; + } + result = json_add_string(&file_json, "file_name", filename); + if (result < 0) { + goto failure; + } + result = json_add_string(&file_json, "share_path", sharepath); + if (result < 0) { + goto failure; + } + result = add_server_id_to_json(&file_json, server_id); + if (result < 0) { + goto failure; + } + result = add_lock_to_json(&file_json, server_id, type, flavour, start, size); + if (result < 0) { + goto failure; + } + + result = json_add_object(&brl_json, key, &file_json); + if (result < 0) { + goto failure; + } + result = json_update_object(&state->root_json, "byte_range_locks", &brl_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return 0; +failure: + json_free(&file_json); + json_free(&brl_json); + TALLOC_FREE(tmp_ctx); + return -1; +} + +bool print_notify_rec_json(struct traverse_state *state, + const struct notify_instance *instance, + const struct server_id server_id, + const char *path) +{ + struct json_object sub_json; + struct json_object notify_json; + char *filter = NULL; + char *subdir_filter = NULL; + struct timeval_buf tv_buf; + struct timeval val; + char *time = NULL; + char *pid = NULL; + struct server_id_buf tmp; + int result = 0; + + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + if (tmp_ctx == NULL) { + return -1; + } + + sub_json = json_new_object(); + if (json_is_invalid(&sub_json)) { + return false; + } + notify_json = json_get_object(&state->root_json, "notifies"); + if (json_is_invalid(¬ify_json)) { + goto failure; + } + + result = add_server_id_to_json(&sub_json, server_id); + if (result < 0) { + goto failure; + } + result = json_add_string(&sub_json, "path", path); + if (result < 0) { + goto failure; + } + filter = talloc_asprintf(tmp_ctx, "%u", instance->filter); + if (filter == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "filter", filter); + if (result < 0) { + goto failure; + } + subdir_filter = talloc_asprintf(tmp_ctx, "%u", instance->subdir_filter); + if (subdir_filter == NULL) { + goto failure; + } + result = json_add_string(&sub_json, "subdir_filter", subdir_filter); + if (result < 0) { + goto failure; + } + val = convert_timespec_to_timeval(instance->creation_time); + time = timeval_str_buf(&val, true, true, &tv_buf); + result = json_add_string(&sub_json, "creation_time", time); + if (result < 0) { + goto failure; + } + + pid = server_id_str_buf(server_id, &tmp); + result = json_add_object(¬ify_json, pid, &sub_json); + if (result < 0) { + goto failure; + } + + result = json_update_object(&state->root_json, "notifies", ¬ify_json); + if (result < 0) { + goto failure; + } + + TALLOC_FREE(tmp_ctx); + return true; +failure: + json_free(&sub_json); + TALLOC_FREE(tmp_ctx); + return false; +} diff --git a/source3/utils/status_json.h b/source3/utils/status_json.h new file mode 100644 index 0000000..ef5d181 --- /dev/null +++ b/source3/utils/status_json.h @@ -0,0 +1,77 @@ +/* + * Samba Unix/Linux SMB client library + * Json output + * Copyright (C) Jule Anger 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "status.h" +#include "smbd/notifyd/notifyd_db.h" + +#ifndef STATUS_JSON_H +#define STATUS_JSON_H + +int add_section_to_json(struct traverse_state *state, + const char *key); + +int add_general_information_to_json(struct traverse_state *state); + +int add_profile_item_to_json(struct traverse_state *state, + const char *section, + const char *subsection, + const char *key, + uintmax_t value); + +int traverse_connections_json(struct traverse_state *state, + const struct connections_data *crec, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree); + +int traverse_sessionid_json(struct traverse_state *state, + struct sessionid *session, + char *uid_str, + char *gid_str, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree, + const char *connection_dialect); + +int print_share_mode_json(struct traverse_state *state, + const struct share_mode_data *d, + const struct share_mode_entry *e, + struct file_id fid, + const char *uid_str, + const char *op_str, + uint32_t lease_type, + const char *filename); + +int print_brl_json(struct traverse_state *state, + const struct server_id server_id, + struct file_id fid, + const char *type, + enum brl_flavour flavour, + intmax_t start, + intmax_t size, + const char *sharepath, + const char *filename); + +bool print_notify_rec_json(struct traverse_state *state, + const struct notify_instance *instance, + const struct server_id server_id, + const char *path); +#endif diff --git a/source3/utils/status_json_dummy.c b/source3/utils/status_json_dummy.c new file mode 100644 index 0000000..3cd8531 --- /dev/null +++ b/source3/utils/status_json_dummy.c @@ -0,0 +1,101 @@ +/* + * Samba Unix/Linux SMB client library + * Json output + * Copyright (C) Jule Anger 2022 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbprofile.h" +#include "../libcli/security/security.h" +#include "librpc/gen_ndr/open_files.h" +#include "conn_tdb.h" +#include "status_json.h" + +int add_section_to_json(struct traverse_state *state, + const char *key) +{ + return 0; +} + +int add_general_information_to_json(struct traverse_state *state) +{ + return 0; +} + +int add_profile_item_to_json(struct traverse_state *state, + const char *section, + const char *subsection, + const char *key, + uintmax_t value) +{ + return 0; +} + +int traverse_connections_json(struct traverse_state *state, + const struct connections_data *crec, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree) +{ + return 0; +} + +int traverse_sessionid_json(struct traverse_state *state, + struct sessionid *session, + char *uid_str, + char *gid_str, + const char *encryption_cipher, + enum crypto_degree encryption_degree, + const char *signing_cipher, + enum crypto_degree signing_degree, + const char *connection_dialect) +{ + return 0; +} + +int print_share_mode_json(struct traverse_state *state, + const struct share_mode_data *d, + const struct share_mode_entry *e, + struct file_id fid, + const char *uid_str, + const char *op_str, + uint32_t lease_type, + const char *filename) +{ + return 0; +} + +int print_brl_json(struct traverse_state *state, + const struct server_id server_id, + struct file_id fid, + const char *type, + enum brl_flavour flavour, + intmax_t start, + intmax_t size, + const char *sharepath, + const char *filename) +{ + return 0; +} + +bool print_notify_rec_json(struct traverse_state *state, + const struct notify_instance *instance, + const struct server_id server_id, + const char *path) +{ + return 0; +} diff --git a/source3/utils/status_profile.c b/source3/utils/status_profile.c new file mode 100644 index 0000000..6e0916e --- /dev/null +++ b/source3/utils/status_profile.c @@ -0,0 +1,381 @@ +/* + * Unix SMB/CIFS implementation. + * status reporting + * Copyright (C) Andrew Tridgell 1994-1998 + * Copyright (C) James Peach 2005-2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbprofile.h" +#include "status_profile.h" +#include "conn_tdb.h" +#include "librpc/gen_ndr/open_files.h" +#include "status_json.h" + +static void profile_separator(const char * title, + struct traverse_state *state) +{ + char line[79 + 1]; + char * end; + + if (state->json_output) { + return; + } + + snprintf(line, sizeof(line), "**** %s ", title); + + for (end = line + strlen(line); end < &line[sizeof(line) -1]; ++end) { + *end = '*'; + } + + line[sizeof(line) - 1] = '\0'; + d_printf("%s\n", line); +} + +/******************************************************************* + dump the elements of the profile structure + ******************************************************************/ +bool status_profile_dump(bool verbose, + struct traverse_state *state) +{ + struct profile_stats stats = {}; + const char* latest_section = NULL; + + if (!profile_setup(NULL, True)) { + fprintf(stderr,"Failed to initialise profile memory\n"); + return False; + } + + smbprofile_collect(&stats); + +#define __PRINT_FIELD_LINE(name, _stats, field) do { \ + uintmax_t val = (uintmax_t)stats.values._stats.field; \ + if (!state->json_output) { \ + d_printf("%-59s%20ju\n", \ + name "_" #field ":", \ + val); \ + } else { \ + add_profile_item_to_json(state, latest_section, name, #field, val); \ + } \ +} while(0); +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) do { \ + latest_section = display; \ + profile_separator(display, state);\ +} while(0); +#define SMBPROFILE_STATS_COUNT(name) do { \ + __PRINT_FIELD_LINE(#name, name##_stats, count); \ +} while(0); +#define SMBPROFILE_STATS_TIME(name) do { \ + __PRINT_FIELD_LINE(#name, name##_stats, time); \ +} while(0); +#define SMBPROFILE_STATS_BASIC(name) do { \ + __PRINT_FIELD_LINE(#name, name##_stats, count); \ + __PRINT_FIELD_LINE(#name, name##_stats, time); \ +} while(0); +#define SMBPROFILE_STATS_BYTES(name) do { \ + __PRINT_FIELD_LINE(#name, name##_stats, count); \ + __PRINT_FIELD_LINE(#name, name##_stats, time); \ + __PRINT_FIELD_LINE(#name, name##_stats, idle); \ + __PRINT_FIELD_LINE(#name, name##_stats, bytes); \ +} while(0); +#define SMBPROFILE_STATS_IOBYTES(name) do { \ + __PRINT_FIELD_LINE(#name, name##_stats, count); \ + __PRINT_FIELD_LINE(#name, name##_stats, time); \ + __PRINT_FIELD_LINE(#name, name##_stats, idle); \ + __PRINT_FIELD_LINE(#name, name##_stats, inbytes); \ + __PRINT_FIELD_LINE(#name, name##_stats, outbytes); \ +} while(0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_ALL_SECTIONS +#undef __PRINT_FIELD_LINE +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + + return True; +} + +/* Convert microseconds to milliseconds. */ +#define usec_to_msec(s) ((s) / 1000) +/* Convert microseconds to seconds. */ +#define usec_to_sec(s) ((s) / 1000000) +/* One second in microseconds. */ +#define one_second_usec (1000000) + +#define sample_interval_usec one_second_usec + +#define percent_time(used, period) ((double)(used) / (double)(period) * 100.0 ) + +static uint64_t print_count_count_samples( + char *buf, const size_t buflen, + const char *name, + const struct smbprofile_stats_count * const current, + const struct smbprofile_stats_count * const last, + uint64_t delta_usec) +{ + uint64_t step = current->count - last->count; + uint64_t count = 0; + + if (step != 0) { + uint64_t delta_sec = usec_to_sec(delta_usec); + + count++; + + if (buf[0] == '\0') { + snprintf(buf, buflen, + "%-40s %ju/sec", + name, (uintmax_t)(step / delta_sec)); + } else { + printf("%-40s %s %ju/sec\n", + buf, name, (uintmax_t)(step / delta_sec)); + buf[0] = '\0'; + } + } + + return count; +} + +static uint64_t print_basic_count_samples( + char *buf, const size_t buflen, + const char *name, + const struct smbprofile_stats_basic * const current, + const struct smbprofile_stats_basic * const last, + uint64_t delta_usec) +{ + uint64_t step = current->count - last->count; + uint64_t spent = current->time - last->time; + uint64_t count = 0; + + if (step != 0) { + uint64_t delta_sec = usec_to_sec(delta_usec); + + count++; + + if (buf[0] == '\0') { + snprintf(buf, buflen, + "%s %ju/sec (%.2f%%)", + name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + } else { + printf("%-40s %s %ju/sec (%.2f%%)\n", + buf, name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + buf[0] = '\0'; + } + } + + return count; +} + +static uint64_t print_bytes_count_samples( + char *buf, const size_t buflen, + const char *name, + const struct smbprofile_stats_bytes * const current, + const struct smbprofile_stats_bytes * const last, + uint64_t delta_usec) +{ + uint64_t step = current->count - last->count; + uint64_t spent = current->time - last->time; + uint64_t count = 0; + + if (step != 0) { + uint64_t delta_sec = usec_to_sec(delta_usec); + + count++; + + if (buf[0] == '\0') { + snprintf(buf, buflen, + "%s %ju/sec (%.2f%%)", + name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + } else { + printf("%-40s %s %ju/sec (%.2f%%)\n", + buf, name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + buf[0] = '\0'; + } + } + + return count; +} + +static uint64_t print_iobytes_count_samples( + char *buf, const size_t buflen, + const char *name, + const struct smbprofile_stats_iobytes * const current, + const struct smbprofile_stats_iobytes * const last, + uint64_t delta_usec) +{ + uint64_t step = current->count - last->count; + uint64_t spent = current->time - last->time; + uint64_t count = 0; + + if (step != 0) { + uint64_t delta_sec = usec_to_sec(delta_usec); + + count++; + + if (buf[0] == '\0') { + snprintf(buf, buflen, + "%s %ju/sec (%.2f%%)", + name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + } else { + printf("%-40s %s %ju/sec (%.2f%%)\n", + buf, name, (uintmax_t)(step / delta_sec), + percent_time(spent, delta_usec)); + buf[0] = '\0'; + } + } + + return count; +} + +static uint64_t print_count_samples( + const struct profile_stats * const current, + const struct profile_stats * const last, + uint64_t delta_usec) +{ + uint64_t count = 0; + char buf[60] = { '\0', }; + + if (delta_usec == 0) { + return 0; + } + +#define SMBPROFILE_STATS_START +#define SMBPROFILE_STATS_SECTION_START(name, display) +#define SMBPROFILE_STATS_COUNT(name) do { \ + count += print_count_count_samples(buf, sizeof(buf), \ + #name, \ + ¤t->values.name##_stats, \ + &last->values.name##_stats, \ + delta_usec); \ +} while(0); +#define SMBPROFILE_STATS_TIME(name) do { \ +} while(0); +#define SMBPROFILE_STATS_BASIC(name) do { \ + count += print_basic_count_samples(buf, sizeof(buf), \ + #name, \ + ¤t->values.name##_stats, \ + &last->values.name##_stats, \ + delta_usec); \ +} while(0); +#define SMBPROFILE_STATS_BYTES(name) do { \ + count += print_bytes_count_samples(buf, sizeof(buf), \ + #name, \ + ¤t->values.name##_stats, \ + &last->values.name##_stats, \ + delta_usec); \ +} while(0); +#define SMBPROFILE_STATS_IOBYTES(name) do { \ + count += print_iobytes_count_samples(buf, sizeof(buf), \ + #name, \ + ¤t->values.name##_stats, \ + &last->values.name##_stats, \ + delta_usec); \ +} while(0); +#define SMBPROFILE_STATS_SECTION_END +#define SMBPROFILE_STATS_END + SMBPROFILE_STATS_ALL_SECTIONS +#undef SMBPROFILE_STATS_START +#undef SMBPROFILE_STATS_SECTION_START +#undef SMBPROFILE_STATS_COUNT +#undef SMBPROFILE_STATS_TIME +#undef SMBPROFILE_STATS_BASIC +#undef SMBPROFILE_STATS_BYTES +#undef SMBPROFILE_STATS_IOBYTES +#undef SMBPROFILE_STATS_SECTION_END +#undef SMBPROFILE_STATS_END + + if (buf[0] != '\0') { + printf("%-40s\n", buf); + buf[0] = '\0'; + } + + return count; +} + +static struct profile_stats sample_data[2]; +static uint64_t sample_time[2]; + +bool status_profile_rates(bool verbose) +{ + uint64_t remain_usec; + uint64_t next_usec; + uint64_t delta_usec; + + int last = 0; + int current = 1; + int tmp; + + if (verbose) { + fprintf(stderr, "Sampling stats at %d sec intervals\n", + usec_to_sec(sample_interval_usec)); + } + + if (!profile_setup(NULL, True)) { + fprintf(stderr,"Failed to initialise profile memory\n"); + return False; + } + + smbprofile_collect(&sample_data[last]); + for (;;) { + sample_time[current] = profile_timestamp(); + next_usec = sample_time[current] + sample_interval_usec; + + /* Take a sample. */ + smbprofile_collect(&sample_data[current]); + + /* Rate convert some values and print results. */ + delta_usec = sample_time[current] - sample_time[last]; + + if (print_count_samples(&sample_data[current], + &sample_data[last], delta_usec)) { + printf("\n"); + } + + /* Swap sampling buffers. */ + tmp = last; + last = current; + current = tmp; + + /* Delay until next sample time. */ + remain_usec = next_usec - profile_timestamp(); + if (remain_usec > sample_interval_usec) { + fprintf(stderr, "eek! falling behind sampling rate!\n"); + } else { + if (verbose) { + fprintf(stderr, + "delaying for %lu msec\n", + (unsigned long )usec_to_msec(remain_usec)); + } + + usleep(remain_usec); + } + + } + + return True; +} diff --git a/source3/utils/status_profile.h b/source3/utils/status_profile.h new file mode 100644 index 0000000..eed54e0 --- /dev/null +++ b/source3/utils/status_profile.h @@ -0,0 +1,30 @@ +/* + * Samba Unix/Linux SMB client library + * Dump profiles + * Copyright (C) Volker Lendecke 2014 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __STATUS_PROFILE_H__ +#define __STATUS_PROFILE_H__ + +#include "replace.h" +#include "status.h" + +bool status_profile_dump(bool be_verbose, + struct traverse_state *state); +bool status_profile_rates(bool be_verbose); + +#endif diff --git a/source3/utils/status_profile_dummy.c b/source3/utils/status_profile_dummy.c new file mode 100644 index 0000000..9083abf --- /dev/null +++ b/source3/utils/status_profile_dummy.c @@ -0,0 +1,35 @@ +/* + * Unix SMB/CIFS implementation. + * Samba internal messaging functions + * Copyright (C) 2013 by Volker Lendecke + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "smbprofile.h" +#include "status_profile.h" + +bool status_profile_dump(bool be_verbose, + struct traverse_state *state) +{ + fprintf(stderr, "Profile data unavailable\n"); + return true; +} + +bool status_profile_rates(bool be_verbose) +{ + fprintf(stderr, "Profile data unavailable\n"); + return true; +} diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c new file mode 100644 index 0000000..fd90e8d --- /dev/null +++ b/source3/utils/testparm.c @@ -0,0 +1,1060 @@ +/* + Unix SMB/CIFS implementation. + Test validity of smb.conf + Copyright (C) Karl Auer 1993, 1994-1998 + + Extensively modified by Andrew Tridgell, 1995 + Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +/* + * Testbed for loadparm.c/params.c + * + * This module simply loads a specified configuration file and + * if successful, dumps it's contents to stdout. Note that the + * operation is performed with DEBUGLEVEL at 3. + * + * Useful for a quick 'syntax check' of a configuration file. + * + */ + +#include "includes.h" +#include "system/filesys.h" +#include "lib/cmdline/cmdline.h" +#include "lib/param/loadparm.h" +#include "lib/param/param.h" +#include "lib/crypto/gnutls_helpers.h" +#include "cmdline_contexts.h" + +#include <regex.h> + +/******************************************************************* + Check if a directory exists. +********************************************************************/ + +static bool directory_exist_stat(const char *dname,SMB_STRUCT_STAT *st) +{ + SMB_STRUCT_STAT st2; + bool ret; + + if (!st) + st = &st2; + + if (sys_stat(dname, st, false) != 0) + return(False); + + ret = S_ISDIR(st->st_ex_mode); + if(!ret) + errno = ENOTDIR; + return ret; +} + +struct idmap_config { + const char *domain_name; + const char *backend; + uint32_t high; + uint32_t low; +}; + +struct idmap_domains { + struct idmap_config *c; + uint32_t count; + uint32_t size; +}; + +static bool lp_scan_idmap_found_domain(const char *string, + regmatch_t matches[], + void *private_data) +{ + bool ok = false; + + if (matches[1].rm_so == -1) { + fprintf(stderr, "Found match, but no name - invalid idmap config"); + return false; + } + if (matches[1].rm_eo <= matches[1].rm_so) { + fprintf(stderr, "Invalid match - invalid idmap config"); + return false; + } + + { + struct idmap_domains *d = private_data; + struct idmap_config *c = &d->c[d->count]; + regoff_t len = matches[1].rm_eo - matches[1].rm_so; + char domname[len + 1]; + + if (d->count >= d->size) { + return false; + } + + memcpy(domname, string + matches[1].rm_so, len); + domname[len] = '\0'; + + c->domain_name = talloc_strdup_upper(d->c, domname); + if (c->domain_name == NULL) { + return false; + } + c->backend = talloc_strdup(d->c, lp_idmap_backend(domname)); + if (c->backend == NULL) { + return false; + } + + if (lp_server_role() != ROLE_ACTIVE_DIRECTORY_DC) { + ok = lp_idmap_range(domname, &c->low, &c->high); + if (!ok) { + fprintf(stderr, + "ERROR: Invalid idmap range for domain " + "%s!\n\n", + c->domain_name); + return false; + } + } + + d->count++; + } + + return false; /* Keep scanning */ +} + +static int idmap_config_int(const char *domname, const char *option, int def) +{ + int len = snprintf(NULL, 0, "idmap config %s", domname); + + if (len == -1) { + return def; + } + { + char config_option[len+1]; + snprintf(config_option, sizeof(config_option), + "idmap config %s", domname); + return lp_parm_int(-1, config_option, option, def); + } +} + +static bool do_idmap_check(void) +{ + struct idmap_domains *d; + uint32_t i; + bool ok = false; + int rc; + + d = talloc_zero(talloc_tos(), struct idmap_domains); + if (d == NULL) { + return false; + } + d->count = 0; + d->size = 32; + + d->c = talloc_array(d, struct idmap_config, d->size); + if (d->c == NULL) { + goto done; + } + + rc = lp_wi_scan_global_parametrics("idmapconfig\\(.*\\):backend", + 2, + lp_scan_idmap_found_domain, + d); + if (rc != 0) { + fprintf(stderr, + "FATAL: wi_scan_global_parametrics failed: %d", + rc); + } + + /* Check autorid backend */ + if (strequal(lp_idmap_default_backend(), "autorid")) { + struct idmap_config *c = NULL; + bool found = false; + + for (i = 0; i < d->count; i++) { + c = &d->c[i]; + + if (strequal(c->backend, "autorid")) { + found = true; + break; + } + } + + if (found) { + uint32_t rangesize = + idmap_config_int("*", "rangesize", 100000); + uint32_t maxranges = + (c->high - c->low + 1) / rangesize; + + if (((c->high - c->low + 1) % rangesize) != 0) { + fprintf(stderr, + "WARNING: The idmap autorid range " + "[%u-%u] SHOULD be a multiple of " + "the rangesize [%u]!" + "\n\n", + c->low, + c->high, + rangesize); + } + + if (maxranges < 2) { + fprintf(stderr, + "ERROR: The idmap autorid range " + "[%u-%u] needs to be at least twice as " + "big as the rangesize [%u]!" + "\n\n", + c->low, + c->high, + rangesize); + ok = false; + goto done; + } + } + } + + /* Check for overlapping idmap ranges */ + for (i = 0; i < d->count; i++) { + struct idmap_config *c = &d->c[i]; + uint32_t j; + + for (j = 0; j < d->count && j != i; j++) { + struct idmap_config *x = &d->c[j]; + + if ((c->low >= x->low && c->low <= x->high) || + (c->high >= x->low && c->high <= x->high)) { + /* + * Allow overlapping ranges for idmap_ad + * and idmap_nss + */ + ok = strequal(c->backend, x->backend); + if (ok) { + ok = strequal(c->backend, "ad") || + strequal(c->backend, "nss"); + if (ok) { + fprintf(stderr, + "NOTE: The idmap_%s " + "range for the domain " + "%s overlaps with the " + "range of %s.\n\n", + c->backend, + c->domain_name, + x->domain_name); + continue; + } + } + + fprintf(stderr, + "ERROR: The idmap range for the domain " + "%s (%s) overlaps with the range of " + "%s (%s)!\n\n", + c->domain_name, + c->backend, + x->domain_name, + x->backend); + ok = false; + goto done; + } + } + } + + ok = true; +done: + TALLOC_FREE(d); + return ok; +} + +/*********************************************** + Here we do a set of 'hard coded' checks for bad + configuration settings. +************************************************/ + +static int do_global_checks(void) +{ + int ret = 0; + SMB_STRUCT_STAT st; + const char *socket_options; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + + fprintf(stderr, "\n"); + + if (lp_security() >= SEC_DOMAIN && !lp_encrypt_passwords()) { + fprintf(stderr, "ERROR: in 'security=domain' mode the " + "'encrypt passwords' parameter must always be " + "set to 'true'.\n\n"); + ret = 1; + } + + if (lp_security() == SEC_ADS) { + const char *workgroup = lp_workgroup(); + const char *realm = lp_realm(); + + if (workgroup == NULL || strlen(workgroup) == 0) { + fprintf(stderr, + "ERROR: The 'security=ADS' mode requires " + "'workgroup' parameter to be set!\n\n "); + ret = 1; + } + + if (realm == NULL || strlen(realm) == 0) { + fprintf(stderr, + "ERROR: The 'security=ADS' mode requires " + "'realm' parameter to be set!\n\n "); + ret = 1; + } + } + + + if (lp_we_are_a_wins_server() && lp_wins_server_list()) { + fprintf(stderr, "ERROR: both 'wins support = true' and " + "'wins server = <server list>' cannot be set in " + "the smb.conf file. nmbd will abort with this " + "setting.\n\n"); + ret = 1; + } + + if (strequal(lp_workgroup(), lp_netbios_name())) { + fprintf(stderr, "WARNING: 'workgroup' and 'netbios name' " + "must differ.\n\n"); + } + + if (lp_client_ipc_signing() == SMB_SIGNING_IF_REQUIRED + || lp_client_ipc_signing() == SMB_SIGNING_OFF) { + fprintf(stderr, "WARNING: The 'client ipc signing' value " + "%s SMB signing is not used when contacting a " + "domain controller or other server. " + "This setting is not recommended; please be " + "aware of the security implications when using " + "this configuration setting.\n\n", + lp_client_ipc_signing() == SMB_SIGNING_OFF ? + "ensures" : "may mean"); + } + + if (strlen(lp_netbios_name()) > 15) { + fprintf(stderr, "WARNING: The 'netbios name' is too long " + "(max. 15 chars).\n\n"); + } + + if (!directory_exist_stat(lp_lock_directory(), &st)) { + fprintf(stderr, "ERROR: lock directory %s does not exist\n\n", + lp_lock_directory()); + ret = 1; + } else if ((st.st_ex_mode & 0777) != 0755) { + fprintf(stderr, "WARNING: lock directory %s should have " + "permissions 0755 for browsing to work\n\n", + lp_lock_directory()); + } + + if (!directory_exist_stat(lp_state_directory(), &st)) { + fprintf(stderr, "ERROR: state directory %s does not exist\n\n", + lp_state_directory()); + ret = 1; + } else if ((st.st_ex_mode & 0777) != 0755) { + fprintf(stderr, "WARNING: state directory %s should have " + "permissions 0755 for browsing to work\n\n", + lp_state_directory()); + } + + if (!directory_exist_stat(lp_cache_directory(), &st)) { + fprintf(stderr, "ERROR: cache directory %s does not exist\n\n", + lp_cache_directory()); + ret = 1; + } else if ((st.st_ex_mode & 0777) != 0755) { + fprintf(stderr, "WARNING: cache directory %s should have " + "permissions 0755 for browsing to work\n\n", + lp_cache_directory()); + } + + if (!directory_exist_stat(lp_pid_directory(), &st)) { + fprintf(stderr, "ERROR: pid directory %s does not exist\n\n", + lp_pid_directory()); + ret = 1; + } + + if (lp_passdb_expand_explicit()) { + fprintf(stderr, "WARNING: passdb expand explicit = yes is " + "deprecated\n\n"); + } + + /* + * Socket options. + */ + socket_options = lp_socket_options(); + if (socket_options != NULL && + (strstr(socket_options, "SO_SNDBUF") || + strstr(socket_options, "SO_RCVBUF") || + strstr(socket_options, "SO_SNDLOWAT") || + strstr(socket_options, "SO_RCVLOWAT"))) + { + fprintf(stderr, + "WARNING: socket options = %s\n" + "This warning is printed because you set one of the\n" + "following options: SO_SNDBUF, SO_RCVBUF, SO_SNDLOWAT,\n" + "SO_RCVLOWAT\n" + "Modern server operating systems are tuned for\n" + "high network performance in the majority of situations;\n" + "when you set 'socket options' you are overriding those\n" + "settings.\n" + "Linux in particular has an auto-tuning mechanism for\n" + "buffer sizes (SO_SNDBUF, SO_RCVBUF) that will be\n" + "disabled if you specify a socket buffer size. This can\n" + "potentially cripple your TCP/IP stack.\n\n" + "Getting the 'socket options' correct can make a big\n" + "difference to your performance, but getting them wrong\n" + "can degrade it by just as much. As with any other low\n" + "level setting, if you must make changes to it, make\n " + "small changes and test the effect before making any\n" + "large changes.\n\n", + socket_options); + } + + /* + * Password server sanity checks. + */ + + if((lp_security() >= SEC_DOMAIN) && !*lp_password_server()) { + const char *sec_setting; + if(lp_security() == SEC_DOMAIN) + sec_setting = "domain"; + else if(lp_security() == SEC_ADS) + sec_setting = "ads"; + else + sec_setting = ""; + + fprintf(stderr, "ERROR: The setting 'security=%s' requires the " + "'password server' parameter be set to the " + "default value * or a valid password server.\n\n", + sec_setting ); + ret = 1; + } + + if((lp_security() >= SEC_DOMAIN) && (strcmp(lp_password_server(), "*") != 0)) { + const char *sec_setting; + if(lp_security() == SEC_DOMAIN) + sec_setting = "domain"; + else if(lp_security() == SEC_ADS) + sec_setting = "ads"; + else + sec_setting = ""; + + fprintf(stderr, "WARNING: The setting 'security=%s' should NOT " + "be combined with the 'password server' " + "parameter.\n" + "(by default Samba will discover the correct DC " + "to contact automatically).\n\n", + sec_setting ); + } + + /* + * Password chat sanity checks. + */ + + if(lp_security() == SEC_USER && lp_unix_password_sync()) { + + /* + * Check that we have a valid lp_passwd_program() if not using pam. + */ + +#ifdef WITH_PAM + if (!lp_pam_password_change()) { +#endif + + if((lp_passwd_program(talloc_tos(), lp_sub) == NULL) || + (strlen(lp_passwd_program(talloc_tos(), lp_sub)) == 0)) + { + fprintf(stderr, + "ERROR: the 'unix password sync' " + "parameter is set and there is no valid " + "'passwd program' parameter.\n\n"); + ret = 1; + } else { + const char *passwd_prog; + char *truncated_prog = NULL; + const char *p; + + passwd_prog = lp_passwd_program(talloc_tos(), lp_sub); + p = passwd_prog; + next_token_talloc(talloc_tos(), + &p, + &truncated_prog, NULL); + if (truncated_prog && access(truncated_prog, F_OK) == -1) { + fprintf(stderr, + "ERROR: the 'unix password sync' " + "parameter is set and the " + "'passwd program' (%s) cannot be " + "executed (error was %s).\n\n", + truncated_prog, + strerror(errno)); + ret = 1; + } + } + +#ifdef WITH_PAM + } +#endif + + if(lp_passwd_chat(talloc_tos(), lp_sub) == NULL) { + fprintf(stderr, + "ERROR: the 'unix password sync' parameter is " + "set and there is no valid 'passwd chat' " + "parameter.\n\n"); + ret = 1; + } + + if ((lp_passwd_program(talloc_tos(), lp_sub) != NULL) && + (strlen(lp_passwd_program(talloc_tos(), lp_sub)) > 0)) + { + /* check if there's a %u parameter present */ + if(strstr_m(lp_passwd_program(talloc_tos(), lp_sub), "%u") == NULL) { + fprintf(stderr, + "ERROR: the 'passwd program' (%s) " + "requires a '%%u' parameter.\n\n", + lp_passwd_program(talloc_tos(), lp_sub)); + ret = 1; + } + } + + /* + * Check that we have a valid script and that it hasn't + * been written to expect the old password. + */ + + if(lp_encrypt_passwords()) { + if(strstr_m( lp_passwd_chat(talloc_tos(), lp_sub), "%o")!=NULL) { + fprintf(stderr, + "ERROR: the 'passwd chat' script [%s] " + "expects to use the old plaintext " + "password via the %%o substitution. With " + "encrypted passwords this is not " + "possible.\n\n", + lp_passwd_chat(talloc_tos(), lp_sub) ); + ret = 1; + } + } + } + + if (strlen(lp_winbind_separator()) != 1) { + fprintf(stderr, "ERROR: the 'winbind separator' parameter must " + "be a single character.\n\n"); + ret = 1; + } + + if (*lp_winbind_separator() == '+') { + fprintf(stderr, "'winbind separator = +' might cause problems " + "with group membership.\n\n"); + } + + if (lp_algorithmic_rid_base() < BASE_RID) { + /* Try to prevent admin foot-shooting, we can't put algorithmic + rids below 1000, that's the 'well known RIDs' on NT */ + fprintf(stderr, "'algorithmic rid base' must be equal to or " + "above %lu\n\n", BASE_RID); + } + + if (lp_algorithmic_rid_base() & 1) { + fprintf(stderr, "'algorithmic rid base' must be even.\n\n"); + } + + if (lp_server_role() != ROLE_STANDALONE) { + const char *default_backends[] = { + "tdb", "tdb2", "ldap", "autorid", "hash" + }; + const char *idmap_backend; + bool valid_backend = false; + uint32_t i; + bool ok; + + idmap_backend = lp_idmap_default_backend(); + + for (i = 0; i < ARRAY_SIZE(default_backends); i++) { + ok = strequal(idmap_backend, default_backends[i]); + if (ok) { + valid_backend = true; + } + } + + if (!valid_backend) { + ret = 1; + fprintf(stderr, "ERROR: Do not use the '%s' backend " + "as the default idmap backend!\n\n", + idmap_backend); + } + + ok = do_idmap_check(); + if (!ok) { + ret = 1; + } + } + +#ifndef HAVE_DLOPEN + if (lp_preload_modules()) { + fprintf(stderr, "WARNING: 'preload modules = ' set while loading " + "plugins not supported.\n\n"); + } +#endif + + if (!lp_passdb_backend()) { + fprintf(stderr, "ERROR: passdb backend must have a value or be " + "left out\n\n"); + } + + if (lp_os_level() > 255) { + fprintf(stderr, "WARNING: Maximum value for 'os level' is " + "255!\n\n"); + } + + if (strequal(lp_dos_charset(), "UTF8") || strequal(lp_dos_charset(), "UTF-8")) { + fprintf(stderr, "ERROR: 'dos charset' must not be UTF8\n\n"); + ret = 1; + } + + if (lp_server_schannel() != true) { /* can be 'auto' */ + fprintf(stderr, + "WARNING: You have not configured " + "'server schannel = yes' (the default). " + "Your server is vulnerable to \"ZeroLogon\" " + "(CVE-2020-1472)\n" + "If required use individual " + "'server require schannel:COMPUTERACCOUNT$ = no' " + "options\n\n"); + } + if (lp_allow_nt4_crypto()) { + fprintf(stderr, + "WARNING: You have not configured " + "'allow nt4 crypto = no' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023 and others!\n" + "If required use individual " + "'allow nt4 crypto:COMPUTERACCOUNT$ = yes' " + "options\n\n"); + } + if (!lp_reject_md5_clients()) { + fprintf(stderr, + "WARNING: You have not configured " + "'reject md5 clients = yes' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023!\n" + "If required use individual " + "'server reject md5 schannel:COMPUTERACCOUNT$ = yes' " + "options\n\n"); + } + if (!lp_server_schannel_require_seal()) { + fprintf(stderr, + "WARNING: You have not configured " + "'server schannel require seal = yes' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023!\n" + "If required use individual " + "'server schannel require seal:COMPUTERACCOUNT$ = no' " + "options\n\n"); + } + + if (lp_client_schannel() != true) { /* can be 'auto' */ + fprintf(stderr, + "WARNING: You have not configured " + "'client schannel = yes' (the default). " + "Your server is vulnerable to \"ZeroLogon\" " + "(CVE-2020-1472)\n" + "If required use individual " + "'client schannel:NETBIOSDOMAIN = no' " + "options\n\n"); + } + if (!lp_reject_md5_servers()) { + fprintf(stderr, + "WARNING: You have not configured " + "'reject md5 servers = yes' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023\n" + "If required use individual " + "'reject md5 servers:NETBIOSDOMAIN = no' " + "options\n\n"); + } + if (!lp_require_strong_key()) { + fprintf(stderr, + "WARNING: You have not configured " + "'require strong key = yes' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023\n" + "If required use individual " + "'require strong key:NETBIOSDOMAIN = no' " + "options\n\n"); + } + if (!lp_winbind_sealed_pipes()) { + fprintf(stderr, + "WARNING: You have not configured " + "'winbind sealed pipes = yes' (the default). " + "Your server is vulnerable to " + "CVE-2022-38023\n" + "If required use individual " + "'winbind sealed pipes:NETBIOSDOMAIN = no' " + "options\n\n"); + } + + if (lp_kerberos_encryption_types() == KERBEROS_ETYPES_LEGACY) { + fprintf(stderr, + "WARNING: You have configured " + "'kerberos encryption types = legacy'. " + "Your server is vulnerable to " + "CVE-2022-37966\n\n"); + } + + return ret; +} + +/** + * per-share logic tests + */ +static void do_per_share_checks(int s) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char **deny_list = lp_hosts_deny(s); + const char **allow_list = lp_hosts_allow(s); + const char **vfs_objects = NULL; + int i; + static bool uses_fruit; + static bool doesnt_use_fruit; + static bool fruit_mix_warned; + + if(deny_list) { + for (i=0; deny_list[i]; i++) { + char *hasstar = strchr_m(deny_list[i], '*'); + char *hasquery = strchr_m(deny_list[i], '?'); + if(hasstar || hasquery) { + fprintf(stderr, + "Invalid character %c in hosts deny list " + "(%s) for service %s.\n\n", + hasstar ? *hasstar : *hasquery, + deny_list[i], + lp_servicename(talloc_tos(), lp_sub, s)); + } + } + } + + if(allow_list) { + for (i=0; allow_list[i]; i++) { + char *hasstar = strchr_m(allow_list[i], '*'); + char *hasquery = strchr_m(allow_list[i], '?'); + if(hasstar || hasquery) { + fprintf(stderr, + "Invalid character %c in hosts allow " + "list (%s) for service %s.\n\n", + hasstar ? *hasstar : *hasquery, + allow_list[i], + lp_servicename(talloc_tos(), lp_sub, s)); + } + } + } + + if(lp_level2_oplocks(s) && !lp_oplocks(s)) { + fprintf(stderr, "Invalid combination of parameters for service " + "%s. Level II oplocks can only be set if oplocks " + "are also set.\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + + if (!lp_store_dos_attributes(s) && lp_map_hidden(s) + && !(lp_create_mask(s) & S_IXOTH)) + { + fprintf(stderr, + "Invalid combination of parameters for service %s. Map " + "hidden can only work if create mask includes octal " + "01 (S_IXOTH).\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + if (!lp_store_dos_attributes(s) && lp_map_hidden(s) + && (lp_force_create_mode(s) & S_IXOTH)) + { + fprintf(stderr, + "Invalid combination of parameters for service " + "%s. Map hidden can only work if force create mode " + "excludes octal 01 (S_IXOTH).\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + if (!lp_store_dos_attributes(s) && lp_map_system(s) + && !(lp_create_mask(s) & S_IXGRP)) + { + fprintf(stderr, + "Invalid combination of parameters for service " + "%s. Map system can only work if create mask includes " + "octal 010 (S_IXGRP).\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + if (!lp_store_dos_attributes(s) && lp_map_system(s) + && (lp_force_create_mode(s) & S_IXGRP)) + { + fprintf(stderr, + "Invalid combination of parameters for service " + "%s. Map system can only work if force create mode " + "excludes octal 010 (S_IXGRP).\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + if (lp_printing(s) == PRINT_CUPS && *(lp_print_command(s)) != '\0') { + fprintf(stderr, + "Warning: Service %s defines a print command, but " + "parameter is ignored when using CUPS libraries.\n\n", + lp_servicename(talloc_tos(), lp_sub, s)); + } + + vfs_objects = lp_vfs_objects(s); + if (vfs_objects && str_list_check(vfs_objects, "fruit")) { + uses_fruit = true; + } else { + doesnt_use_fruit = true; + } + + if (uses_fruit && doesnt_use_fruit && !fruit_mix_warned) { + fruit_mix_warned = true; + fprintf(stderr, + "WARNING: some services use vfs_fruit, others don't. Mounting them " + "in conjunction on OS X clients results in undefined behaviour.\n\n"); + } +} + + int main(int argc, const char *argv[]) +{ + const char *config_file = NULL; + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + int opt; + int s; + static int silent_mode = False; + static int show_all_parameters = False; + int ret = 0; + poptContext pc; + static char *parameter_name = NULL; + static const char *section_name = NULL; + const char *cname; + const char *caddr; + static int show_defaults; + static int skip_logic_checks = 0; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "suppress-prompt", + .shortName = 's', + .argInfo = POPT_ARG_VAL, + .arg = &silent_mode, + .val = 1, + .descrip = "Suppress prompt for enter", + }, + { + .longName = "verbose", + .shortName = 'v', + .argInfo = POPT_ARG_NONE, + .arg = &show_defaults, + .val = 1, + .descrip = "Show default options too", + }, + { + .longName = "skip-logic-checks", + .shortName = 'l', + .argInfo = POPT_ARG_NONE, + .arg = &skip_logic_checks, + .val = 1, + .descrip = "Skip the global checks", + }, + { + .longName = "show-all-parameters", + .shortName = '\0', + .argInfo = POPT_ARG_VAL, + .arg = &show_all_parameters, + .val = True, + .descrip = "Show the parameters, type, possible " + "values", + }, + { + .longName = "parameter-name", + .shortName = '\0', + .argInfo = POPT_ARG_STRING, + .arg = ¶meter_name, + .val = 0, + .descrip = "Limit testparm to a named parameter", + }, + { + .longName = "section-name", + .shortName = '\0', + .argInfo = POPT_ARG_STRING, + .arg = §ion_name, + .val = 0, + .descrip = "Limit testparm to a named section", + }, + POPT_COMMON_DEBUG_ONLY + POPT_COMMON_OPTION_ONLY + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + TALLOC_CTX *frame = talloc_stackframe(); + struct loadparm_context *lp_ctx = NULL; + + smb_init_locale(); + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_NONE, + true /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + ret = 1; + goto done; + } + lp_ctx = samba_cmdline_get_lp_ctx(); + + /* + * Set the default debug level to 1. + * Allow it to be overridden by the command line, + * not by smb.conf. + */ + lpcfg_set_cmdline(lp_ctx, "log level", "1"); + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + ret = 1; + goto done; + } + + poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case POPT_ERROR_BADOPT: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (show_all_parameters) { + show_parameter_list(); + exit(0); + } + + if (poptPeekArg(pc)) { + config_file = talloc_strdup(frame, poptGetArg(pc)); + if (config_file == NULL) { + DBG_ERR("out of memory\n"); + TALLOC_FREE(frame); + exit(1); + } + } else { + config_file = get_dyn_CONFIGFILE(); + } + + cname = talloc_strdup(frame, poptGetArg(pc)); + caddr = talloc_strdup(frame, poptGetArg(pc)); + + poptFreeContext(pc); + + if ( cname && ! caddr ) { + printf ( "ERROR: You must specify both a machine name and an IP address.\n" ); + ret = 1; + goto done; + } + + fprintf(stderr,"Load smb config files from %s\n",config_file); + + if (!lp_load_with_registry_shares(config_file)) { + fprintf(stderr,"Error loading services.\n"); + ret = 1; + goto done; + } + + fprintf(stderr,"Loaded services file OK.\n"); + + fprintf(stderr, + "Weak crypto is %sallowed by GnuTLS " + "(e.g. NTLM as a compatibility fallback)\n", + samba_gnutls_weak_crypto_allowed() ? "" : "dis"); + + if (skip_logic_checks == 0) { + ret = do_global_checks(); + } + + for (s=0;s<1000;s++) { + if (VALID_SNUM(s) && (skip_logic_checks == 0)) { + do_per_share_checks(s); + } + } + + + if (!section_name && !parameter_name) { + fprintf(stderr, + "Server role: %s\n\n", + server_role_str(lp_server_role())); + } + + if (!cname) { + if (!silent_mode) { + fprintf(stderr,"Press enter to see a dump of your service definitions\n"); + fflush(stdout); + getc(stdin); + } + if (parameter_name || section_name) { + bool isGlobal = False; + s = GLOBAL_SECTION_SNUM; + + if (!section_name) { + section_name = GLOBAL_NAME; + isGlobal = True; + } else if ((isGlobal=!strwicmp(section_name, GLOBAL_NAME)) == 0 && + (s=lp_servicenumber(section_name)) == -1) { + fprintf(stderr,"Unknown section %s\n", + section_name); + ret = 1; + goto done; + } + if (parameter_name) { + if (!dump_a_parameter( s, parameter_name, stdout, isGlobal)) { + fprintf(stderr,"Parameter %s unknown for section %s\n", + parameter_name, section_name); + ret = 1; + goto done; + } + } else { + if (isGlobal == True) + lp_dump(stdout, show_defaults, 0); + else + lp_dump_one(stdout, show_defaults, s); + } + goto done; + } + + lp_dump(stdout, show_defaults, lp_numservices()); + } + + if(cname && caddr){ + /* this is totally ugly, a real `quick' hack */ + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) { + if (allow_access(lp_hosts_deny(-1), lp_hosts_allow(-1), cname, caddr) + && allow_access(lp_hosts_deny(s), lp_hosts_allow(s), cname, caddr)) { + fprintf(stderr,"Allow connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(talloc_tos(), lp_sub, s)); + } else { + fprintf(stderr,"Deny connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(talloc_tos(), lp_sub, s)); + } + } + } + } + +done: + gfree_loadparm(); + TALLOC_FREE(frame); + return ret; +} diff --git a/source3/utils/wscript_build b/source3/utils/wscript_build new file mode 100644 index 0000000..ca57e80 --- /dev/null +++ b/source3/utils/wscript_build @@ -0,0 +1,364 @@ +#!/usr/bin/env python + +bld.SAMBA3_SUBSYSTEM('PASSWD_UTIL', + source='passwd_util.c', + deps='samba-util') + +bld.SAMBA3_SUBSYSTEM('CONN_TDB', + source='conn_tdb.c') + +bld.SAMBA3_SUBSYSTEM('DNS_UTIL', + source='net_dns.c net_ads_join_dns.c', + deps='addns') + +bld.SAMBA3_BINARY('profiles', + source='profiles.c', + deps=''' + talloc + CMDLINE_S3 + smbconf + REGFIO''') + +bld.SAMBA3_BINARY('smbcontrol', + source='smbcontrol.c', + deps=''' + talloc + smbconf + CMDLINE_S3 + cmdline_contexts + PRINTBASE''') + +bld.SAMBA3_BINARY('smbtree', + source='smbtree.c', + deps=''' + talloc + smbconf + smbclient + msrpc3 + CMDLINE_S3 + RPC_NDR_SRVSVC''') + +bld.SAMBA3_BINARY('smbpasswd', + source='smbpasswd.c', + deps=''' + talloc + smbconf + pdb + PASSWD_UTIL + PASSCHANGE + cmdline_contexts + ''') + +bld.SAMBA3_BINARY('pdbedit', + source='pdbedit.c', + deps=''' + talloc + smbconf + CMDLINE_S3 + cmdline_contexts + pdb + PASSWD_UTIL''') + +bld.SAMBA3_BINARY('smbget', + source='smbget.c', + deps=''' + talloc + CMDLINE_S3 + smbclient''') + +bld.SAMBA3_BINARY('nmblookup', + source='nmblookup.c', + deps=''' + talloc + smbconf + CMDLINE_S3 + LIBNMB''') + +bld.SAMBA3_BINARY('smbcacls', + source='smbcacls.c', + deps=''' + talloc + CMDLINE_S3 + msrpc3 + libcli_lsa3 + util_sd + krb5samba''') + +bld.SAMBA3_BINARY('smbcquotas', + source='smbcquotas.c', + deps=''' + talloc + CMDLINE_S3 + libsmb + msrpc3 + libcli_lsa3''') + +bld.SAMBA3_BINARY('eventlogadm', + source='eventlogadm.c', + deps=''' + talloc + smbconf + cmdline_contexts + LIBEVENTLOG''', + install_path='${SBINDIR}') + +bld.SAMBA3_BINARY('sharesec', + source='sharesec.c', + deps=''' + talloc + msrpc3 + libcli_lsa3 + CMDLINE_S3 + cmdline_contexts + util_sd + ''') + +bld.SAMBA3_BINARY('log2pcap', + source='log2pcaphex.c', + deps='''talloc popt''', + install=False) + +bld.SAMBA3_BINARY('smbfilter', + source='smbfilter.c', + deps=''' + talloc + smbconf + LIBNMB''', + install=False) + +bld.SAMBA3_BINARY('ntlm_auth', + source='''ntlm_auth.c ntlm_auth_diagnostics.c''', + deps=''' + talloc + krb5samba + tiniparser + libsmb + CMDLINE_S3 + cmdline_contexts + wbclient + gse gensec''') + +bld.SAMBA3_BINARY('dbwrap_tool', + source='dbwrap_tool.c', + deps=''' + talloc + CMDLINE_S3 + cmdline_contexts + ''') + +bld.SAMBA3_BINARY('dbwrap_torture', + source='dbwrap_torture.c', + deps=''' + talloc + CMDLINE_S3 + smbconf + ''', + install=False) + +bld.SAMBA3_BINARY('samba-regedit', + source="""regedit.c regedit_samba3.c + regedit_wrap.c regedit_treeview.c + regedit_valuelist.c regedit_dialog.c + regedit_hexedit.c regedit_list.c""", + deps=''' + ncurses + menu + panel + form + registry + smbconf + CMDLINE_S3 + ''', + enabled=bld.env.build_regedit) + +bld.SAMBA3_BINARY('testparm', + source='testparm.c', + deps=''' + talloc + smbconf + CMDLINE_S3 + cmdline_contexts + GNUTLS_HELPERS + ''') + +bld.SAMBA3_BINARY('net', + source='''net.c + net_ads.c + net_help.c + clirap2.c + net_rap.c + net_rpc.c + net_rpc_samsync.c + net_time.c + net_lookup.c + net_cache.c + net_groupmap.c + net_idmap.c + net_idmap_check.c + interact.c + net_status.c + net_rpc_printer.c + net_rpc_rights.c + net_rpc_service.c + net_rpc_registry.c + net_usershare.c + netlookup.c + net_sam.c + net_rpc_shell.c + net_util.c + net_rpc_sh_acct.c + net_rpc_audit.c + net_ads_gpo.c + net_conf.c + net_conf_util.c + net_join.c + net_offlinejoin.c + net_user.c + net_group.c + net_file.c + net_registry.c + net_registry_check.c + net_dom.c + net_share.c + net_g_lock.c + net_serverid.c + net_eventlog.c + net_printing.c + net_rpc_trust.c + net_rpc_conf.c + net_afs.c + net_notify.c + net_tdb.c + net_witness.c + net_vfs.c + ../registry/reg_format.c + ../registry/reg_import.c + net_registry_util.c + net_help_common.c''', + deps=''' + talloc + netapi + addns + samba_intl + CMDLINE_S3 + cmdline_contexts + pdb + libsmb + smbconf + KRBCLIENT + ndr-standard + msrpc3 + gpo + ads + smbd_base + LIBADS_SERVER + LIBADS_PRINTER + SMBREADLINE + PASSWD_UTIL + LIBNET + LIBNET_DSSYNC + LIBEVENTLOG + REGFIO + NDR_NTPRINTING + RPC_NDR_WINREG + RPC_CLIENT_SCHANNEL + LIBCLI_SAMR + libcli_lsa3 + libcli_netlogon3 + cli_spoolss + RPC_NDR_SRVSVC + RPC_NDR_SVCCTL + RPC_NDR_DSSETUP + RPC_NDR_INITSHUTDOWN + printing_migrate + trusts_util + IDMAP_AUTORID_TDB + CONN_TDB + jansson + common_auth + ADOUBLE + DNS_UTIL + util_sd + ''') + +bld.SAMBA3_BINARY('mvxattr', + source='mvxattr.c', + deps=''' + talloc + popt + samba-util + ''', + enabled=bld.env.build_mvxattr) + +bld.SAMBA3_BINARY('destroy_netlogon_creds_cli', + source='destroy_netlogon_creds_cli.c', + deps = ''' + talloc + smbconf + NETLOGON_CREDS_CLI + ''', + install=False) + +smbstatus_source = 'status.c' + +if bld.CONFIG_GET("WITH_PROFILE"): + smbstatus_source += ' status_profile.c' +else: + smbstatus_source += ' status_profile_dummy.c' + +if bld.CONFIG_GET("HAVE_JANSSON"): + smbstatus_source += ' status_json.c' +else: + smbstatus_source += ' status_json_dummy.c' + +bld.SAMBA3_BINARY('smbstatus', + source=smbstatus_source, + deps=''' + talloc + smbconf + CMDLINE_S3 + cmdline_contexts + smbd_base + LOCKING + PROFILE + CONN_TDB + ''') + +bld.SAMBA3_BINARY('mdsearch', + source='mdsearch.c', + deps=''' + talloc + tevent + smbconf + CMDLINE_S3 + cmdline_contexts + libsmb + msrpc3 + RPCCLI_MDSSVC + mdssvc + ''') + +bld.SAMBA3_BINARY('wspsearch', + source='wspsearch.c', + deps=''' + talloc + tevent + smbconf + CMDLINE_S3 + cmdline_contexts + libsmb + msrpc3 + LIBSAMBA_WSP + RPCCLI_WSP + WSP_UTIL + dcerpc + ''', + enabled=bld.env.with_wsp) + +pytalloc_util = bld.pyembed_libname('pytalloc-util') +pyrpc_util = bld.pyembed_libname('pyrpc_util') +bld.SAMBA3_PYTHON('python_net_s3', + source='py_net.c', + deps='LIBNET DNS_UTIL cmdline_contexts %s %s' % (pytalloc_util, pyrpc_util), + realname='samba/net_s3.so' + ) diff --git a/source3/utils/wspsearch.c b/source3/utils/wspsearch.c new file mode 100644 index 0000000..063b952 --- /dev/null +++ b/source3/utils/wspsearch.c @@ -0,0 +1,842 @@ +/* + * Unix SMB/CIFS implementation. + * + * Window Search Service + * + * Copyright (c) Noel Power + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ +#include "includes.h" +#include "lib/util/debug.h" +#include "lib/cmdline/cmdline.h" +#include "lib/cmdline_contexts.h" +#include "param.h" +#include "client.h" +#include "libsmb/proto.h" +#include "librpc/rpc/rpc_common.h" +#include "librpc/wsp/wsp_util.h" +#include "rpc_client/cli_pipe.h" +#include "rpc_client/wsp_cli.h" +#include "libcli/wsp/wsp_aqs.h" +#include "librpc/gen_ndr/ndr_wsp.h" +#include "librpc/gen_ndr/ndr_wsp_data.h" +#include "dcerpc.h" + +#define WIN_VERSION_64 0x10000 + +/* send connectin message */ +static NTSTATUS wsp_connect(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + const char* clientmachine, + const char* clientuser, + const char* server, + bool *is_64bit) +{ + struct wsp_request *request = NULL; + struct wsp_response *response = NULL; + uint32_t client_ver; + uint32_t server_ver; + DATA_BLOB unread = data_blob_null; + NTSTATUS status; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + + if (local_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + response = talloc_zero(local_ctx, struct wsp_response); + if (!response) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + request = talloc_zero(local_ctx, struct wsp_request); + if (!request) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + if (!init_connectin_request(local_ctx, request, + clientmachine, clientuser, server)) { + DBG_ERR("Failed in initialise connection message\n"); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + status = wsp_request_response(local_ctx, wsp_ctx, + request, response, &unread); + if (NT_STATUS_IS_OK(status)) { + client_ver = request->message.cpmconnect.iclientversion; + server_ver = response->message.cpmconnect.server_version; + *is_64bit = + (server_ver & WIN_VERSION_64) + && (client_ver & WIN_VERSION_64); + } + +out: + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + +static NTSTATUS create_query(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + uint32_t limit, + t_select_stmt *select, + uint32_t *single_cursor) +{ + struct wsp_request *request = NULL; + struct wsp_response *response = NULL; + NTSTATUS status; + DATA_BLOB unread = data_blob_null; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + if (local_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + request = talloc_zero(local_ctx, struct wsp_request); + if (!request) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + response = talloc_zero(local_ctx, struct wsp_response); + if (!response) { + status = NT_STATUS_NO_MEMORY; + goto out;; + } + + if (!create_querysearch_request(ctx, request, select)) { + DBG_ERR("error setting up query request message\n"); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + request->message.cpmcreatequery.rowsetproperties.cmaxresults = limit; + + status = wsp_request_response(local_ctx, + wsp_ctx, + request, + response, + &unread); + if (NT_STATUS_IS_OK(status)) { + if (unread.length == 4) { + *single_cursor = IVAL(unread.data, 0); + } + } + +out: + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + +static NTSTATUS create_bindings(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + t_select_stmt *select, + uint32_t cursor, + struct wsp_cpmsetbindingsin *bindings_out, + bool is_64bit) +{ + struct wsp_request *request = NULL; + struct wsp_response *response = NULL; + NTSTATUS status; + DATA_BLOB unread = data_blob_null; + + request = talloc_zero(ctx, struct wsp_request); + if (!request) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + response = talloc_zero(ctx, struct wsp_response); + if (!response) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + if (!create_setbindings_request(ctx, + request, + select, + cursor, + is_64bit)) { + DBG_ERR("Failed to create setbindings message\n"); + status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + status = wsp_request_response(ctx, + wsp_ctx, + request, + response, + &unread); + if (NT_STATUS_IS_OK(status)) { + *bindings_out = request->message.cpmsetbindings; + } + +out: + data_blob_free(&unread); + return status; +} + +static NTSTATUS create_querystatusex(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + uint32_t cursor, + uint32_t *nrows) +{ + struct wsp_request *request = NULL; + struct wsp_response *response = NULL; + struct wsp_cpmgetquerystatusexin *statusexin = NULL; + NTSTATUS status; + DATA_BLOB unread = data_blob_null; + TALLOC_CTX *local_ctx = talloc_new(ctx); + + if (local_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + request = talloc_zero(local_ctx, struct wsp_request); + if (!request) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + response = talloc_zero(local_ctx, struct wsp_response); + if (!response) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + statusexin = &request->message.cpmgetquerystatusex; + + request->header.msg = CPMGETQUERYSTATUSEX; + statusexin->hcursor = cursor; + statusexin->bmk = 0xfffffffc; + status = wsp_request_response(local_ctx, + wsp_ctx, + request, + response, + &unread); + if (NT_STATUS_IS_OK(status)) { + *nrows = response->message.cpmgetquerystatusex.resultsfound; + } + +out: + data_blob_free(&unread); + TALLOC_FREE(local_ctx); + return status; +} + +static NTSTATUS print_rowsreturned( + TALLOC_CTX *ctx, + DATA_BLOB *buffer, + bool is_64bit, + bool disp_all_cols, + struct wsp_cpmsetbindingsin *bindings, + uint32_t cbreserved, + uint64_t address, + uint32_t rowsreturned, + uint32_t *rows_processed) +{ + NTSTATUS status; + uint32_t row = 0; + TALLOC_CTX *local_ctx = NULL; + struct wsp_cbasestoragevariant **rowsarray = NULL; + enum ndr_err_code err; + + local_ctx = talloc_init("results"); + if (local_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + rowsarray = talloc_zero_array(local_ctx, + struct wsp_cbasestoragevariant*, + rowsreturned); + if (rowsarray == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + err = extract_rowsarray(rowsarray, + buffer, + is_64bit, + bindings, + cbreserved, + address, + rowsreturned, + rowsarray); + if (err) { + DBG_ERR("failed to extract rows from getrows response\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + + for(row = 0; row < rowsreturned; row++) { + TALLOC_CTX *row_ctx = NULL; + const char *col_str = NULL; + + row_ctx = talloc_init("row"); + if (row_ctx == NULL) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + if (disp_all_cols) { + int i; + for (i = 0; i < bindings->ccolumns; i++){ + col_str = + variant_as_string( + row_ctx, + &rowsarray[row][i], + true); + if (col_str) { + printf("%s%s", + i ? ", " : "", col_str); + } else { + printf("%sN/A", + i ? ", " : ""); + } + } + } else { + col_str = variant_as_string( + row_ctx, + &rowsarray[row][0], + true); + printf("%s", col_str); + } + printf("\n"); + TALLOC_FREE(row_ctx); + } + status = NT_STATUS_OK; +out: + TALLOC_FREE(local_ctx); + *rows_processed = row; + return status; +} + +static NTSTATUS create_getrows(TALLOC_CTX *ctx, + struct wsp_client_ctx *wsp_ctx, + struct wsp_cpmsetbindingsin *bindings, + uint32_t cursor, + uint32_t nrows, + bool disp_all_cols, + bool is_64bit) +{ + struct wsp_request *request = NULL; + struct wsp_response *response = NULL; + NTSTATUS status; + DATA_BLOB unread = data_blob_null; + uint32_t bmk = 0xfffffffc; + uint32_t skip = 0; + uint32_t total_rows = 0; + uint32_t INITIAL_ROWS = 32; + uint32_t requested_rows = INITIAL_ROWS; + uint32_t rows_printed; + uint64_t baseaddress; + uint32_t offset_lowbits = 0xdeabd860; + uint32_t offset_hibits = 0xfeeddeaf; + + TALLOC_CTX *row_ctx; + bool loop_again; + + do { + row_ctx = talloc_new(NULL); + if (!row_ctx) { + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + request = talloc_zero(row_ctx, struct wsp_request); + if (!request) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + response = talloc_zero(row_ctx, struct wsp_response); + if (!response) { + status = NT_STATUS_NO_MEMORY; + goto out; + } + + create_seekat_getrows_request(request, + request, + cursor, + bmk, + skip, + requested_rows, + 40, + offset_lowbits, + bindings->brow, + 0); + + if (is_64bit) { + /* + * MS-WSP 2.2.2 + * ulreservered holds the high 32-bits part of + * a 64-bit offset if 64-bit offsets are being used. + */ + request->header.ulreserved2 = offset_hibits; + baseaddress = request->header.ulreserved2; + baseaddress <<= 32; + baseaddress += offset_lowbits; + } else { + baseaddress = offset_lowbits; + } + + status = wsp_request_response(request, + wsp_ctx, + request, + response, + &unread); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + + total_rows += response->message.cpmgetrows.rowsreturned; + if (response->message.cpmgetrows.rowsreturned + != requested_rows) { + uint32_t rowsreturned = + response->message.cpmgetrows.rowsreturned; + if (response->message.cpmgetrows.etype == EROWSEEKAT) { + struct wsp_cpmgetrowsout *resp; + struct wsp_crowseekat *seekat; + resp = &response->message.cpmgetrows; + seekat = + &resp->seekdescription.crowseekat; + bmk = seekat->bmkoffset; + skip = seekat->cskip; + } else { + bmk = 0xfffffffc; + skip = total_rows; + } + requested_rows = requested_rows - rowsreturned; + } else { + requested_rows = INITIAL_ROWS; + bmk = 0xfffffffc; + skip = total_rows; + } + + if (response->message.cpmgetrows.rowsreturned) { + status = print_rowsreturned(row_ctx, &unread, + is_64bit, + disp_all_cols, + bindings, 40, + baseaddress, + response->message.cpmgetrows.rowsreturned, + &rows_printed); + if (!NT_STATUS_IS_OK(status)) { + goto out; + } + data_blob_free(&unread); + } + + /* + * response is a talloc child of row_ctx so we need to + * assign loop_again before we delete row_ctx + */ + loop_again = response->message.cpmgetrows.rowsreturned; + + TALLOC_FREE(row_ctx); + if (nrows && total_rows > nrows) { + DBG_ERR("Something is wrong, results returned %d " + "exceed expected number of results %d\n", + total_rows, nrows); + status = NT_STATUS_UNSUCCESSFUL; + goto out; + } + } while (loop_again); +out: + data_blob_free(&unread); + TALLOC_FREE(row_ctx); + return status; +} + +const char *default_column = "System.ItemUrl"; + +static bool is_valid_kind(const char *kind) +{ + const char* kinds[] = {"calendar", + "communication", + "contact", + "document", + "email", + "feed", + "folder", + "game", + "instantMessage", + "journal", + "link", + "movie", + "music", + "note", + "picture", + "program", + "recordedtv", + "searchfolder", + "task", + "video", + "webhistory"}; + char* search_kind = NULL; + int i; + bool found = false; + + search_kind = strlower_talloc(NULL, kind); + if (search_kind == NULL) { + DBG_ERR("couldn't convert %s to lower case\n", + kind); + return NULL; + } + + for (i=0; i<ARRAY_SIZE(kinds); i++) { + if (strequal(search_kind, kinds[i])) { + found = true; + break; + } + } + + if (found == false) { + DBG_ERR("Invalid kind %s\n", kind); + } + TALLOC_FREE(search_kind); + return found; +} + +static char * build_default_sql(TALLOC_CTX *ctx, + const char *kind, + const char *phrase, + const char *location) +{ + char *sql = NULL; + /* match what windows clients do */ + sql = talloc_asprintf(ctx, + "Scope:\"%s\" AND NOT System.Shell.SFGAOFlagsStrings:hidden" + " AND NOT System.Shell.OmitFromView:true", location); + + if (kind) { + if (!is_valid_kind(kind)) { + return NULL; + } + sql = talloc_asprintf(ctx, "System.Kind:%s AND %s", + kind, sql); + } + + if (phrase) { + sql = talloc_asprintf(ctx, + "All:$=\"%s\" OR All:$<\"%s\"" + " AND %s", phrase, phrase, sql); + } + sql = talloc_asprintf(ctx, "SELECT %s" + " WHERE %s", default_column, sql); + return sql; +} + +int main(int argc, char **argv) +{ + int opt; + int result = 0; + NTSTATUS status = NT_STATUS_OK; + poptContext pc; + char* server = NULL; + char* share = NULL; + char* path = NULL; + char* location = NULL; + char* query = NULL; + bool custom_query = false; + const char* phrase = NULL; + const char* kind = NULL; + uint32_t limit = 500; + uint32_t nrows = 0; + struct wsp_cpmsetbindingsin bindings_used = {0}; + bool is_64bit = false; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "limit", + 0, + POPT_ARG_INT, + &limit, + 0, + "limit results", + "default is 500, specifying 0 means unlimited" }, + { "search", + 0, + POPT_ARG_STRING, + &phrase, + 0, + "Search phrase", + "phrase" }, + { "kind", 0, POPT_ARG_STRING, &kind, 0, + "Kind of thing to search for [Calendar|Communication|" + "Contact|Document|Email|Feed|Folder|Game|" + "InstantMessage|Journal|Link|Movie|Music|Note|Picture|" + "Program|RecordedTV|SearchFolder|Task|Video" + "|WebHistory]", + "kind" }, + { "query", + 0, + POPT_ARG_STRING, + &query, + 0, + "specify a more complex query", + "query" }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + TALLOC_CTX *frame = talloc_stackframe(); + struct tevent_context *ev_ctx + = samba_tevent_context_init(talloc_tos()); + uint32_t cursor = 0; + struct wsp_client_ctx *wsp_ctx = NULL; + t_select_stmt *select_stmt = NULL; + const char **const_argv = discard_const_p(const char *, argv); + struct dcerpc_binding_handle *h = NULL; + struct cli_state *c = NULL; + uint32_t flags = CLI_FULL_CONNECTION_IPC; + bool ok; + + ok = samba_cmdline_init(frame, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to set up cmdline parser\n"); + result = -1; + goto out; + } + + pc = samba_popt_get_context("wspsearch", + argc, + const_argv, + long_options, + 0); + poptSetOtherOptionHelp(pc, "[OPTIONS] //server1/share1"); + + while ((opt = poptGetNextOpt(pc)) != -1) ; + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + result = -1; + goto out; + } + + path = talloc_strdup(talloc_tos(), poptGetArg(pc)); + if (!path) { + DBG_ERR("Invalid argument\n"); + result = -1; + goto out; + } + + string_replace(path,'/','\\'); + server = talloc_strdup(talloc_tos(), path+2); + if (!server) { + DBG_ERR("Invalid argument\n"); + return -1; + } + + if (server) { + /* + * if we specify --query then we don't need actually need the + * share part, if it is specified then we don't care as we + * expect the scope to be part of the query (and if it isn't + * then it will probably fail anyway) + */ + share = strchr_m(server,'\\'); + if (!query && !share) { + DBG_ERR("Invalid argument\n"); + return -1; + } + if (share) { + *share = 0; + share++; + } + } + + DBG_INFO("server name is %s\n", server ? server : "N/A"); + DBG_INFO("share name is %s\n", share ? share : "N/A"); + DBG_INFO("search phrase is %s\n", phrase ? phrase : "N/A"); + DBG_INFO("search kind is %s\n", kind ? kind : "N/A"); + + if (!query && (kind == NULL && phrase == NULL)) { + poptPrintUsage(pc, stderr, 0); + result = -1; + goto out; + } + + if (!query) { + location = talloc_asprintf(talloc_tos(), + "FILE://%s/%s", server, share); + query = build_default_sql(talloc_tos(), kind, phrase, location); + if (!query) { + result = -1; + goto out; + } + } else { + custom_query = true; + } + + printf("custom_query %d\n", custom_query); + select_stmt = get_wsp_sql_tree(query); + + poptFreeContext(pc); + + if (select_stmt == NULL) { + DBG_ERR("query failed\n"); + result = -1; + goto out; + } + + if (select_stmt->cols == NULL) { + select_stmt->cols = talloc_zero(select_stmt, t_col_list); + if (select_stmt->cols == NULL) { + DBG_ERR("out of memory\n"); + result = -1; + goto out; + } + select_stmt->cols->num_cols = 1; + select_stmt->cols->cols = + talloc_zero_array(select_stmt->cols, char*, 1); + if (select_stmt->cols->cols == NULL) { + DBG_ERR("out of memory\n"); + result = -1; + goto out; + } + select_stmt->cols->cols[0] = + talloc_strdup(select_stmt->cols, default_column); + } + + status = cli_full_connection_creds(&c, + lp_netbios_name(), + server, + NULL, + 0, + "IPC$", + "IPC", + samba_cmdline_get_creds(), + flags); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to IPC$: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + + status = wsp_server_connect(talloc_tos(), + server, + ev_ctx, + samba_cmdline_get_lp_ctx(), + samba_cmdline_get_creds(), + c, + &wsp_ctx); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to wsp: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + + h = get_wsp_pipe(wsp_ctx); + if (h == NULL) { + DBG_ERR("Failed to communicate with server, no pipe\n"); + result = -1; + goto out; + } + + dcerpc_binding_handle_set_timeout(h, + DCERPC_REQUEST_TIMEOUT * 1000); + + /* connect */ + DBG_INFO("sending connect\n"); + status = wsp_connect(talloc_tos(), + wsp_ctx, + lpcfg_netbios_name(samba_cmdline_get_lp_ctx()), + cli_credentials_get_username( + samba_cmdline_get_creds()), + server, + &is_64bit); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to connect to wsp: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + + DBG_INFO("sending query\n"); + + status = create_query(talloc_tos(), + wsp_ctx, + limit, + select_stmt, + &cursor); + + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to send query: %s)\n", + nt_errstr(status)); + result = -1; + goto out; + } + + DBG_INFO("sending createbindings\n"); + /* set bindings */ + status = create_bindings(talloc_tos(), + wsp_ctx, + select_stmt, + cursor, + &bindings_used, + is_64bit); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("failed to setbindings: %s)\n", + nt_errstr(status)); + result = -1; + goto out; + } + + status = create_querystatusex(talloc_tos(), + wsp_ctx, + bindings_used.hcursor, + &nrows); + if (!nrows) { + result = 0; + DBG_ERR("no results found\n"); + goto out; + } + + printf("found %d results, returning %d \n", + nrows, + limit ? MIN(nrows, limit) : nrows); + status = create_getrows(talloc_tos(), + wsp_ctx, + &bindings_used, + bindings_used.hcursor, + limit ? MIN(nrows, limit) : nrows, + custom_query, + is_64bit); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("Failed to retrieve rows, error: %s\n", + nt_errstr(status)); + result = -1; + goto out; + } + result = 0; +out: + TALLOC_FREE(frame); + return result; +} |