summaryrefslogtreecommitdiff
path: root/source3/utils
diff options
context:
space:
mode:
Diffstat (limited to 'source3/utils')
-rw-r--r--source3/utils/debug2html.c256
-rw-r--r--source3/utils/debugparse.c308
-rw-r--r--source3/utils/eventlogadm.c229
-rw-r--r--source3/utils/log2pcaphex.c304
-rw-r--r--source3/utils/net.c777
-rw-r--r--source3/utils/net.h152
-rw-r--r--source3/utils/net_ads.c2586
-rw-r--r--source3/utils/net_ads_gpo.c695
-rw-r--r--source3/utils/net_afs.c114
-rw-r--r--source3/utils/net_afs.h29
-rw-r--r--source3/utils/net_cache.c373
-rw-r--r--source3/utils/net_conf.c1179
-rw-r--r--source3/utils/net_dns.c210
-rw-r--r--source3/utils/net_dom.c265
-rw-r--r--source3/utils/net_file.c57
-rw-r--r--source3/utils/net_group.c65
-rw-r--r--source3/utils/net_groupmap.c906
-rw-r--r--source3/utils/net_help.c69
-rw-r--r--source3/utils/net_help_common.c55
-rw-r--r--source3/utils/net_help_common.h49
-rw-r--r--source3/utils/net_idmap.c441
-rw-r--r--source3/utils/net_join.c54
-rw-r--r--source3/utils/net_lookup.c447
-rw-r--r--source3/utils/net_proto.h489
-rw-r--r--source3/utils/net_rap.c1374
-rw-r--r--source3/utils/net_registry.c531
-rw-r--r--source3/utils/net_registry_util.c143
-rw-r--r--source3/utils/net_registry_util.h41
-rw-r--r--source3/utils/net_rpc.c7209
-rw-r--r--source3/utils/net_rpc_audit.c494
-rw-r--r--source3/utils/net_rpc_join.c506
-rw-r--r--source3/utils/net_rpc_printer.c2517
-rw-r--r--source3/utils/net_rpc_registry.c1311
-rw-r--r--source3/utils/net_rpc_rights.c708
-rw-r--r--source3/utils/net_rpc_samsync.c362
-rw-r--r--source3/utils/net_rpc_service.c725
-rw-r--r--source3/utils/net_rpc_sh_acct.c453
-rw-r--r--source3/utils/net_rpc_shell.c298
-rw-r--r--source3/utils/net_sam.c1938
-rw-r--r--source3/utils/net_share.c77
-rw-r--r--source3/utils/net_status.c269
-rw-r--r--source3/utils/net_time.c238
-rw-r--r--source3/utils/net_user.c67
-rw-r--r--source3/utils/net_usershare.c1073
-rw-r--r--source3/utils/net_util.c597
-rw-r--r--source3/utils/netlookup.c227
-rw-r--r--source3/utils/nmblookup.c369
-rw-r--r--source3/utils/ntlm_auth.c2491
-rw-r--r--source3/utils/ntlm_auth.h26
-rw-r--r--source3/utils/ntlm_auth_diagnostics.c607
-rw-r--r--source3/utils/ntlm_auth_proto.h48
-rw-r--r--source3/utils/passwd_proto.h32
-rw-r--r--source3/utils/passwd_util.c68
-rw-r--r--source3/utils/pdbedit.c1101
-rw-r--r--source3/utils/profiles.c303
-rw-r--r--source3/utils/rpccheck.c62
-rw-r--r--source3/utils/sharesec.c619
-rw-r--r--source3/utils/smbcacls.c1032
-rw-r--r--source3/utils/smbcontrol.c1401
-rw-r--r--source3/utils/smbcquotas.c619
-rw-r--r--source3/utils/smbfilter.c287
-rw-r--r--source3/utils/smbget.c644
-rw-r--r--source3/utils/smbpasswd.c603
-rw-r--r--source3/utils/smbtree.c332
-rw-r--r--source3/utils/smbw_sample.c96
-rw-r--r--source3/utils/status.c485
-rw-r--r--source3/utils/status_profile.c541
-rw-r--r--source3/utils/testparm.c434
68 files changed, 43467 insertions, 0 deletions
diff --git a/source3/utils/debug2html.c b/source3/utils/debug2html.c
new file mode 100644
index 0000000000..26096c9de4
--- /dev/null
+++ b/source3/utils/debug2html.c
@@ -0,0 +1,256 @@
+/* ========================================================================== **
+ * debug2html.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * Parse Samba debug logs (2.0 & greater) and output the results as HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General 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 program provides an example of the use of debugparse.c, and also
+ * does a decent job of converting Samba logs into HTML.
+ * -------------------------------------------------------------------------- **
+ *
+ * Revision 1.4 1998/11/13 03:37:01 tridge
+ * fixes for OSF1 compilation
+ *
+ * Revision 1.3 1998/10/28 20:33:35 crh
+ * I've moved the debugparse module files into the ubiqx directory because I
+ * know that 'make proto' will ignore them there. The debugparse.h header
+ * file is included in includes.h, and includes.h is included in debugparse.c,
+ * so all of the pieces "see" each other. I've compiled and tested this,
+ * and it does seem to work. It's the same compromise model I used when
+ * adding the ubiqx modules into the system, which is why I put it all into
+ * the same directory.
+ *
+ * Chris -)-----
+ *
+ * Revision 1.1 1998/10/26 23:21:37 crh
+ * Here is the simple debug parser and the debug2html converter. Still to do:
+ *
+ * * Debug message filtering.
+ * * I need to add all this to Makefile.in
+ * (If it looks at all strange I'll ask for help.)
+ *
+ * If you want to compile debug2html, you'll need to do it by hand until I
+ * make the changes to Makefile.in. Sorry.
+ *
+ * Chris -)-----
+ *
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * The size of the read buffer.
+ */
+
+#define DBG_BSIZE 1024
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+static dbg_Token modechange( dbg_Token newmode, dbg_Token mode )
+ /* ------------------------------------------------------------------------ **
+ * Handle a switch between header and message printing.
+ *
+ * Input: new - The token value of the current token. This indicates
+ * the lexical item currently being recognized.
+ * mode - The current mode. This is either dbg_null or
+ * dbg_message. It could really be any toggle
+ * (true/false, etc.)
+ *
+ * Output: The new mode. This will be the same as the input mode unless
+ * there was a transition in or out of message processing.
+ *
+ * Notes: The purpose of the mode value is to mark the beginning and end
+ * of the message text block. In order to show the text in its
+ * correct format, it must be included within a <PRE></PRE> block.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( newmode )
+ {
+ case dbg_null:
+ case dbg_ignore:
+ return( mode );
+ case dbg_message:
+ if( dbg_message != mode )
+ {
+ /* Switching to message mode. */
+ (void)printf( "<PRE>\n" );
+ return( dbg_message );
+ }
+ break;
+ default:
+ if( dbg_message == mode )
+ {
+ /* Switching out of message mode. */
+ (void)printf( "</PRE>\n\n" );
+ return( dbg_null );
+ }
+ }
+
+ return( mode );
+ } /* modechange */
+
+static void newblock( dbg_Token old, dbg_Token newtok )
+ /* ------------------------------------------------------------------------ **
+ * Handle the transition between tokens.
+ *
+ * Input: old - The previous token.
+ * new - The current token.
+ *
+ * Output: none.
+ *
+ * Notes: This is called whenever there is a transition from one token
+ * type to another. It first prints the markup tags that close
+ * the previous token, and then the markup tags for the new
+ * token.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( old )
+ {
+ case dbg_timestamp:
+ (void)printf( ",</B>" );
+ break;
+ case dbg_level:
+ (void)printf( "</FONT>]</B>\n " );
+ break;
+ case dbg_sourcefile:
+ (void)printf( ":" );
+ break;
+ case dbg_lineno:
+ (void)printf( ")" );
+ break;
+ default:
+ break;
+ }
+
+ switch( newtok )
+ {
+ case dbg_timestamp:
+ (void)printf( "<B>[" );
+ break;
+ case dbg_level:
+ (void)printf( " <B><FONT COLOR=MAROON>" );
+ break;
+ case dbg_lineno:
+ (void)printf( "(" );
+ break;
+ default:
+ break;
+ }
+ } /* newblock */
+
+static void charprint( dbg_Token tok, int c )
+ /* ------------------------------------------------------------------------ **
+ * Filter the input characters to determine what goes to output.
+ *
+ * Input: tok - The token value of the current character.
+ * c - The current character.
+ *
+ * Output: none.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_ignore:
+ case dbg_header:
+ break;
+ case dbg_null:
+ case dbg_eof:
+ (void)putchar( '\n' );
+ break;
+ default:
+ switch( c )
+ {
+ case '<':
+ (void)printf( "&lt;" );
+ break;
+ case '>':
+ (void)printf( "&gt;" );
+ break;
+ case '&':
+ (void)printf( "&amp;" );
+ break;
+ case '\"':
+ (void)printf( "&#34;" );
+ break;
+ default:
+ (void)putchar( c );
+ break;
+ }
+ }
+ } /* charprint */
+
+int main( int argc, char *argv[] )
+ /* ------------------------------------------------------------------------ **
+ * This simple program scans and parses Samba debug logs, and produces HTML
+ * output.
+ *
+ * Input: argc - Currently ignored.
+ * argv - Currently ignored.
+ *
+ * Output: Always zero.
+ *
+ * Notes: The HTML output is sent to stdout.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ int i;
+ int len;
+ char bufr[DBG_BSIZE];
+ dbg_Token old = dbg_null,
+ newtok = dbg_null,
+ state = dbg_null,
+ mode = dbg_null;
+
+ (void)printf( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" );
+ (void)printf( "<HTML>\n<HEAD>\n" );
+ (void)printf( " <TITLE>Samba Debug Output</TITLE>\n</HEAD>\n\n<BODY>\n" );
+
+ while( (!feof( stdin ))
+ && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) )
+ {
+ for( i = 0; i < len; i++ )
+ {
+ old = newtok;
+ newtok = dbg_char2token( &state, bufr[i] );
+ if( newtok != old )
+ {
+ mode = modechange( newtok, mode );
+ newblock( old, newtok );
+ }
+ charprint( newtok, bufr[i] );
+ }
+ }
+ (void)modechange( dbg_eof, mode );
+
+ (void)printf( "</BODY>\n</HTML>\n" );
+ return( 0 );
+ } /* main */
diff --git a/source3/utils/debugparse.c b/source3/utils/debugparse.c
new file mode 100644
index 0000000000..5835de1b8d
--- /dev/null
+++ b/source3/utils/debugparse.c
@@ -0,0 +1,308 @@
+/* ========================================================================== **
+ * debugparse.c
+ *
+ * Copyright (C) 1998 by Christopher R. Hertel
+ *
+ * Email: crh@ubiqx.mn.org
+ *
+ * -------------------------------------------------------------------------- **
+ * This module is a very simple parser for Samba debug log files.
+ * -------------------------------------------------------------------------- **
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * -------------------------------------------------------------------------- **
+ * The important function in this module is dbg_char2token(). The rest is
+ * basically fluff. (Potentially useful fluff, but still fluff.)
+ * ========================================================================== **
+ */
+
+#include "debugparse.h"
+
+/* -------------------------------------------------------------------------- **
+ * Constants...
+ *
+ * DBG_BSIZE - This internal constant is used only by dbg_test(). It is the
+ * size of the read buffer. I've tested the function using a
+ * DBG_BSIZE value of 2.
+ */
+
+#define DBG_BSIZE 128
+
+/* -------------------------------------------------------------------------- **
+ * Functions...
+ */
+
+const char *dbg_token2string( dbg_Token tok )
+ /* ------------------------------------------------------------------------ **
+ * Given a token, return a string describing the token.
+ *
+ * Input: tok - One of the set of dbg_Tokens defined in debugparse.h.
+ *
+ * Output: A string identifying the token. This is useful for debugging,
+ * etc.
+ *
+ * Note: If the token is not known, this function will return the
+ * string "<unknown>".
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ switch( tok )
+ {
+ case dbg_null:
+ return( "null" );
+ case dbg_ignore:
+ return( "ignore" );
+ case dbg_header:
+ return( "header" );
+ case dbg_timestamp:
+ return( "time stamp" );
+ case dbg_level:
+ return( "level" );
+ case dbg_sourcefile:
+ return( "source file" );
+ case dbg_function:
+ return( "function" );
+ case dbg_lineno:
+ return( "line number" );
+ case dbg_message:
+ return( "message" );
+ case dbg_eof:
+ return( "[EOF]" );
+ }
+ return( "<unknown>" );
+ } /* dbg_token2string */
+
+dbg_Token dbg_char2token( dbg_Token *state, int c )
+ /* ------------------------------------------------------------------------ **
+ * Parse input one character at a time.
+ *
+ * Input: state - A pointer to a token variable. This is used to
+ * maintain the parser state between calls. For
+ * each input stream, you should set up a separate
+ * state variable and initialize it to dbg_null.
+ * Pass a pointer to it into this function with each
+ * character in the input stream. See dbg_test()
+ * for an example.
+ * c - The "current" character in the input stream.
+ *
+ * Output: A token.
+ * The token value will change when delimiters are found,
+ * which indicate a transition between syntactical objects.
+ * Possible return values are:
+ *
+ * dbg_null - The input character was an end-of-line.
+ * This resets the parser to its initial state
+ * in preparation for parsing the next line.
+ * dbg_eof - Same as dbg_null, except that the character
+ * was an end-of-file.
+ * dbg_ignore - Returned for whitespace and delimiters.
+ * These lexical tokens are only of interest
+ * to the parser.
+ * dbg_header - Indicates the start of a header line. The
+ * input character was '[' and was the first on
+ * the line.
+ * dbg_timestamp - Indicates that the input character was part
+ * of a header timestamp.
+ * dbg_level - Indicates that the input character was part
+ * of the debug-level value in the header.
+ * dbg_sourcefile - Indicates that the input character was part
+ * of the sourcefile name in the header.
+ * dbg_function - Indicates that the input character was part
+ * of the function name in the header.
+ * dbg_lineno - Indicates that the input character was part
+ * of the DEBUG call line number in the header.
+ * dbg_message - Indicates that the input character was part
+ * of the DEBUG message text.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ /* The terminating characters that we see will greatly depend upon
+ * how they are read. For example, if gets() is used instead of
+ * fgets(), then we will not see newline characters. A lot also
+ * depends on the calling function, which may handle terminators
+ * itself.
+ *
+ * '\n', '\0', and EOF are all considered line terminators. The
+ * dbg_eof token is sent back if an EOF is encountered.
+ *
+ * Warning: only allow the '\0' character to be sent if you are
+ * using gets() to read whole lines (thus replacing '\n'
+ * with '\0'). Sending '\0' at the wrong time will mess
+ * up the parsing.
+ */
+ switch( c )
+ {
+ case EOF:
+ *state = dbg_null; /* Set state to null (initial state) so */
+ return( dbg_eof ); /* that we can restart with new input. */
+ case '\n':
+ case '\0':
+ *state = dbg_null; /* A newline or eoln resets to the null state. */
+ return( dbg_null );
+ }
+
+ /* When within the body of the message, only a line terminator
+ * can cause a change of state. We've already checked for line
+ * terminators, so if the current state is dbg_msgtxt, simply
+ * return that as our current token.
+ */
+ if( dbg_message == *state )
+ return( dbg_message );
+
+ /* If we are at the start of a new line, and the input character
+ * is an opening bracket, then the line is a header line, otherwise
+ * it's a message body line.
+ */
+ if( dbg_null == *state )
+ {
+ if( '[' == c )
+ {
+ *state = dbg_timestamp;
+ return( dbg_header );
+ }
+ *state = dbg_message;
+ return( dbg_message );
+ }
+
+ /* We've taken care of terminators, text blocks and new lines.
+ * The remaining possibilities are all within the header line
+ * itself.
+ */
+
+ /* Within the header line, whitespace can be ignored *except*
+ * within the timestamp.
+ */
+ if( isspace( c ) )
+ {
+ /* Fudge. The timestamp may contain space characters. */
+ if( (' ' == c) && (dbg_timestamp == *state) )
+ return( dbg_timestamp );
+ /* Otherwise, ignore whitespace. */
+ return( dbg_ignore );
+ }
+
+ /* Okay, at this point we know we're somewhere in the header.
+ * Valid header *states* are: dbg_timestamp, dbg_level,
+ * dbg_sourcefile, dbg_function, and dbg_lineno.
+ */
+ switch( c )
+ {
+ case ',':
+ if( dbg_timestamp == *state )
+ {
+ *state = dbg_level;
+ return( dbg_ignore );
+ }
+ break;
+ case ']':
+ if( dbg_level == *state )
+ {
+ *state = dbg_sourcefile;
+ return( dbg_ignore );
+ }
+ break;
+ case ':':
+ if( dbg_sourcefile == *state )
+ {
+ *state = dbg_function;
+ return( dbg_ignore );
+ }
+ break;
+ case '(':
+ if( dbg_function == *state )
+ {
+ *state = dbg_lineno;
+ return( dbg_ignore );
+ }
+ break;
+ case ')':
+ if( dbg_lineno == *state )
+ {
+ *state = dbg_null;
+ return( dbg_ignore );
+ }
+ break;
+ }
+
+ /* If the previous block did not result in a state change, then
+ * return the current state as the current token.
+ */
+ return( *state );
+ } /* dbg_char2token */
+
+void dbg_test( void );
+void dbg_test( void )
+ /* ------------------------------------------------------------------------ **
+ * Simple test function.
+ *
+ * Input: none.
+ * Output: none.
+ * Notes: This function was used to test dbg_char2token(). It reads a
+ * Samba log file from stdin and prints parsing info to stdout.
+ * It also serves as a simple example.
+ *
+ * ------------------------------------------------------------------------ **
+ */
+ {
+ char bufr[DBG_BSIZE];
+ int i;
+ int linecount = 1;
+ dbg_Token old = dbg_null,
+ newtok= dbg_null,
+ state = dbg_null;
+
+ while( fgets( bufr, DBG_BSIZE, stdin ) )
+ {
+ for( i = 0; bufr[i]; i++ )
+ {
+ old = newtok;
+ newtok = dbg_char2token( &state, bufr[i] );
+ switch( newtok )
+ {
+ case dbg_header:
+ if( linecount > 1 )
+ (void)putchar( '\n' );
+ break;
+ case dbg_null:
+ linecount++;
+ break;
+ case dbg_ignore:
+ break;
+ default:
+ if( old != newtok )
+ (void)printf( "\n[%05d]%12s: ", linecount, dbg_token2string(newtok) );
+ (void)putchar( bufr[i] );
+ }
+ }
+ }
+ (void)putchar( '\n' );
+ } /* dbg_test */
+
+
+/* -------------------------------------------------------------------------- **
+ * This simple main line can be uncommented and used to test the parser.
+ */
+
+/*
+ * int main( void )
+ * {
+ * dbg_test();
+ * return( 0 );
+ * }
+ */
+
+/* ========================================================================== */
diff --git a/source3/utils/eventlogadm.c b/source3/utils/eventlogadm.c
new file mode 100644
index 0000000000..1b38a7b13b
--- /dev/null
+++ b/source3/utils/eventlogadm.c
@@ -0,0 +1,229 @@
+
+/*
+ * Samba Unix/Linux SMB client utility
+ * Write Eventlog records to a tdb, perform other eventlog related functions
+ *
+ *
+ * Copyright (C) Brian Moran 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"
+
+#undef DBGC_CLASS
+#define DBGC_CLASS DBGC_UTIL_EVENTLOG
+
+
+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( "\nMiscellaneous options:\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 (from smb.conf):\n" );
+ printf( "--------------------------------------\n" );
+ if ( elogs ) {
+ for ( i = 0; elogs[i]; i++ ) {
+ printf( "\t%s\n", elogs[i] );
+ }
+ }
+ else
+ printf( "\t<None specified>\n");
+}
+
+static int DoAddSourceCommand( int argc, char **argv, bool debugflag, char *exename )
+{
+
+ 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 */
+ if (!W_ERROR_IS_OK(regdb_init())) {
+ printf( "Can't open the registry.\n" );
+ return -1;
+ }
+
+ if ( !eventlog_add_source( argv[0], argv[1], argv[2] ) )
+ return -2;
+ return 0;
+}
+
+static int DoWriteCommand( int argc, char **argv, bool debugflag, char *exename )
+{
+ FILE *f1;
+ char *argfname;
+ ELOG_TDB *etdb;
+
+ /* fixed constants are bad bad bad */
+ char linein[1024];
+ bool is_eor;
+ Eventlog_entry ee;
+ int rcnum;
+
+ 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 ) ) ) {
+ printf( "can't open the eventlog TDB (%s)\n", argfname );
+ return -1;
+ }
+
+ ZERO_STRUCT( ee ); /* MUST initialize between records */
+
+ while ( !feof( f1 ) ) {
+ fgets( linein, sizeof( linein ) - 1, f1 );
+ linein[strlen( linein ) - 1] = 0; /* whack the line delimiter */
+
+ if ( debugflag )
+ printf( "Read line [%s]\n", linein );
+
+ is_eor = False;
+
+
+ parse_logentry( ( char * ) &linein, &ee, &is_eor );
+ /* should we do something with the return code? */
+
+ if ( is_eor ) {
+ fixup_eventlog_entry( &ee );
+
+ if ( opt_debug )
+ printf( "record number [%d], tg [%d] , tw [%d]\n", ee.record.record_number, ee.record.time_generated, ee.record.time_written );
+
+ if ( ee.record.time_generated != 0 ) {
+
+ /* printf("Writing to the event log\n"); */
+
+ rcnum = write_eventlog_tdb( ELOG_TDB_CTX(etdb), &ee );
+ if ( !rcnum ) {
+ printf( "Can't write to the event log\n" );
+ } else {
+ if ( opt_debug )
+ printf( "Wrote record %d\n",
+ rcnum );
+ }
+ } else {
+ if ( opt_debug )
+ printf( "<null record>\n" );
+ }
+ ZERO_STRUCT( ee ); /* MUST initialize between records */
+ }
+ }
+
+ 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;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+
+ fstring opname;
+
+ load_case_tables();
+
+ opt_debug = 0; /* todo set this from getopts */
+
+ lp_load(get_dyn_CONFIGFILE(), True, False, False, True);
+
+ 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:" ) ) != 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;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ( argc < 1 ) {
+ printf( "\nNot enough arguments!\n" );
+ usage( exename );
+ exit( 1 );
+ }
+
+ /* note that the separate command types should call usage if they need to... */
+ while ( 1 ) {
+ if ( !StrCaseCmp( opname, "addsource" ) ) {
+ rc = DoAddSourceCommand( argc, argv, opt_debug,
+ exename );
+ break;
+ }
+ if ( !StrCaseCmp( opname, "write" ) ) {
+ rc = DoWriteCommand( 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/log2pcaphex.c b/source3/utils/log2pcaphex.c
new file mode 100644
index 0000000000..20cc40ca59
--- /dev/null
+++ b/source3/utils/log2pcaphex.c
@@ -0,0 +1,304 @@
+/*
+ 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 use with -h parameter:
+ log2pcaphex < samba-log-file | text2pcap -T 139,139 - foo.pcap
+
+ TODO: Have correct IP and TCP checksums.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General 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"
+
+/* We don't care about the paranoid malloc checker in this standalone
+ program */
+#undef malloc
+
+#include <assert.h>
+
+bool quiet = 0;
+bool 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 magic;
+ uint16 major;
+ uint16 minor;
+ int32 zone;
+ uint32 sigfigs;
+ uint32 snaplen;
+ uint32 linktype;
+};
+
+struct tcpdump_packet {
+ struct timeval ts;
+ uint32 caplen;
+ uint32 len;
+};
+
+typedef struct {
+ uint8 ver_hdrlen;
+ uint8 dscp;
+ uint16 packet_length;
+ uint16 identification;
+ uint8 flags;
+ uint8 fragment;
+ uint8 ttl;
+ uint8 protocol;
+ uint16 hdr_checksum;
+ uint32 src_addr;
+ uint32 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 source_port;
+ uint16 dest_port;
+ uint32 seq_num;
+ uint32 ack_num;
+ uint8 hdr_length;
+ uint8 flags;
+ uint16 window;
+ uint16 checksum;
+ uint16 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)
+{
+ static int i = 0;
+ struct tcpdump_packet p;
+ i++;
+ 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;
+long curpacket_len = 0;
+
+static void read_log_msg(FILE *in, unsigned char **_buffer, long *buffersize, long *data_offset, long *data_length)
+{
+ unsigned char *buffer;
+ int tmp; long i;
+ assert(fscanf(in, " size=%ld\n", buffersize));
+ *buffersize+=4; /* for netbios */
+ buffer = (unsigned char *)malloc(*buffersize);
+ memset(buffer, 0, *buffersize);
+ /* NetBIOS */
+ buffer[0] = 0x00;
+ buffer[1] = 0x00;
+ memcpy(buffer+2, &buffersize, 2);
+ 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;
+ assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp;
+ assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp;
+ assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2);
+ assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp;
+ assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2);
+ assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2);
+ assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2);
+ assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2);
+ assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2);
+ assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp;
+ for(i = 0; i < buffer[smb_wct]; i++) {
+ assert(fscanf(in, " smb_vwv[%*2d]=%*5d (0x%X)\n", &tmp));
+ 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;
+ (*data_offset)+=2;
+ *_buffer = buffer;
+}
+
+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 data after each line */
+ assert(fscanf(in, "%8s %8s", real[0], real[1]) == 2);
+ }
+ ret = fscanf(in, " [%03lX]", &addr);
+ if(!ret) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i);
+ return i-1;
+ }
+ assert(addr == i);
+ }
+ if(!fscanf(in, "%02X", &tmp)) {
+ if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i-1);
+ return i-1;
+ }
+ buffer[i] = tmp;
+ }
+ return data_length;
+}
+
+int main (int argc, char **argv)
+{
+ const char *infile, *outfile;
+ FILE *out, *in;
+ int opt;
+ poptContext pc;
+ char buffer[4096];
+ long data_offset = 0, data_length;
+ long data_bytes_read = 0;
+ int in_packet = 0;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "quiet", 'q', POPT_ARG_NONE, NULL, 'q', "Be quiet, don't output warnings" },
+ { "hex", 'h', POPT_ARG_NONE, NULL, 'h', "Output format readable by text2pcap" },
+ POPT_TABLEEND
+ };
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]");
+
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'q':
+ quiet = true;
+ break;
+ case 'h':
+ hexformat = true;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ infile = poptGetArg(pc);
+
+ if(infile) {
+ in = fopen(infile, "r");
+ if(!in) {
+ perror("fopen");
+ 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);
+ }
+ }
+
+ if(!outfile) out = stdout;
+
+ if(!hexformat)print_pcap_header(out);
+
+ while(!feof(in)) {
+ fgets(buffer, sizeof(buffer), in);
+ 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;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/source3/utils/net.c b/source3/utils/net.c
new file mode 100644
index 0000000000..bab2f41d11
--- /dev/null
+++ b/source3/utils/net.c
@@ -0,0 +1,777 @@
+/*
+ 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 "utils/net.h"
+
+extern bool AllowDebugChange;
+
+#ifdef WITH_FAKE_KASERVER
+#include "utils/net_afs.h"
+#endif
+
+/***********************************************************************/
+/* Beginning of internationalization section. Translatable constants */
+/* should be kept in this area and referenced in the rest of the code. */
+/* */
+/* No functions, outside of Samba or LSB (Linux Standards Base) should */
+/* be used (if possible). */
+/***********************************************************************/
+
+#define YES_STRING "Yes"
+#define NO_STRING "No"
+
+/***********************************************************************/
+/* end of internationalization section */
+/***********************************************************************/
+
+uint32 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)
+{
+ 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_changesecretpw(struct net_context *c, int argc,
+ const char **argv)
+{
+ char *trust_pw;
+ uint32 sec_channel_type = SEC_CHAN_WKSTA;
+
+ if(c->opt_force) {
+ 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 (!secrets_store_machine_password(trust_pw, lp_workgroup(), sec_channel_type)) {
+ d_fprintf(stderr, "Unable to write the machine account password in the secrets database");
+ return 1;
+ }
+ else {
+ d_printf("Modified trust account password in secrets database\n");
+ }
+ }
+ else {
+ d_printf("Machine account password change requires the -f flag.\n");
+ d_printf("Do NOT use this function unless you know what it does!\n");
+ d_printf("This function will change the ADS Domain member machine account password in the secrets.tdb file!\n");
+ }
+
+ 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)
+{
+ DOM_SID sid;
+ const char *name;
+ fstring sid_str;
+
+ if (argc >= 1) {
+ name = argv[0];
+ }
+ else {
+ name = global_myname();
+ }
+
+ if(!initialize_password_db(false, NULL)) {
+ DEBUG(0, ("WARNING: Could not open passdb - local sid may not reflect passdb\n"
+ "backend knowledge (such as the sid stored in LDAP)\n"));
+ }
+
+ /* 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;
+ }
+ sid_to_fstring(sid_str, &sid);
+ d_printf("SID for domain %s is: %s\n", name, sid_str);
+ return 0;
+}
+
+static int net_setlocalsid(struct net_context *c, int argc, const char **argv)
+{
+ 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: net setlocalsid S-1-5-21-x-y-z\n");
+ return 1;
+ }
+
+ if (!secrets_store_domain_sid(global_myname(), &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)
+{
+ 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: 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)
+{
+ DOM_SID domain_sid;
+ fstring sid_str;
+
+ if (argc > 0) {
+ d_printf("usage: net getdomainsid\n");
+ return 1;
+ }
+
+ if(!initialize_password_db(false, NULL)) {
+ DEBUG(0, ("WARNING: Could not open passdb - domain SID may "
+ "not reflect passdb\n"
+ "backend knowledge (such as the SID stored in "
+ "LDAP)\n"));
+ }
+
+ /* 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 (!secrets_fetch_domain_sid(global_myname(), &domain_sid)) {
+ d_fprintf(stderr, "Could not fetch local SID\n");
+ return 1;
+ }
+ sid_to_fstring(sid_str, &domain_sid);
+ d_printf("SID for local machine %s is: %s\n", global_myname(), sid_str);
+
+ if (!secrets_fetch_domain_sid(c->opt_workgroup, &domain_sid)) {
+ d_fprintf(stderr, "Could not fetch domain SID\n");
+ return 1;
+ }
+
+ sid_to_fstring(sid_str, &domain_sid);
+ d_printf("SID for domain %s is: %s\n", c->opt_workgroup, sid_str);
+
+ return 0;
+}
+
+static bool search_maxrid(struct pdb_search *search, const char *type,
+ uint32 *max_rid)
+{
+ struct samr_displayentry *entries;
+ uint32 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);
+ pdb_search_destroy(search);
+ return true;
+}
+
+static uint32 get_maxrid(void)
+{
+ uint32 max_rid = 0;
+
+ if (!search_maxrid(pdb_search_users(0), "users", &max_rid))
+ return 0;
+
+ if (!search_maxrid(pdb_search_groups(), "groups", &max_rid))
+ return 0;
+
+ if (!search_maxrid(pdb_search_aliases(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 rid;
+
+ if (argc != 0) {
+ DEBUG(0, ("usage: net maxrid\n"));
+ return 1;
+ }
+
+ if ((rid = get_maxrid()) == 0) {
+ DEBUG(0, ("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,
+ "Run functions using RPC transport",
+ " Use 'net help rpc' to get more extensive information about "
+ "'net rpc' commands."
+ },
+ {
+ "rap",
+ net_rap,
+ NET_TRANSPORT_RAP,
+ "Run functions using RAP transport",
+ " Use 'net help rap' to get more extensive information about "
+ "'net rap' commands."
+ },
+ {
+ "ads",
+ net_ads,
+ NET_TRANSPORT_ADS,
+ "Run functions using ADS transport",
+ " 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,
+ "Functions on remote opened files",
+ " Use 'net help file' to get more information about 'net "
+ "file' commands."
+ },
+ {
+ "share",
+ net_share,
+ NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ "Functions on shares",
+ " Use 'net help share' to get more information about 'net "
+ "share' commands."
+ },
+ {
+ "session",
+ net_rap_session,
+ NET_TRANSPORT_RAP,
+ "Manage sessions",
+ " Use 'net help session' to get more information about 'net "
+ "session' commands."
+ },
+ {
+ "server",
+ net_rap_server,
+ NET_TRANSPORT_RAP,
+ "List servers in workgroup",
+ " Use 'net help server' to get more information about 'net "
+ "server' commands."
+ },
+ {
+ "domain",
+ net_rap_domain,
+ NET_TRANSPORT_RAP,
+ "List domains/workgroups on network",
+ " Use 'net help domain' to get more information about 'net "
+ "domain' commands."
+ },
+ {
+ "printq",
+ net_rap_printq,
+ NET_TRANSPORT_RAP,
+ "Modify printer queue",
+ " Use 'net help printq' to get more information about 'net "
+ "printq' commands."
+ },
+ {
+ "user",
+ net_user,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ "Manage users",
+ " Use 'net help user' to get more information about 'net "
+ "user' commands."
+ },
+ {
+ "group",
+ net_group,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC | NET_TRANSPORT_RAP,
+ "Manage groups",
+ " Use 'net help group' to get more information about 'net "
+ "group' commands."
+ },
+ {
+ "groupmap",
+ net_groupmap,
+ NET_TRANSPORT_LOCAL,
+ "Manage group mappings",
+ " Use 'net help groupmap' to get more information about 'net "
+ "groupmap' commands."
+ },
+ {
+ "sam",
+ net_sam,
+ NET_TRANSPORT_LOCAL,
+ "Functions on the SAM database",
+ " Use 'net help sam' to get more information about 'net sam' "
+ "commands."
+ },
+ {
+ "validate",
+ net_rap_validate,
+ NET_TRANSPORT_RAP,
+ "Validate username and password",
+ " Use 'net help validate' to get more information about 'net "
+ "validate' commands."
+ },
+ {
+ "groupmember",
+ net_rap_groupmember,
+ NET_TRANSPORT_RAP,
+ "Modify group memberships",
+ " Use 'net help groupmember' to get more information about "
+ "'net groupmember' commands."
+ },
+ { "admin",
+ net_rap_admin,
+ NET_TRANSPORT_RAP,
+ "Execute remote command on a remote OS/2 server",
+ " Use 'net help admin' to get more information about 'net "
+ "admin' commands."
+ },
+ { "service",
+ net_rap_service,
+ NET_TRANSPORT_RAP,
+ "List/modify running services",
+ " Use 'net help service' to get more information about 'net "
+ "service' commands."
+ },
+ {
+ "password",
+ net_rap_password,
+ NET_TRANSPORT_RAP,
+ "Change user password on target server",
+ " Use 'net help password' to get more information about 'net "
+ "password' commands."
+ },
+ { "changetrustpw",
+ net_changetrustpw,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC,
+ "Change the trust password",
+ " Use 'net help changetrustpw' to get more information about "
+ "'net changetrustpw'."
+ },
+ { "changesecretpw",
+ net_changesecretpw,
+ NET_TRANSPORT_LOCAL,
+ "Change the secret password",
+ " 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."
+ },
+ { "time",
+ net_time,
+ NET_TRANSPORT_LOCAL,
+ "Show/set time",
+ " Use 'net help time' to get more information about 'net "
+ "time' commands."
+ },
+ { "lookup",
+ net_lookup,
+ NET_TRANSPORT_LOCAL,
+ "Look up host names/IP addresses",
+ " Use 'net help lookup' to get more information about 'net "
+ "lookup' commands."
+ },
+ { "join",
+ net_join,
+ NET_TRANSPORT_ADS | NET_TRANSPORT_RPC,
+ "Join a domain/AD",
+ " Use 'net help join' to get more information about 'net "
+ "join'."
+ },
+ { "dom",
+ net_dom,
+ NET_TRANSPORT_LOCAL,
+ "Join/unjoin (remote) machines to/from a domain/AD",
+ " Use 'net help dom' to get more information about 'net dom' "
+ "commands."
+ },
+ { "cache",
+ net_cache,
+ NET_TRANSPORT_LOCAL,
+ "Operate on the cache tdb file",
+ " Use 'net help cache' to get more information about 'net "
+ "cache' commands."
+ },
+ { "getlocalsid",
+ net_getlocalsid,
+ NET_TRANSPORT_LOCAL,
+ "Get the SID for the local domain",
+ " net getlocalsid"
+ },
+ { "setlocalsid",
+ net_setlocalsid,
+ NET_TRANSPORT_LOCAL,
+ "Set the SID for the local domain",
+ " net setlocalsid S-1-5-21-x-y-z"
+ },
+ { "setdomainsid",
+ net_setdomainsid,
+ NET_TRANSPORT_LOCAL,
+ "Set domain SID on member servers",
+ " net setdomainsid S-1-5-21-x-y-z"
+ },
+ { "getdomainsid",
+ net_getdomainsid,
+ NET_TRANSPORT_LOCAL,
+ "Get domain SID on member servers",
+ " net getdomainsid"
+ },
+ { "maxrid",
+ net_maxrid,
+ NET_TRANSPORT_LOCAL,
+ "Display the maximul RID currently used",
+ " net maxrid"
+ },
+ { "idmap",
+ net_idmap,
+ NET_TRANSPORT_LOCAL,
+ "IDmap functions",
+ " Use 'net help idmap to get more information about 'net "
+ "idmap' commands."
+ },
+ { "status",
+ net_status,
+ NET_TRANSPORT_LOCAL,
+ "Display server status",
+ " Use 'net help status' to get more information about 'net "
+ "status' commands."
+ },
+ { "usershare",
+ net_usershare,
+ NET_TRANSPORT_LOCAL,
+ "Manage user-modifiable shares",
+ " Use 'net help usershare to get more information about 'net "
+ "usershare' commands."
+ },
+ { "usersidlist",
+ net_usersidlist,
+ NET_TRANSPORT_RPC,
+ "Display list of all users with SID",
+ " Use 'net help usersidlist' to get more information about "
+ "'net usersidlist'."
+ },
+ { "conf",
+ net_conf,
+ NET_TRANSPORT_LOCAL,
+ "Manage Samba registry based configuration",
+ " Use 'net help conf' to get more information about 'net "
+ "conf' commands."
+ },
+ { "registry",
+ net_registry,
+ NET_TRANSPORT_LOCAL,
+ "Manage the Samba registry",
+ " Use 'net help registry' to get more information about 'net "
+ "registry' commands."
+ },
+#ifdef WITH_FAKE_KASERVER
+ { "afs",
+ net_afs,
+ NET_TRANSPORT_LOCAL,
+ "Manage AFS tokens",
+ " Use 'net help afs' to get more information about 'net afs' "
+ "commands."
+ },
+#endif
+
+ { "help",
+ net_help,
+ NET_TRANSPORT_LOCAL,
+ "Print usage information",
+ " Use 'net help help' to list usage information for 'net' "
+ "commands."
+ },
+ {NULL, NULL, 0, NULL, NULL}
+};
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, const char **argv)
+{
+ int opt,i;
+ char *p;
+ int rc = 0;
+ int argc_new = 0;
+ const char ** argv_new;
+ poptContext pc;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct net_context *c = talloc_zero(frame, struct net_context);
+
+ struct poptOption long_options[] = {
+ {"help", 'h', POPT_ARG_NONE, 0, 'h'},
+ {"workgroup", 'w', POPT_ARG_STRING, &c->opt_target_workgroup},
+ {"user", 'U', POPT_ARG_STRING, &c->opt_user_name, 'U'},
+ {"ipaddress", 'I', POPT_ARG_STRING, 0,'I'},
+ {"port", 'p', POPT_ARG_INT, &c->opt_port},
+ {"myname", 'n', POPT_ARG_STRING, &c->opt_requester_name},
+ {"server", 'S', POPT_ARG_STRING, &c->opt_host},
+ {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport (UNIX extended servers only)" },
+ {"container", 'c', POPT_ARG_STRING, &c->opt_container},
+ {"comment", 'C', POPT_ARG_STRING, &c->opt_comment},
+ {"maxusers", 'M', POPT_ARG_INT, &c->opt_maxusers},
+ {"flags", 'F', POPT_ARG_INT, &c->opt_flags},
+ {"long", 'l', POPT_ARG_NONE, &c->opt_long_list_entries},
+ {"reboot", 'r', POPT_ARG_NONE, &c->opt_reboot},
+ {"force", 'f', POPT_ARG_NONE, &c->opt_force},
+ {"stdin", 'i', POPT_ARG_NONE, &c->opt_stdin},
+ {"timeout", 't', POPT_ARG_INT, &c->opt_timeout},
+ {"machine-pass",'P', POPT_ARG_NONE, &c->opt_machine_pass},
+ {"kerberos", 'k', POPT_ARG_NONE, &c->opt_kerberos},
+ {"myworkgroup", 'W', POPT_ARG_STRING, &c->opt_workgroup},
+ {"verbose", 'v', POPT_ARG_NONE, &c->opt_verbose},
+ {"test", 'T', POPT_ARG_NONE, &c->opt_testmode},
+ /* Options for 'net groupmap set' */
+ {"local", 'L', POPT_ARG_NONE, &c->opt_localgroup},
+ {"domain", 'D', POPT_ARG_NONE, &c->opt_domaingroup},
+ {"ntname", 'N', POPT_ARG_STRING, &c->opt_newntname},
+ {"rid", 'R', POPT_ARG_INT, &c->opt_rid},
+ /* Options for 'net rpc share migrate' */
+ {"acls", 0, POPT_ARG_NONE, &c->opt_acls},
+ {"attrs", 0, POPT_ARG_NONE, &c->opt_attrs},
+ {"timestamps", 0, POPT_ARG_NONE, &c->opt_timestamps},
+ {"exclude", 'X', POPT_ARG_STRING, &c->opt_exclude},
+ {"destination", 0, POPT_ARG_STRING, &c->opt_destination},
+ {"tallocreport", 0, POPT_ARG_NONE, &c->do_talloc_report},
+ /* Options for 'net rpc vampire (keytab)' */
+ {"force-full-repl", 0, POPT_ARG_NONE, &c->opt_force_full_repl},
+ {"single-obj-repl", 0, POPT_ARG_NONE, &c->opt_single_obj_repl},
+ {"clean-old-entries", 0, POPT_ARG_NONE, &c->opt_clean_old_entries},
+
+ POPT_COMMON_SAMBA
+ { 0, 0, 0, 0}
+ };
+
+
+ zero_addr(&c->opt_dest_ip);
+
+ load_case_tables();
+
+ /* set default debug level to 0 regardless of what smb.conf sets */
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 0;
+ dbf = x_stderr;
+ c->private_data = net_func;
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'h':
+ c->display_usage = true;
+ break;
+ case 'e':
+ c->smb_encrypt = 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;
+ case 'U':
+ c->opt_user_specified = true;
+ c->opt_user_name = SMB_STRDUP(c->opt_user_name);
+ p = strchr(c->opt_user_name,'%');
+ if (p) {
+ *p = 0;
+ c->opt_password = p+1;
+ }
+ break;
+ default:
+ d_fprintf(stderr, "\nInvalid option %s: %s\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ net_help(c, argc, argv);
+ exit(1);
+ }
+ }
+
+ /*
+ * Don't load debug level from smb.conf. It should be
+ * set by cmdline arg or remain default (0)
+ */
+ AllowDebugChange = false;
+ lp_load(get_dyn_CONFIGFILE(), true, false, false, true);
+
+ 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) {
+ set_global_myname(c->opt_requester_name);
+ }
+
+ if (!c->opt_user_name && getenv("LOGNAME")) {
+ c->opt_user_name = getenv("LOGNAME");
+ }
+
+ if (!c->opt_workgroup) {
+ c->opt_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!c->opt_target_workgroup) {
+ c->opt_target_workgroup = smb_xstrdup(lp_workgroup());
+ }
+
+ if (!init_names())
+ exit(1);
+
+ load_interfaces();
+
+ /* this makes sure that when we do things like call scripts,
+ that it won't assert becouse we are not root */
+ sec_init();
+
+ if (c->opt_machine_pass) {
+ /* it is very useful to be able to make ads queries as the
+ machine account for testing purposes and for domain leave */
+
+ net_use_krb_machine_account(c);
+ }
+
+ if (!c->opt_password) {
+ c->opt_password = getenv("PASSWD");
+ }
+
+ 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);
+
+ TALLOC_FREE(frame);
+ return rc;
+}
diff --git a/source3/utils/net.h b/source3/utils/net.h
new file mode 100644
index 0000000000..5dc2b11d4e
--- /dev/null
+++ b/source3/utils/net.h
@@ -0,0 +1,152 @@
+/*
+ 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
+ */
+
+#include "lib/netapi/netapi.h"
+#include "libnet/libnet.h"
+
+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;
+ 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;
+ bool opt_kerberos;
+ int opt_force_full_repl;
+ int opt_single_obj_repl;
+ int opt_clean_old_entries;
+
+ int opt_have_ip;
+ struct sockaddr_storage opt_dest_ip;
+ bool smb_encrypt;
+ struct libnetapi_ctx *netapi_ctx;
+
+ bool display_usage;
+ void *private_data;
+};
+
+#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 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 attribute;
+ struct net_context *c;
+}copy_clistate;
+
+struct rpc_sh_ctx {
+ struct cli_state *cli;
+
+ 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_syntax_id *interface;
+ 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 */
+
+/* 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 0000000000..7dbe518c3d
--- /dev/null
+++ b/source3/utils/net_ads.c
@@ -0,0 +1,2586 @@
+/*
+ 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"
+
+#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;
+}
+
+/*
+ do a cldap netlogon query
+*/
+static int net_ads_cldap_netlogon(struct net_context *c, ADS_STRUCT *ads)
+{
+ char addr[INET6_ADDRSTRLEN];
+ struct nbt_cldap_netlogon_5 reply;
+
+ print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+ if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
+ d_fprintf(stderr, "CLDAP query failed!\n");
+ return -1;
+ }
+
+ d_printf("Information for Domain Controller: %s\n\n",
+ addr);
+
+ d_printf("Response Type: ");
+ switch (reply.type) {
+ case SAMLOGON_AD_UNK_R:
+ d_printf("SAMLOGON\n");
+ break;
+ case SAMLOGON_AD_R:
+ d_printf("SAMLOGON_USER\n");
+ break;
+ default:
+ d_printf("0x%x\n", reply.type);
+ break;
+ }
+
+ d_printf("GUID: %s\n", smb_uuid_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",
+ (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");
+
+
+ printf("Forest:\t\t\t%s\n", reply.forest);
+ printf("Domain:\t\t\t%s\n", reply.dns_domain);
+ printf("Domain Controller:\t%s\n", reply.pdc_dns_name);
+
+ printf("Pre-Win2k Domain:\t%s\n", reply.domain);
+ printf("Pre-Win2k Hostname:\t%s\n", reply.pdc_name);
+
+ if (*reply.user_name) printf("User name:\t%s\n", reply.user_name);
+
+ printf("Server Site Name :\t\t%s\n", reply.server_site);
+ printf("Client Site Name :\t\t%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)
+{
+ ADS_STRUCT *ads;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads lookup\n"
+ " Find the ADS DC using CLDAP lookup.\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
+ d_fprintf(stderr, "Didn't find the cldap server!\n");
+ return -1;
+ }
+
+ if (!ads->config.realm) {
+ ads->config.realm = CONST_DISCARD(char *, c->opt_target_workgroup);
+ ads->ldap.port = 389;
+ }
+
+ return net_ads_cldap_netlogon(c, ads);
+}
+
+
+
+static int net_ads_info(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ char addr[INET6_ADDRSTRLEN];
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads info\n"
+ " Display information about an Active Directory "
+ "server.\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
+ d_fprintf(stderr, "Didn't find the ldap server!\n");
+ return -1;
+ }
+
+ if (!ads || !ads->config.realm) {
+ d_fprintf(stderr, "Didn't find the ldap server!\n");
+ return -1;
+ }
+
+ /* 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");
+ }
+
+ 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(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 );
+
+ return 0;
+}
+
+static 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 ADS_STATUS ads_startup_int(struct net_context *c, bool only_own_domain,
+ uint32 auth_flags, 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;
+
+ /* 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(realm, c->opt_target_workgroup, c->opt_host);
+
+ 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) {
+ ads_destroy(&ads);
+ return ADS_ERROR(LDAP_NO_MEMORY);
+ }
+ }
+
+ if (c->opt_password) {
+ use_in_memory_ccache();
+ SAFE_FREE(ads->auth.password);
+ ads->auth.password = smb_xstrdup(c->opt_password);
+ }
+
+ ads->auth.flags |= auth_flags;
+ SAFE_FREE(ads->auth.user_name);
+ ads->auth.user_name = smb_xstrdup(c->opt_user_name);
+
+ /*
+ * 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';
+ SAFE_FREE(ads->auth.realm);
+ ads->auth.realm = smb_xstrdup(cp);
+ strupper_m(ads->auth.realm);
+ }
+
+ 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)));
+ ads_destroy(&ads);
+ return status;
+ }
+
+ if (!need_password && !second_time && !(auth_flags & ADS_AUTH_NO_BIND)) {
+ need_password = true;
+ second_time = true;
+ goto retry;
+ } else {
+ ads_destroy(&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->config.tried_closest_dc) {
+
+ namecache_delete(ads->server.realm, 0x1C);
+ namecache_delete(ads->server.workgroup, 0x1C);
+
+ ads_destroy(&ads);
+ ads = NULL;
+
+ goto retry_connect;
+ }
+ }
+
+ *ads_ret = ads;
+ return status;
+}
+
+ADS_STATUS ads_startup(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
+{
+ return ads_startup_int(c, only_own_domain, 0, ads);
+}
+
+ADS_STATUS ads_startup_nobind(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads)
+{
+ return ads_startup_int(c, only_own_domain, ADS_AUTH_NO_BIND, 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(const char *realm, const char *workgroup, const char *host)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+
+ if ( (ads = ads_init( realm, workgroup, host )) == NULL ) {
+ return -1;
+ }
+
+ ads->auth.flags |= ADS_AUTH_NO_BIND;
+
+ status = ads_connect(ads);
+ if ( !ADS_ERR_OK(status) ) {
+ return -1;
+ }
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+int net_ads_check_our_domain(struct net_context *c)
+{
+ return net_ads_check_int(lp_realm(), lp_workgroup(), NULL);
+}
+
+int net_ads_check(struct net_context *c)
+{
+ return net_ads_check_int(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)
+{
+ ADS_STRUCT *ads;
+ char addr[INET6_ADDRSTRLEN];
+ struct nbt_cldap_netlogon_5 reply;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads workgroup\n"
+ " Print the workgroup name\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup_nobind(c, false, &ads))) {
+ d_fprintf(stderr, "Didn't find the cldap server!\n");
+ return -1;
+ }
+
+ if (!ads->config.realm) {
+ ads->config.realm = CONST_DISCARD(char *, c->opt_target_workgroup);
+ ads->ldap.port = 389;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
+ if ( !ads_cldap_netlogon_5(talloc_tos(), addr, ads->server.realm, &reply ) ) {
+ d_fprintf(stderr, "CLDAP query failed!\n");
+ return -1;
+ }
+
+ d_printf("Workgroup: %s\n", reply.domain);
+
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+
+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(field, "sAMAccountName") == 0) {
+ disp_fields[0] = SMB_STRDUP((char *) values[0]);
+ }
+ if (StrCaseCmp(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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ char *upn, *userdn;
+ LDAPMessage *res=NULL;
+ int rc = -1;
+ char *ou_str = NULL;
+
+ if (argc < 1 || c->display_usage)
+ return net_ads_user_usage(c, argc, argv);
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ 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, WELL_KNOWN_GUID_USERS);
+ }
+
+ 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 */
+ asprintf(&upn, "%s@%s", argv[0], ads->config.realm);
+ status = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1],
+ ads->auth.time_offset);
+ safe_free(upn);
+ if (ADS_ERR_OK(status)) {
+ d_printf("User %s added\n", argv[0]);
+ rc = 0;
+ goto done;
+ }
+
+ /* 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);
+ status=ads_find_user_acct(ads, &res, argv[0]);
+ if (ADS_ERR_OK(status)) {
+ userdn = ads_get_dn(ads, res);
+ ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ SAFE_FREE(ou_str);
+ return rc;
+}
+
+static int ads_user_info(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ LDAPMessage *res;
+ const char *attrs[] = {"memberOf", NULL};
+ char *searchstring=NULL;
+ char **grouplist;
+ char *escaped_user;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_user_usage(c, argc, argv);
+ }
+
+ escaped_user = escape_ldap_string_alloc(argv[0]);
+
+ if (!escaped_user) {
+ d_fprintf(stderr, "ads_user_info: failed to escape user %s\n", argv[0]);
+ return -1;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ SAFE_FREE(escaped_user);
+ return -1;
+ }
+
+ asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user);
+ rc = ads_search(ads, &res, searchstring, attrs);
+ safe_free(searchstring);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_search: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ SAFE_FREE(escaped_user);
+ return -1;
+ }
+
+ 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);
+ }
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ SAFE_FREE(escaped_user);
+ return 0;
+}
+
+static int ads_user_delete(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ LDAPMessage *res = NULL;
+ char *userdn;
+
+ if (argc < 1) {
+ return net_ads_user_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
+ d_printf("User %s does not exist.\n", argv[0]);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+ userdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, userdn);
+ ads_memfree(ads, userdn);
+ if (ADS_ERR_OK(rc)) {
+ d_printf("User %s deleted\n", argv[0]);
+ ads_destroy(&ads);
+ return 0;
+ }
+ d_fprintf(stderr, "Error deleting user %s: %s\n", argv[0],
+ ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+}
+
+int net_ads_user(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ ads_user_add,
+ NET_TRANSPORT_ADS,
+ "Add an AD user",
+ "net ads user add\n"
+ " Add an AD user"
+ },
+ {
+ "info",
+ ads_user_info,
+ NET_TRANSPORT_ADS,
+ "Display information about an AD user",
+ "net ads user info\n"
+ " Display information about an AD user"
+ },
+ {
+ "delete",
+ ads_user_delete,
+ NET_TRANSPORT_ADS,
+ "Delete an AD user",
+ "net ads user delete\n"
+ " Delete an AD user"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("Usage:\n");
+ d_printf("net ads user\n"
+ " List AD users\n");
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ if (c->opt_long_list_entries)
+ d_printf("\nUser name Comment"
+ "\n-----------------------------\n");
+
+ rc = 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);
+ ads_destroy(&ads);
+ return ADS_ERR_OK(rc) ? 0 : -1;
+ }
+
+ return net_run_function(c, argc, argv, "net ads user", func);
+}
+
+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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ LDAPMessage *res=NULL;
+ int rc = -1;
+ char *ou_str = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_group_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ 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 done;
+ }
+
+ if (ads_count_replies(ads, res)) {
+ d_fprintf(stderr, "ads_group_add: Group %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, WELL_KNOWN_GUID_USERS);
+ }
+
+ status = ads_add_group_acct(ads, argv[0], ou_str, c->opt_comment);
+
+ if (ADS_ERR_OK(status)) {
+ d_printf("Group %s added\n", argv[0]);
+ rc = 0;
+ } else {
+ d_fprintf(stderr, "Could not add group %s: %s\n", argv[0],
+ ads_errstr(status));
+ }
+
+ done:
+ if (res)
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ SAFE_FREE(ou_str);
+ return rc;
+}
+
+static int ads_group_delete(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ LDAPMessage *res = NULL;
+ char *groupdn;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_group_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ rc = ads_find_user_acct(ads, &res, argv[0]);
+ if (!ADS_ERR_OK(rc) || ads_count_replies(ads, res) != 1) {
+ d_printf("Group %s does not exist.\n", argv[0]);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+ groupdn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, groupdn);
+ ads_memfree(ads, groupdn);
+ if (ADS_ERR_OK(rc)) {
+ d_printf("Group %s deleted\n", argv[0]);
+ ads_destroy(&ads);
+ return 0;
+ }
+ d_fprintf(stderr, "Error deleting group %s: %s\n", argv[0],
+ ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+}
+
+int net_ads_group(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "add",
+ ads_group_add,
+ NET_TRANSPORT_ADS,
+ "Add an AD group",
+ "net ads group add\n"
+ " Add an AD group"
+ },
+ {
+ "delete",
+ ads_group_delete,
+ NET_TRANSPORT_ADS,
+ "Delete an AD group",
+ "net ads group delete\n"
+ " Delete an AD group"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *shortattrs[] = {"sAMAccountName", NULL};
+ const char *longattrs[] = {"sAMAccountName", "description", NULL};
+ char *disp_fields[2] = {NULL, NULL};
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("Usage:\n");
+ d_printf("net ads group\n"
+ " List AD groups\n");
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ if (c->opt_long_list_entries)
+ d_printf("\nGroup name Comment"
+ "\n-----------------------------\n");
+ rc = 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);
+
+ ads_destroy(&ads);
+ return ADS_ERR_OK(rc) ? 0 : -1;
+ }
+ return net_run_function(c, argc, argv, "net ads group", func);
+}
+
+static int net_ads_status(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ LDAPMessage *res;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads status\n"
+ " Display machine account details\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+
+ rc = ads_find_machine_acct(ads, &res, global_myname());
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_find_machine_acct: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, "No machine account for '%s' found\n", global_myname());
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_destroy(&ads);
+ return 0;
+}
+
+/*******************************************************************
+ 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 *ctx;
+ struct libnet_UnjoinCtx *r = NULL;
+ WERROR werr;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads leave\n"
+ " Leave an AD domain\n");
+ return 0;
+ }
+
+ if (!*lp_realm()) {
+ d_fprintf(stderr, "No realm set, are we joined ?\n");
+ return -1;
+ }
+
+ if (!(ctx = talloc_init("net_ads_leave"))) {
+ d_fprintf(stderr, "Could not initialise talloc context.\n");
+ return -1;
+ }
+
+ if (!c->opt_kerberos) {
+ use_in_memory_ccache();
+ }
+
+ werr = libnet_init_UnjoinCtx(ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Could not initialise unjoin context.\n");
+ return -1;
+ }
+
+ 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();
+ r->in.unjoin_flags = WKSSVC_JOIN_FLAGS_JOIN_TYPE |
+ WKSSVC_JOIN_FLAGS_ACCOUNT_DELETE;
+
+ werr = libnet_Unjoin(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 (W_ERROR_IS_OK(werr)) {
+ d_printf("Deleted account for '%s' in realm '%s'\n",
+ r->in.machine_name, r->out.dns_domain_name);
+ 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);
+ werr = WERR_OK;
+ goto done;
+ }
+
+ d_fprintf(stderr, "Failed to disable machine account for '%s' in realm '%s'\n",
+ r->in.machine_name, r->out.dns_domain_name);
+
+ done:
+ TALLOC_FREE(r);
+ TALLOC_FREE(ctx);
+
+ if (W_ERROR_IS_OK(werr)) {
+ return 0;
+ }
+
+ return -1;
+}
+
+static NTSTATUS net_ads_join_ok(struct net_context *c)
+{
+ ADS_STRUCT *ads = NULL;
+ ADS_STATUS status;
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ net_use_krb_machine_account(c);
+
+ status = ads_startup(c, true, &ads);
+ if (!ADS_ERR_OK(status)) {
+ return ads_ntstatus(status);
+ }
+
+ ads_destroy(&ads);
+ return NT_STATUS_OK;
+}
+
+/*
+ check that an existing join is OK
+ */
+int net_ads_testjoin(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ use_in_memory_ccache();
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads testjoin\n"
+ " Test if the existing join is ok\n");
+ return 0;
+ }
+
+ /* Display success or failure */
+ status = net_ads_join_ok(c);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,"Join to domain is not valid: %s\n",
+ get_friendly_nt_error_msg(status));
+ return -1;
+ }
+
+ printf("Join is OK\n");
+ return 0;
+}
+
+/*******************************************************************
+ Simple configu 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(global_myname()) > 15) {
+ d_printf("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n", global_myname(),
+ (unsigned int)strlen(global_myname()));
+ return WERR_INVALID_COMPUTER_NAME;
+ }
+
+ if ( lp_security() == SEC_ADS && !*lp_realm()) {
+ d_fprintf(stderr, "realm must be set in in %s for ADS "
+ "join to succeed.\n", get_dyn_CONFIGFILE());
+ return WERR_INVALID_PARAM;
+ }
+
+ return WERR_OK;
+}
+
+/*******************************************************************
+ Send a DNS update request
+*******************************************************************/
+
+#if defined(WITH_DNS_UPDATES)
+#include "dns.h"
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName, const char *pszHostName,
+ const struct sockaddr_storage *sslist,
+ size_t num_addrs );
+
+static NTSTATUS net_update_dns_internal(TALLOC_CTX *ctx, ADS_STRUCT *ads,
+ const char *machine_name,
+ const struct sockaddr_storage *addrs,
+ int num_addrs)
+{
+ struct dns_rr_ns *nameservers = NULL;
+ int ns_count = 0;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ DNS_ERROR dns_err;
+ fstring dns_server;
+ const char *dnsdomain = NULL;
+ char *root_domain = NULL;
+
+ 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_ads_join: Failed to find name server for the %s "
+ "realm\n", ads->config.realm));
+ goto done;
+ }
+
+ dnsdomain = root_domain;
+
+ }
+
+ /* 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[0].hostname );
+
+ dns_err = DoDNSUpdate(dns_server, dnsdomain, machine_name, addrs, num_addrs);
+ if (!ERR_DNS_IS_OK(dns_err)) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ }
+
+done:
+
+ SAFE_FREE( root_domain );
+
+ return status;
+}
+
+static NTSTATUS net_update_dns(TALLOC_CTX *mem_ctx, ADS_STRUCT *ads)
+{
+ int num_addrs;
+ struct sockaddr_storage *iplist = NULL;
+ fstring machine_name;
+ NTSTATUS status;
+
+ name_to_fqdn( machine_name, global_myname() );
+ strlower_m( machine_name );
+
+ /* Get our ip address (not the 127.0.0.x address but a real ip
+ * address) */
+
+ num_addrs = get_my_ip_address( &iplist );
+ if ( num_addrs <= 0 ) {
+ DEBUG(4,("net_update_dns: Failed to find my non-loopback IP "
+ "addresses!\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = net_update_dns_internal(mem_ctx, ads, machine_name,
+ iplist, num_addrs);
+ SAFE_FREE( iplist );
+ return status;
+}
+#endif
+
+
+/*******************************************************************
+ ********************************************************************/
+
+static int net_ads_join_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("net ads join [options]\n");
+ d_printf("Valid options:\n");
+ d_printf(" createupn[=UPN] Set the userPrincipalName attribute during the join.\n");
+ d_printf(" The deault UPN is in the form host/netbiosname@REALM.\n");
+ d_printf(" createcomputer=OU Precreate the computer account in a specific OU.\n");
+ d_printf(" The OU string read from top to bottom without RDNs and delimited by a '/'.\n");
+ d_printf(" E.g. \"createcomputer=Computers/Servers/Unix\"\n");
+ d_printf(" NB: A backslash '\\' is used as escape at multiple levels and may\n");
+ d_printf(" need to be doubled or even quadrupled. It is not used as a separator.\n");
+ d_printf(" osName=string Set the operatingSystem attribute during the join.\n");
+ d_printf(" osVer=string Set the operatingSystemVersion attribute during the join.\n");
+ d_printf(" NB: osName and osVer must be specified together for either to take effect.\n");
+ d_printf(" Also, the operatingSystemService attribute is also set when along with\n");
+ d_printf(" the two other attributes.\n");
+
+ return -1;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+int net_ads_join(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *ctx = NULL;
+ struct libnet_JoinCtx *r = NULL;
+ const char *domain = lp_realm();
+ WERROR werr = WERR_SETUP_NOT_JOINED;
+ bool createupn = false;
+ const char *machineupn = NULL;
+ const char *create_in_ou = NULL;
+ int i;
+ const char *os_name = NULL;
+ const char *os_version = NULL;
+ bool modify_config = lp_config_backend_is_registry();
+
+ if (c->display_usage)
+ return net_ads_join_usage(c, argc, argv);
+
+ if (!modify_config) {
+
+ werr = check_ads_config();
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Invalid configuration. Exiting....\n");
+ goto fail;
+ }
+ }
+
+ if (!(ctx = talloc_init("net_ads_join"))) {
+ d_fprintf(stderr, "Could not initialise talloc context.\n");
+ werr = WERR_NOMEM;
+ goto fail;
+ }
+
+ if (!c->opt_kerberos) {
+ use_in_memory_ccache();
+ }
+
+ werr = libnet_init_JoinCtx(ctx, &r);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto fail;
+ }
+
+ /* process additional command line args */
+
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "createupn", strlen("createupn")) ) {
+ createupn = true;
+ machineupn = get_string_param(argv[i]);
+ }
+ else if ( !StrnCaseCmp(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_PARAM;
+ goto fail;
+ }
+ }
+ else if ( !StrnCaseCmp(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_PARAM;
+ goto fail;
+ }
+ }
+ else if ( !StrnCaseCmp(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_PARAM;
+ goto fail;
+ }
+ }
+ else {
+ domain = argv[i];
+ }
+ }
+
+ if (!*domain) {
+ d_fprintf(stderr, "Please supply a valid domain name\n");
+ werr = WERR_INVALID_PARAM;
+ goto fail;
+ }
+
+ /* Do the domain join here */
+
+ r->in.domain_name = domain;
+ r->in.create_upn = createupn;
+ r->in.upn = machineupn;
+ r->in.account_ou = create_in_ou;
+ r->in.os_name = os_name;
+ r->in.os_version = os_version;
+ 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(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);
+ }
+
+#if defined(WITH_DNS_UPDATES)
+ if (r->out.domain_is_ad) {
+ /* We enter this block with user creds */
+ ADS_STRUCT *ads_dns = NULL;
+
+ if ( (ads_dns = ads_init( lp_realm(), NULL, NULL )) != NULL ) {
+ /* kinit with the machine password */
+
+ use_in_memory_ccache();
+ asprintf( &ads_dns->auth.user_name, "%s$", global_myname() );
+ ads_dns->auth.password = secrets_fetch_machine_password(
+ r->out.netbios_domain_name, NULL, NULL );
+ ads_dns->auth.realm = SMB_STRDUP( r->out.dns_domain_name );
+ strupper_m(ads_dns->auth.realm );
+ ads_kinit_password( ads_dns );
+ }
+
+ if ( !ads_dns || !NT_STATUS_IS_OK(net_update_dns( ctx, ads_dns )) ) {
+ d_fprintf( stderr, "DNS update failed!\n" );
+ }
+
+ /* exit from this block using machine creds */
+ ads_destroy(&ads_dns);
+ }
+#endif
+ TALLOC_FREE(r);
+ TALLOC_FREE( 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( ctx );
+
+ return -1;
+}
+
+/*******************************************************************
+ ********************************************************************/
+
+static int net_ads_dns_register(struct net_context *c, int argc, const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ TALLOC_CTX *ctx;
+
+#ifdef DEVELOPER
+ talloc_enable_leak_report();
+#endif
+
+ if (argc > 0 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads dns register\n"
+ " Register hostname with DNS\n");
+ return -1;
+ }
+
+ if (!(ctx = talloc_init("net_ads_dns"))) {
+ d_fprintf(stderr, "Could not initialise talloc context\n");
+ return -1;
+ }
+
+ status = ads_startup(c, true, &ads);
+ if ( !ADS_ERR_OK(status) ) {
+ DEBUG(1, ("error on ads_startup: %s\n", ads_errstr(status)));
+ TALLOC_FREE(ctx);
+ return -1;
+ }
+
+ if ( !NT_STATUS_IS_OK(net_update_dns(ctx, ads)) ) {
+ d_fprintf( stderr, "DNS update failed!\n" );
+ ads_destroy( &ads );
+ TALLOC_FREE( ctx );
+ return -1;
+ }
+
+ d_fprintf( stderr, "Successfully registered hostname with DNS\n" );
+
+ ads_destroy(&ads);
+ TALLOC_FREE( ctx );
+
+ return 0;
+#else
+ d_fprintf(stderr, "DNS update support not enabled at compile time!\n");
+ return -1;
+#endif
+}
+
+#if defined(WITH_DNS_UPDATES)
+DNS_ERROR do_gethostbyname(const char *server, const char *host);
+#endif
+
+static int net_ads_dns_gethostbyname(struct net_context *c, int argc, const char **argv)
+{
+#if defined(WITH_DNS_UPDATES)
+ DNS_ERROR err;
+
+#ifdef DEVELOPER
+ talloc_enable_leak_report();
+#endif
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads dns gethostbyname <server> <name>\n"
+ " Look up hostname from the AD\n"
+ " server\tName server to use\n"
+ " name\tName to look up\n");
+ return -1;
+ }
+
+ err = do_gethostbyname(argv[0], argv[1]);
+
+ d_printf("do_gethostbyname returned %d\n", ERROR_DNS_V(err));
+#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,
+ "Add host dns entry to AD",
+ "net ads dns register\n"
+ " Add host dns entry to AD"
+ },
+ {
+ "gethostbyname",
+ net_ads_dns_gethostbyname,
+ NET_TRANSPORT_ADS,
+ "Look up host",
+ "net ads dns gethostbyname\n"
+ " Look up host"
+ },
+ {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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ LDAPMessage *res = NULL;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads printer search\n"
+ " List printers in the AD\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ rc = ads_find_printers(ads, &res);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_find_printer: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, "No results found\n");
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return 0;
+}
+
+static int net_ads_printer_info(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ LDAPMessage *res = NULL;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "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");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ if (argc > 0) {
+ printername = argv[0];
+ } else {
+ printername = "*";
+ }
+
+ if (argc > 1) {
+ servername = argv[1];
+ } else {
+ servername = global_myname();
+ }
+
+ rc = ads_find_printer_on_server(ads, &res, printername, servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "Server '%s' not found: %s\n",
+ servername, ads_errstr(rc));
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, "Printer '%s' not found\n", printername);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ ads_dump(ads, res);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+static int net_ads_printer_publish(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername, *printername;
+ struct cli_state *cli;
+ struct rpc_pipe_client *pipe_hnd;
+ struct sockaddr_storage server_ss;
+ NTSTATUS nt_status;
+ TALLOC_CTX *mem_ctx = talloc_init("net_ads_printer_publish");
+ ADS_MODLIST mods = ads_init_mods(mem_ctx);
+ char *prt_dn, *srv_dn, **srv_cn;
+ char *srv_cn_escaped = NULL, *printername_escaped = NULL;
+ LDAPMessage *res = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "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_destroy(mem_ctx);
+ return -1;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ printername = argv[0];
+
+ if (argc == 2) {
+ servername = argv[1];
+ } else {
+ servername = global_myname();
+ }
+
+ /* Get printer data from SPOOLSS */
+
+ resolve_name(servername, &server_ss, 0x20);
+
+ nt_status = cli_full_connection(&cli, global_myname(), servername,
+ &server_ss, 0,
+ "IPC$", "IPC",
+ c->opt_user_name, c->opt_workgroup,
+ c->opt_password ? c->opt_password : "",
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ Undefined, NULL);
+
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ d_fprintf(stderr, "Unable to open a connnection to %s to obtain data "
+ "for %s\n", servername, printername);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /* 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);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ 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!");
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn_escaped, printername_escaped, srv_dn);
+
+ SAFE_FREE(srv_cn_escaped);
+ SAFE_FREE(printername_escaped);
+
+ nt_status = cli_rpc_pipe_open_noauth(cli, &syntax_spoolss, &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, "Unable to open a connnection to the spoolss pipe on %s\n",
+ servername);
+ SAFE_FREE(prt_dn);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ if (!W_ERROR_IS_OK(get_remote_printer_publishing_data(pipe_hnd, mem_ctx, &mods,
+ printername))) {
+ SAFE_FREE(prt_dn);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods);
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_publish_printer: %s\n", ads_errstr(rc));
+ SAFE_FREE(prt_dn);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ d_printf("published printer\n");
+ SAFE_FREE(prt_dn);
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+
+ return 0;
+}
+
+static int net_ads_printer_remove(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *servername;
+ char *prt_dn;
+ LDAPMessage *res = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "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");
+ return -1;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+
+ if (argc > 1) {
+ servername = argv[1];
+ } else {
+ servername = global_myname();
+ }
+
+ rc = ads_find_printer_on_server(ads, &res, argv[0], servername);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_find_printer_on_server: %s\n", ads_errstr(rc));
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ if (ads_count_replies(ads, res) == 0) {
+ d_fprintf(stderr, "Printer '%s' not found\n", argv[1]);
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ prt_dn = ads_get_dn(ads, res);
+ ads_msgfree(ads, res);
+ rc = ads_del_dn(ads, prt_dn);
+ ads_memfree(ads, prt_dn);
+
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "ads_del_dn: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ ads_destroy(&ads);
+ return 0;
+}
+
+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,
+ "Search for a printer",
+ "net ads printer search\n"
+ " Search for a printer"
+ },
+ {
+ "info",
+ net_ads_printer_info,
+ NET_TRANSPORT_ADS,
+ "Display printer information",
+ "net ads printer info\n"
+ " Display printer information"
+ },
+ {
+ "publish",
+ net_ads_printer_publish,
+ NET_TRANSPORT_ADS,
+ "Publish a printer",
+ "net ads printer publish\n"
+ " Publish a printer"
+ },
+ {
+ "remove",
+ net_ads_printer_remove,
+ NET_TRANSPORT_ADS,
+ "Delete a printer",
+ "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)
+{
+ ADS_STRUCT *ads;
+ const char *auth_principal = c->opt_user_name;
+ const char *auth_password = c->opt_password;
+ char *realm = NULL;
+ char *new_password = NULL;
+ char *chr, *prompt;
+ const char *user;
+ ADS_STATUS ret;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads password <username>\n"
+ " Change password for user\n"
+ " username\tName of user to change password for\n");
+ return 0;
+ }
+
+ if (c->opt_user_name == NULL || c->opt_password == NULL) {
+ d_fprintf(stderr, "You must supply an administrator username/password\n");
+ return -1;
+ }
+
+ if (argc < 1) {
+ d_fprintf(stderr, "ERROR: You must say which username to change password for\n");
+ return -1;
+ }
+
+ user = argv[0];
+ if (!strchr_m(user, '@')) {
+ asprintf(&chr, "%s@%s", argv[0], lp_realm());
+ user = chr;
+ }
+
+ 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 */
+ if (!(ads = ads_init(realm, c->opt_workgroup, c->opt_host))) {
+ return -1;
+ }
+
+ /* we don't actually need a full connect, but it's the easy way to
+ fill in the KDC's addresss */
+ ads_connect(ads);
+
+ if (!ads->config.realm) {
+ d_fprintf(stderr, "Didn't find the kerberos server!\n");
+ return -1;
+ }
+
+ if (argv[1]) {
+ new_password = (char *)argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", user);
+ new_password = getpass(prompt);
+ free(prompt);
+ }
+
+ ret = kerberos_set_password(ads->auth.kdc_server, auth_principal,
+ auth_password, user, new_password, ads->auth.time_offset);
+ if (!ADS_ERR_OK(ret)) {
+ d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Password change for %s completed.\n", user);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+int net_ads_changetrustpw(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ char *host_principal;
+ fstring my_name;
+ ADS_STATUS ret;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads changetrustpw\n"
+ " Change the machine account's trust password\n");
+ return 0;
+ }
+
+ if (!secrets_init()) {
+ DEBUG(1,("Failed to initialise secrets database\n"));
+ return -1;
+ }
+
+ net_use_krb_machine_account(c);
+
+ use_in_memory_ccache();
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+
+ fstrcpy(my_name, global_myname());
+ strlower_m(my_name);
+ asprintf(&host_principal, "%s$@%s", my_name, ads->config.realm);
+ d_printf("Changing password for principal: %s\n", host_principal);
+
+ ret = ads_change_trust_account_password(ads, host_principal);
+
+ if (!ADS_ERR_OK(ret)) {
+ d_fprintf(stderr, "Password change failed: %s\n", ads_errstr(ret));
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+ return -1;
+ }
+
+ d_printf("Password change for principal %s succeeded.\n", host_principal);
+
+ if (lp_use_kerberos_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");
+ }
+ }
+
+ ads_destroy(&ads);
+ SAFE_FREE(host_principal);
+
+ return 0;
+}
+
+/*
+ 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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *ldap_exp;
+ const char **attrs;
+ LDAPMessage *res = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_search_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ ldap_exp = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, ads->config.bind_path,
+ LDAP_SCOPE_SUBTREE,
+ ldap_exp, attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+
+/*
+ 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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *dn;
+ const char **attrs;
+ LDAPMessage *res = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_dn_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ dn = argv[0];
+ attrs = (argv + 1);
+
+ rc = ads_do_search_all(ads, dn,
+ LDAP_SCOPE_BASE,
+ "(objectclass=*)", attrs, &res);
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+/*
+ 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)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS rc;
+ const char *sid_string;
+ const char **attrs;
+ LDAPMessage *res = NULL;
+ DOM_SID sid;
+
+ if (argc < 1 || c->display_usage) {
+ return net_ads_sid_usage(c, argc, argv);
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, false, &ads))) {
+ return -1;
+ }
+
+ sid_string = argv[0];
+ attrs = (argv + 1);
+
+ if (!string_to_sid(&sid, sid_string)) {
+ d_fprintf(stderr, "could not convert sid\n");
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ rc = ads_search_retry_sid(ads, &res, &sid, attrs);
+ if (!ADS_ERR_OK(rc)) {
+ d_fprintf(stderr, "search failed: %s\n", ads_errstr(rc));
+ ads_destroy(&ads);
+ return -1;
+ }
+
+ d_printf("Got %d replies\n\n", ads_count_replies(ads, res));
+
+ /* dump the results */
+ ads_dump(ads, res);
+
+ ads_msgfree(ads, res);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+static int net_ads_keytab_flush(struct net_context *c, int argc, const char **argv)
+{
+ int ret;
+ ADS_STRUCT *ads;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads keytab flush\n"
+ " Delete the whole keytab\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+ ret = ads_keytab_flush(ads);
+ ads_destroy(&ads);
+ return ret;
+}
+
+static int net_ads_keytab_add(struct net_context *c, int argc, const char **argv)
+{
+ int i;
+ int ret = 0;
+ ADS_STRUCT *ads;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads keytab add <principal> [principal ...]\n"
+ " Add principals to local keytab\n"
+ " principal\tKerberos principal to add to "
+ "keytab\n");
+ return 0;
+ }
+
+ d_printf("Processing principals to add...\n");
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+ for (i = 0; i < argc; i++) {
+ ret |= ads_keytab_add_entry(ads, argv[i]);
+ }
+ ads_destroy(&ads);
+ return ret;
+}
+
+static int net_ads_keytab_create(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ int ret;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads keytab create\n"
+ " Create new default keytab\n");
+ return 0;
+ }
+
+ if (!ADS_ERR_OK(ads_startup(c, true, &ads))) {
+ return -1;
+ }
+ ret = ads_keytab_create_default(ads);
+ ads_destroy(&ads);
+ 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("Usage:\n"
+ "net ads keytab list [keytab]\n"
+ " List a local keytab\n"
+ " keytab\tKeytab to list\n");
+ return 0;
+ }
+
+ 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,
+ NET_TRANSPORT_ADS,
+ "Add a service principal",
+ "net ads keytab add\n"
+ " Add a service principal"
+ },
+ {
+ "create",
+ net_ads_keytab_create,
+ NET_TRANSPORT_ADS,
+ "Create a fresh keytab",
+ "net ads keytab create\n"
+ " Create a fresh keytab"
+ },
+ {
+ "flush",
+ net_ads_keytab_flush,
+ NET_TRANSPORT_ADS,
+ "Remove all keytab entries",
+ "net ads keytab flush\n"
+ " Remove all keytab entries"
+ },
+ {
+ "list",
+ net_ads_keytab_list,
+ NET_TRANSPORT_ADS,
+ "List a keytab",
+ "net ads keytab list\n"
+ " List a keytab"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (!lp_use_kerberos_keytab()) {
+ d_printf("\nWarning: \"use kerberos keytab\" must be set to \"true\" in order 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("Usage:\n"
+ "net ads kerberos renew\n"
+ " Renew TGT from existing credential cache\n");
+ return 0;
+ }
+
+ 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(struct net_context *c, int argc, const char **argv)
+{
+ struct PAC_DATA *pac = NULL;
+ struct PAC_LOGON_INFO *info = NULL;
+ TALLOC_CTX *mem_ctx = NULL;
+ NTSTATUS status;
+ int ret = -1;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads kerberos pac\n"
+ " Dump the Kerberos PAC\n");
+ return 0;
+ }
+
+ mem_ctx = talloc_init("net_ads_kerberos_pac");
+ if (!mem_ctx) {
+ goto out;
+ }
+
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+
+ status = kerberos_return_pac(mem_ctx,
+ c->opt_user_name,
+ c->opt_password,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ true,
+ true,
+ 2592000, /* one month */
+ &pac);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_printf("failed to query kerberos PAC: %s\n",
+ nt_errstr(status));
+ goto out;
+ }
+
+ info = get_logon_info_from_pac(pac);
+ if (info) {
+ const char *s;
+ s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_LOGON_INFO, info);
+ d_printf("The Pac: %s\n", s);
+ }
+
+ ret = 0;
+ out:
+ TALLOC_FREE(mem_ctx);
+ return ret;
+}
+
+static int net_ads_kerberos_kinit(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ int ret = -1;
+ NTSTATUS status;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads kerberos kinit\n"
+ " Get Ticket Granting Ticket (TGT) for the user\n");
+ return 0;
+ }
+
+ mem_ctx = talloc_init("net_ads_kerberos_kinit");
+ if (!mem_ctx) {
+ goto out;
+ }
+
+ 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 */
+ &status);
+ if (ret) {
+ d_printf("failed to kinit password: %s\n",
+ nt_errstr(status));
+ }
+ out:
+ 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,
+ "Retrieve Ticket Granting Ticket (TGT)",
+ "net ads kerberos kinit\n"
+ " Receive Ticket Granting Ticket (TGT)"
+ },
+ {
+ "renew",
+ net_ads_kerberos_renew,
+ NET_TRANSPORT_ADS,
+ "Renew Ticket Granting Ticket from credential cache"
+ "net ads kerberos renew\n"
+ " Renew Ticket Granting Ticket from credential cache"
+ },
+ {
+ "pac",
+ net_ads_kerberos_pac,
+ NET_TRANSPORT_ADS,
+ "Dump Kerberos PAC",
+ "net ads kerberos pac\n"
+ " Dump Kerberos PAC"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net ads kerberos", func);
+}
+
+int net_ads(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "info",
+ net_ads_info,
+ NET_TRANSPORT_ADS,
+ "Display details on remote ADS server",
+ "net ads info\n"
+ " Display details on remote ADS server"
+ },
+ {
+ "join",
+ net_ads_join,
+ NET_TRANSPORT_ADS,
+ "Join the local machine to ADS realm",
+ "net ads join\n"
+ " Join the local machine to ADS realm"
+ },
+ {
+ "testjoin",
+ net_ads_testjoin,
+ NET_TRANSPORT_ADS,
+ "Validate machine account",
+ "net ads testjoin\n"
+ " Validate machine account"
+ },
+ {
+ "leave",
+ net_ads_leave,
+ NET_TRANSPORT_ADS,
+ "Remove the local machine from ADS",
+ "net ads leave\n"
+ " Remove the local machine from ADS"
+ },
+ {
+ "status",
+ net_ads_status,
+ NET_TRANSPORT_ADS,
+ "Display machine account details",
+ "net ads status\n"
+ " Display machine account details"
+ },
+ {
+ "user",
+ net_ads_user,
+ NET_TRANSPORT_ADS,
+ "List/modify users",
+ "net ads user\n"
+ " List/modify users"
+ },
+ {
+ "group",
+ net_ads_group,
+ NET_TRANSPORT_ADS,
+ "List/modify groups",
+ "net ads group\n"
+ " List/modify groups"
+ },
+ {
+ "dns",
+ net_ads_dns,
+ NET_TRANSPORT_ADS,
+ "Issue dynamic DNS update",
+ "net ads dns\n"
+ " Issue dynamic DNS update"
+ },
+ {
+ "password",
+ net_ads_password,
+ NET_TRANSPORT_ADS,
+ "Change user passwords",
+ "net ads password\n"
+ " Change user passwords"
+ },
+ {
+ "changetrustpw",
+ net_ads_changetrustpw,
+ NET_TRANSPORT_ADS,
+ "Change trust account password",
+ "net ads changetrustpw\n"
+ " Change trust account password"
+ },
+ {
+ "printer",
+ net_ads_printer,
+ NET_TRANSPORT_ADS,
+ "List/modify printer entries",
+ "net ads printer\n"
+ " List/modify printer entries"
+ },
+ {
+ "search",
+ net_ads_search,
+ NET_TRANSPORT_ADS,
+ "Issue LDAP search using filter",
+ "net ads search\n"
+ " Issue LDAP search using filter"
+ },
+ {
+ "dn",
+ net_ads_dn,
+ NET_TRANSPORT_ADS,
+ "Issue LDAP search by DN",
+ "net ads dn\n"
+ " Issue LDAP search by DN"
+ },
+ {
+ "sid",
+ net_ads_sid,
+ NET_TRANSPORT_ADS,
+ "Issue LDAP search by SID",
+ "net ads sid\n"
+ " Issue LDAP search by SID"
+ },
+ {
+ "workgroup",
+ net_ads_workgroup,
+ NET_TRANSPORT_ADS,
+ "Display workgroup name",
+ "net ads workgroup\n"
+ " Display the workgroup name"
+ },
+ {
+ "lookup",
+ net_ads_lookup,
+ NET_TRANSPORT_ADS,
+ "Perfom CLDAP query on DC",
+ "net ads lookup\n"
+ " Find the ADS DC using CLDAP lookups"
+ },
+ {
+ "keytab",
+ net_ads_keytab,
+ NET_TRANSPORT_ADS,
+ "Manage local keytab file",
+ "net ads keytab\n"
+ " Manage local keytab file"
+ },
+ {
+ "gpo",
+ net_ads_gpo,
+ NET_TRANSPORT_ADS,
+ "Manage group policy objects",
+ "net ads gpo\n"
+ " Manage group policy objects"
+ },
+ {
+ "kerberos",
+ net_ads_kerberos,
+ NET_TRANSPORT_ADS,
+ "Manage kerberos keytab",
+ "net ads kerberos\n"
+ " Manage kerberos keytab"
+ },
+ {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_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();
+}
+
+/* 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 /* WITH_ADS */
diff --git a/source3/utils/net_ads_gpo.c b/source3/utils/net_ads_gpo.c
new file mode 100644
index 0000000000..3c66325abe
--- /dev/null
+++ b/source3/utils/net_ads_gpo.c
@@ -0,0 +1,695 @@
+/*
+ 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"
+
+#ifdef HAVE_ADS
+
+static int net_ads_gpo_refresh(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ const char *dn = NULL;
+ struct GROUP_POLICY_OBJECT *gpo_list = NULL;
+ struct GROUP_POLICY_OBJECT *read_list = NULL;
+ uint32 uac = 0;
+ uint32 flags = 0;
+ struct GROUP_POLICY_OBJECT *gpo;
+ NTSTATUS result;
+ struct nt_user_token *token = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads gpo refresh <username|machinename>\n"
+ " Lists all GPOs assigned to an account and "
+ "downloads them\n"
+ " username\tUser to refresh GPOs for\n"
+ " machinename\tMachine to refresh GPOs for\n");
+ return -1;
+ }
+
+ mem_ctx = talloc_init("net_ads_gpo_refresh");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, &ads);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("failed to connect AD server: %s\n", ads_errstr(status));
+ goto out;
+ }
+
+ status = ads_find_samaccount(ads, mem_ctx, argv[0], &uac, &dn);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("failed to find samaccount for %s\n", argv[0]);
+ goto out;
+ }
+
+ if (uac & UF_WORKSTATION_TRUST_ACCOUNT) {
+ flags |= GPO_LIST_FLAG_MACHINE;
+ }
+
+ d_printf("\n%s: '%s' has dn: '%s'\n\n",
+ (uac & UF_WORKSTATION_TRUST_ACCOUNT) ? "machine" : "user",
+ argv[0], dn);
+
+ d_printf("* fetching token ");
+ 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)) {
+ d_printf("failed: %s\n", ads_errstr(status));
+ goto out;
+ }
+ d_printf("finished\n");
+
+ d_printf("* fetching GPO List ");
+ status = ads_get_gpo_list(ads, mem_ctx, dn, flags, token, &gpo_list);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("failed: %s\n", ads_errstr(status));
+ goto out;
+ }
+ d_printf("finished\n");
+
+ d_printf("* refreshing Group Policy Data ");
+ if (!NT_STATUS_IS_OK(result = check_refresh_gpo_list(ads, mem_ctx,
+ flags,
+ gpo_list))) {
+ d_printf("failed: %s\n", nt_errstr(result));
+ goto out;
+ }
+ d_printf("finished\n");
+
+ d_printf("* storing GPO list to registry ");
+
+ {
+ WERROR werr = gp_reg_state_store(mem_ctx, flags, dn,
+ token, gpo_list);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("failed: %s\n", dos_errstr(werr));
+ goto out;
+ }
+ }
+
+ d_printf("finished\n");
+
+ if (c->opt_verbose) {
+
+ d_printf("* dumping GPO list\n");
+
+ for (gpo = gpo_list; gpo; gpo = gpo->next) {
+
+ dump_gpo(ads, mem_ctx, gpo, 0);
+#if 0
+ char *server, *share, *nt_path, *unix_path;
+
+ d_printf("--------------------------------------\n");
+ d_printf("Name:\t\t\t%s\n", gpo->display_name);
+ d_printf("LDAP GPO version:\t%d (user: %d, machine: %d)\n",
+ gpo->version,
+ GPO_VERSION_USER(gpo->version),
+ GPO_VERSION_MACHINE(gpo->version));
+
+ result = gpo_explode_filesyspath(mem_ctx, gpo->file_sys_path,
+ &server, &share, &nt_path,
+ &unix_path);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("got: %s\n", nt_errstr(result));
+ }
+
+ d_printf("GPO stored on server: %s, share: %s\n", server, share);
+ d_printf("\tremote path:\t%s\n", nt_path);
+ d_printf("\tlocal path:\t%s\n", unix_path);
+#endif
+ }
+ }
+
+ d_printf("* re-reading GPO list from registry ");
+
+ {
+ WERROR werr = gp_reg_state_read(mem_ctx, flags,
+ &token->user_sids[0],
+ &read_list);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("failed: %s\n", dos_errstr(werr));
+ goto out;
+ }
+ }
+
+ d_printf("finished\n");
+
+ if (c->opt_verbose) {
+
+ d_printf("* dumping GPO list from registry\n");
+
+ for (gpo = read_list; gpo; gpo = gpo->next) {
+
+ dump_gpo(ads, mem_ctx, gpo, 0);
+
+#if 0
+ char *server, *share, *nt_path, *unix_path;
+
+ d_printf("--------------------------------------\n");
+ d_printf("Name:\t\t\t%s\n", gpo->display_name);
+ d_printf("LDAP GPO version:\t%d (user: %d, machine: %d)\n",
+ gpo->version,
+ GPO_VERSION_USER(gpo->version),
+ GPO_VERSION_MACHINE(gpo->version));
+
+ result = gpo_explode_filesyspath(mem_ctx, gpo->file_sys_path,
+ &server, &share, &nt_path,
+ &unix_path);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("got: %s\n", nt_errstr(result));
+ }
+
+ d_printf("GPO stored on server: %s, share: %s\n", server, share);
+ d_printf("\tremote path:\t%s\n", nt_path);
+ d_printf("\tlocal path:\t%s\n", unix_path);
+#endif
+ }
+ }
+
+ out:
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+
+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("Usage:\n"
+ "net ads gpo listall\n"
+ " List all GPOs on the DC\n");
+ return 0;
+ }
+
+ mem_ctx = talloc_init("net_ads_gpo_list_all");
+ if (mem_ctx == NULL) {
+ return -1;
+ }
+
+ status = ads_startup(c, false, &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,
+ DACL_SECURITY_INFORMATION,
+ &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, 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));
+ ads_memfree(ads, dn);
+ goto out;
+ }
+
+ dump_gpo(ads, mem_ctx, &gpo, 0);
+ ads_memfree(ads, dn);
+ }
+
+out:
+ ads_msgfree(ads, res);
+
+ talloc_destroy(mem_ctx);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+static int net_ads_gpo_list(struct net_context *c, int argc, const char **argv)
+{
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ LDAPMessage *res = NULL;
+ TALLOC_CTX *mem_ctx;
+ const char *dn = NULL;
+ uint32 uac = 0;
+ uint32 flags = 0;
+ struct GROUP_POLICY_OBJECT *gpo_list;
+ struct nt_user_token *token = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads gpo list <username|machinename>\n"
+ " 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, &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(ads, mem_ctx, gpo_list, 0);
+
+out:
+ ads_msgfree(ads, res);
+
+ talloc_destroy(mem_ctx);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+#if 0
+static int net_ads_gpo_apply(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ ADS_STRUCT *ads;
+ ADS_STATUS status;
+ const char *dn = NULL;
+ struct GROUP_POLICY_OBJECT *gpo_list;
+ uint32 uac = 0;
+ uint32 flags = 0;
+ struct nt_user_token *token = NULL;
+ const char *filter = NULL;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads gpo apply <username|machinename>\n"
+ " Apply GPOs for machine/user\n"
+ " username\tUsername to apply GPOs for\n"
+ " machinename\tMachine to apply GPOs for\n");
+ return -1;
+ }
+
+ mem_ctx = talloc_init("net_ads_gpo_apply");
+ if (mem_ctx == NULL) {
+ goto out;
+ }
+
+ if (argc >= 2) {
+ filter = cse_gpo_name_to_guid_string(argv[1]);
+ }
+
+ status = ads_startup(c, false, &ads);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("got: %s\n", ads_errstr(status));
+ goto out;
+ }
+
+ status = ads_find_samaccount(ads, mem_ctx, argv[0], &uac, &dn);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("failed to find samaccount for %s: %s\n",
+ argv[0], ads_errstr(status));
+ goto out;
+ }
+
+ if (uac & UF_WORKSTATION_TRUST_ACCOUNT) {
+ flags |= GPO_LIST_FLAG_MACHINE;
+ }
+
+ if (opt_verbose) {
+ flags |= GPO_INFO_FLAG_VERBOSE;
+ }
+
+ 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;
+ }
+
+ status = gpo_process_gpo_list(ads, mem_ctx, token, gpo_list,
+ filter, flags);
+ if (!ADS_ERR_OK(status)) {
+ d_printf("failed to process gpo list: %s\n",
+ ads_errstr(status));
+ goto out;
+ }
+
+out:
+ ads_destroy(&ads);
+ talloc_destroy(mem_ctx);
+ return 0;
+}
+#endif
+
+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("Usage:\n"
+ "net ads gpo linkget <container>\n"
+ " Lists gPLink of a containter\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, &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(ads, mem_ctx, &gp_link);
+
+out:
+ talloc_destroy(mem_ctx);
+ ads_destroy(&ads);
+
+ 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 gpo_opt = 0;
+ TALLOC_CTX *mem_ctx;
+
+ if (argc < 2 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net ads gpo linkadd <linkdn> <gpodn> [options]\n"
+ " 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");
+ d_printf("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, &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);
+ ads_destroy(&ads);
+
+ 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, &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);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+#endif
+
+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("Usage:\n"
+ "net ads gpo getgpo <gpo>\n"
+ " List speciefied 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, &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(ads, mem_ctx, &gpo, 1);
+
+out:
+ talloc_destroy(mem_ctx);
+ ads_destroy(&ads);
+
+ return 0;
+}
+
+int net_ads_gpo(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+#if 0
+ {
+ "apply",
+ net_ads_gpo_apply,
+ NET_TRANSPORT_ADS,
+ "Apply GPO to container",
+ "net ads gpo apply\n"
+ " Apply GPO to container"
+ },
+#endif
+ {
+ "getgpo",
+ net_ads_gpo_get_gpo,
+ NET_TRANSPORT_ADS,
+ "List specified GPO",
+ "net ads gpo getgpo\n"
+ " List specified GPO"
+ },
+ {
+ "linkadd",
+ net_ads_gpo_link_add,
+ NET_TRANSPORT_ADS,
+ "Link a container to a GPO",
+ "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,
+ "Lists gPLink of containter",
+ "net ads gpo linkget\n"
+ " Lists gPLink of containter"
+ },
+ {
+ "list",
+ net_ads_gpo_list,
+ NET_TRANSPORT_ADS,
+ "Lists all GPOs for machine/user",
+ "net ads gpo list\n"
+ " Lists all GPOs for machine/user"
+ },
+ {
+ "listall",
+ net_ads_gpo_list_all,
+ NET_TRANSPORT_ADS,
+ "Lists all GPOs on a DC",
+ "net ads gpo listall\n"
+ " Lists all GPOs on a DC"
+ },
+ {
+ "refresh",
+ net_ads_gpo_refresh,
+ NET_TRANSPORT_ADS,
+ "Lists all GPOs assigned to an account and downloads "
+ "them",
+ "net ads gpo refresh\n"
+ " Lists all GPOs assigned to an account and "
+ "downloads them"
+ },
+ {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_afs.c b/source3/utils/net_afs.c
new file mode 100644
index 0000000000..0f19ec1e12
--- /dev/null
+++ b/source3/utils/net_afs.c
@@ -0,0 +1,114 @@
+/*
+ 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"
+
+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("usage: 'net afs key <keyfile> cell'\n");
+ 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");
+ return -1;
+ }
+
+ if (!secrets_store_afs_keyfile(argv[1], &keyfile)) {
+ d_fprintf(stderr, "Could not write keyfile to secrets.tdb\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int net_afs_impersonate(struct net_context *c, int argc,
+ const char **argv)
+{
+ char *token;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: net afs impersonate <user> <cell>\n");
+ 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,
+ "Import an OpenAFS keyfile",
+ "net afs key <filename>\n"
+ " Import kefile from <filename>."
+ },
+ {
+ "impersonate",
+ net_afs_impersonate,
+ NET_TRANSPORT_LOCAL,
+ "Get a user token",
+ "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);
+}
+
diff --git a/source3/utils/net_afs.h b/source3/utils/net_afs.h
new file mode 100644
index 0000000000..31606dd717
--- /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 0000000000..4e9ae18c0d
--- /dev/null
+++ b/source3/utils/net_cache.c
@@ -0,0 +1,373 @@
+/*
+ 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"
+
+/**
+ * @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, const char* datastr,
+ const time_t timeout, void* dptr)
+{
+ char *timeout_str;
+ char *alloc_str = NULL;
+ time_t now_t = time(NULL);
+ struct tm timeout_tm, *now_tm;
+ /* localtime returns statically allocated pointer, so timeout_tm
+ has to be copied somewhere else */
+
+ now_tm = localtime(&timeout);
+ if (!now_tm) {
+ return;
+ }
+ memcpy(&timeout_tm, now_tm, sizeof(struct tm));
+ now_tm = localtime(&now_t);
+ if (!now_tm) {
+ 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 {
+ asprintf(&alloc_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour,
+ timeout_tm.tm_min, timeout_tm.tm_sec);
+ if (!alloc_str) {
+ return;
+ }
+ timeout_str = alloc_str;
+ }
+
+ 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("\nUsage: 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");
+ gencache_shutdown();
+ return 0;
+ }
+
+ d_fprintf(stderr, "Entry couldn't be added. Perhaps there's already such a key.\n");
+ gencache_shutdown();
+ 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("\nUsage: 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];
+ char* valuestr;
+ time_t timeout;
+
+ if (argc < 1 || c->display_usage) {
+ d_printf("\nUsage: net cache get <key>\n");
+ return -1;
+ }
+
+ if (gencache_get(keystr, &valuestr, &timeout)) {
+ print_cache_entry(keystr, valuestr, timeout, NULL);
+ 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("Usage: net cache search <pattern>\n");
+ return -1;
+ }
+
+ pattern = argv[0];
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ return 0;
+}
+
+
+/**
+ * List the contents of the cache
+ *
+ * @param c A net_context structure
+ * @param argv ignored in this functionailty
+ * @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("Usage:\n"
+ "net cache list\n"
+ " List all cache entries.\n");
+ return 0;
+ }
+ gencache_iterate(print_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ 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("Usage:\n"
+ "net cache flush\n"
+ " Delete all cache entries.\n");
+ return 0;
+ }
+ gencache_iterate(delete_cache_entry, NULL, pattern);
+ gencache_shutdown();
+ return 0;
+}
+
+/**
+ * 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,
+ "Add new cache entry",
+ "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,
+ "Delete existing cache entry by key",
+ "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,
+ "Get cache entry by key",
+ "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,
+ "Search entry by pattern",
+ "net cache search <pattern>\n"
+ " Search entry by pattern.\n"
+ " pattern\tPattern to search for in cache."
+ },
+ {
+ "list",
+ net_cache_list,
+ NET_TRANSPORT_LOCAL,
+ "List all cache entries",
+ "net cache list\n"
+ " List all cache entries"
+ },
+ {
+ "flush",
+ net_cache_flush,
+ NET_TRANSPORT_LOCAL,
+ "Delete all cache entries",
+ "net cache flush\n"
+ " Delete all cache entries"
+ },
+ {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 0000000000..ab1b0f3df7
--- /dev/null
+++ b/source3/utils/net_conf.c
@@ -0,0 +1,1179 @@
+/*
+ * 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 "utils/net.h"
+
+/**********************************************************************
+ *
+ * usage functions
+ *
+ **********************************************************************/
+
+static int net_conf_list_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("USAGE: net conf list\n");
+ return -1;
+}
+
+static int net_conf_import_usage(struct net_context *c, int argc,
+ const char**argv)
+{
+ d_printf("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("USAGE: net conf listshares\n");
+ return -1;
+}
+
+static int net_conf_drop_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("USAGE: net conf drop\n");
+ return -1;
+}
+
+static int net_conf_showshare_usage(struct net_context *c, int argc,
+ const char **argv)
+{
+ d_printf("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("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("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("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("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("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("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("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("USAGE: net conf delincludes <section>\n");
+ return -1;
+}
+
+
+/**********************************************************************
+ *
+ * Helper functions
+ *
+ **********************************************************************/
+
+/**
+ * This functions process a service previously loaded with libsmbconf.
+ */
+static WERROR import_process_service(struct net_context *c,
+ struct smbconf_ctx *conf_ctx,
+ struct smbconf_service *service)
+{
+ uint32_t idx;
+ WERROR werr = WERR_OK;
+ uint32_t num_includes = 0;
+ char **includes = NULL;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (c->opt_testmode) {
+ 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)) {
+ werr = smbconf_delete_share(conf_ctx, service->name);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+ werr = smbconf_create_share(conf_ctx, service->name);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+
+ for (idx = 0; idx < service->num_params; idx ++) {
+ if (strequal(service->param_names[idx], "include")) {
+ includes = TALLOC_REALLOC_ARRAY(mem_ctx,
+ includes,
+ char *,
+ num_includes+1);
+ if (includes == NULL) {
+ werr = WERR_NOMEM;
+ goto done;
+ }
+ includes[num_includes] = talloc_strdup(includes,
+ service->param_values[idx]);
+ if (includes[num_includes] == NULL) {
+ werr = WERR_NOMEM;
+ goto done;
+ }
+ num_includes++;
+ } else {
+ werr = smbconf_set_parameter(conf_ctx,
+ service->name,
+ service->param_names[idx],
+ service->param_values[idx]);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+ }
+
+ werr = smbconf_set_includes(conf_ctx, service->name, num_includes,
+ (const char **)includes);
+
+done:
+ TALLOC_FREE(mem_ctx);
+ return werr;
+}
+
+
+/**********************************************************************
+ *
+ * the main conf functions
+ *
+ **********************************************************************/
+
+static int net_conf_list(struct net_context *c, struct smbconf_ctx *conf_ctx,
+ int argc, const char **argv)
+{
+ WERROR werr = WERR_OK;
+ 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;
+ }
+
+ werr = smbconf_get_config(conf_ctx, mem_ctx, &num_shares, &shares);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error getting config: %s\n",
+ dos_errstr(werr));
+ 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;
+ WERROR werr;
+
+ 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_lower(mem_ctx, argv[1]);
+ if (servicename == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+ 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;
+ }
+
+ werr = smbconf_init(mem_ctx, &txt_ctx, conf_source);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("error loading file '%s': %s\n", filename,
+ dos_errstr(werr));
+ 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;
+
+ werr = smbconf_get_share(txt_ctx, mem_ctx,
+ servicename,
+ &service);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ werr = import_process_service(c, conf_ctx, service);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ } else {
+ struct smbconf_service **services = NULL;
+ uint32_t num_shares, sidx;
+
+ werr = smbconf_get_config(txt_ctx, mem_ctx,
+ &num_shares,
+ &services);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ if (!c->opt_testmode) {
+ werr = smbconf_drop(conf_ctx);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+ for (sidx = 0; sidx < num_shares; sidx++) {
+ werr = import_process_service(c, conf_ctx,
+ services[sidx]);
+ if (!W_ERROR_IS_OK(werr)) {
+ goto done;
+ }
+ }
+ }
+
+ ret = 0;
+
+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)
+{
+ WERROR werr = WERR_OK;
+ 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;
+ }
+
+ werr = smbconf_get_share_names(conf_ctx, mem_ctx, &num_shares,
+ &share_names);
+ if (!W_ERROR_IS_OK(werr)) {
+ 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;
+ WERROR werr;
+
+ if (argc != 0 || c->display_usage) {
+ net_conf_drop_usage(c, argc, argv);
+ goto done;
+ }
+
+ werr = smbconf_drop(conf_ctx);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error deleting configuration: %s\n",
+ dos_errstr(werr));
+ 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;
+ WERROR werr = WERR_OK;
+ 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_lower(mem_ctx, argv[0]);
+ if (sharename == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_get_share(conf_ctx, mem_ctx, sharename, &service);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("error getting share parameters: %s\n",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ d_printf("[%s]\n", sharename);
+
+ 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;
+ WERROR werr = WERR_OK;
+ char *sharename = NULL;
+ const char *path = NULL;
+ const char *comment = NULL;
+ const char *guest_ok = "no";
+ const char *writeable = "no";
+ SMB_STRUCT_STAT sbuf;
+ 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];
+ 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;
+ }
+ 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;
+ }
+ case 2:
+ path = argv[1];
+ sharename = talloc_strdup_lower(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 (getpwnam(sharename)) {
+ d_fprintf(stderr, "ERROR: share name %s is already a valid "
+ "system user name.\n", sharename);
+ 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] != '/') {
+ d_fprintf(stderr,
+ "Error: path '%s' is not an absolute path.\n",
+ path);
+ goto done;
+ }
+
+ if (sys_stat(path, &sbuf) != 0) {
+ d_fprintf(stderr,
+ "ERROR: cannot stat path '%s' to ensure "
+ "this is a directory.\n"
+ "Error was '%s'.\n",
+ path, strerror(errno));
+ goto done;
+ }
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ d_fprintf(stderr,
+ "ERROR: path '%s' is not a directory.\n",
+ path);
+ goto done;
+ }
+
+ /*
+ * create the share
+ */
+
+ werr = smbconf_create_share(conf_ctx, sharename);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error creating share %s: %s\n",
+ sharename, dos_errstr(werr));
+ goto done;
+ }
+
+ /*
+ * fill the share with parameters
+ */
+
+ werr = smbconf_set_parameter(conf_ctx, sharename, "path", path);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error setting parameter %s: %s\n",
+ "path", dos_errstr(werr));
+ goto done;
+ }
+
+ if (comment != NULL) {
+ werr = smbconf_set_parameter(conf_ctx, sharename, "comment",
+ comment);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error setting parameter %s: %s\n",
+ "comment", dos_errstr(werr));
+ goto done;
+ }
+ }
+
+ werr = smbconf_set_parameter(conf_ctx, sharename, "guest ok", guest_ok);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error setting parameter %s: %s\n",
+ "'guest ok'", dos_errstr(werr));
+ goto done;
+ }
+
+ werr = smbconf_set_parameter(conf_ctx, sharename, "writeable",
+ writeable);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error setting parameter %s: %s\n",
+ "writeable", dos_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+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;
+ WERROR werr = WERR_OK;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+
+ if (argc != 1 || c->display_usage) {
+ net_conf_delshare_usage(c, argc, argv);
+ goto done;
+ }
+ sharename = talloc_strdup_lower(mem_ctx, argv[0]);
+ if (sharename == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_delete_share(conf_ctx, sharename);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error deleting share %s: %s\n",
+ sharename, dos_errstr(werr));
+ 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;
+ WERROR werr = WERR_OK;
+ 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;
+ }
+ service = talloc_strdup_lower(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+ param = talloc_strdup_lower(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+ value_str = argv[2];
+
+ if (!smbconf_share_exists(conf_ctx, service)) {
+ werr = smbconf_create_share(conf_ctx, service);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error creating share '%s': %s\n",
+ service, dos_errstr(werr));
+ goto done;
+ }
+ }
+
+ werr = smbconf_set_parameter(conf_ctx, service, param, value_str);
+
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error setting value '%s': %s\n",
+ param, dos_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+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;
+ WERROR werr = WERR_OK;
+ 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;
+ }
+ service = talloc_strdup_lower(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+ param = talloc_strdup_lower(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_get_parameter(conf_ctx, mem_ctx, service, param, &valstr);
+
+ if (W_ERROR_EQUAL(werr, WERR_NO_SUCH_SERVICE)) {
+ d_fprintf(stderr,
+ "Error: given service '%s' does not exist.\n",
+ service);
+ goto done;
+ } else if (W_ERROR_EQUAL(werr, WERR_INVALID_PARAM)) {
+ d_fprintf(stderr,
+ "Error: given parameter '%s' is not set.\n",
+ param);
+ goto done;
+ } else if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error getting value '%s': %s.\n",
+ param, dos_errstr(werr));
+ 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;
+ WERROR werr = WERR_OK;
+ 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;
+ }
+ service = talloc_strdup_lower(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+ param = talloc_strdup_lower(mem_ctx, argv[1]);
+ if (param == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_delete_parameter(conf_ctx, service, param);
+
+ if (W_ERROR_EQUAL(werr, WERR_NO_SUCH_SERVICE)) {
+ d_fprintf(stderr,
+ "Error: given service '%s' does not exist.\n",
+ service);
+ goto done;
+ } else if (W_ERROR_EQUAL(werr, WERR_INVALID_PARAM)) {
+ d_fprintf(stderr,
+ "Error: given parameter '%s' is not set.\n",
+ param);
+ goto done;
+ } else if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "Error deleting value '%s': %s.\n",
+ param, dos_errstr(werr));
+ 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)
+{
+ WERROR werr;
+ 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_lower(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_get_includes(conf_ctx, mem_ctx, service,
+ &num_includes, &includes);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("error getting includes: %s\n", dos_errstr(werr));
+ 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)
+{
+ WERROR werr;
+ 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_lower(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;
+ }
+
+ werr = smbconf_set_includes(conf_ctx, service, num_includes, includes);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("error setting includes: %s\n", dos_errstr(werr));
+ 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)
+{
+ WERROR werr;
+ 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_lower(mem_ctx, argv[0]);
+ if (service == NULL) {
+ d_printf("error: out of memory!\n");
+ goto done;
+ }
+
+ werr = smbconf_delete_includes(conf_ctx, service);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_printf("error deleting includes: %s\n", dos_errstr(werr));
+ 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)
+{
+ WERROR werr;
+ TALLOC_CTX *mem_ctx = talloc_stackframe();
+ struct smbconf_ctx *conf_ctx;
+ int ret = -1;
+
+ werr = smbconf_init(mem_ctx, &conf_ctx, "registry:");
+
+ if (!W_ERROR_IS_OK(werr)) {
+ return -1;
+ }
+
+ ret = fn(c, conf_ctx, argc, argv);
+
+ smbconf_shutdown(conf_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(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,
+ "Dump the complete configuration in smb.conf like "
+ "format.",
+ "net conf list\n"
+ " Dump the complete configuration in smb.conf like "
+ "format."
+
+ },
+ {
+ "import",
+ net_conf_import,
+ NET_TRANSPORT_LOCAL,
+ "Import configuration from file in smb.conf format.",
+ "net conf import\n"
+ " Import configuration from file in smb.conf format."
+ },
+ {
+ "listshares",
+ net_conf_listshares,
+ NET_TRANSPORT_LOCAL,
+ "List the share names.",
+ "net conf listshares\n"
+ " List the share names."
+ },
+ {
+ "drop",
+ net_conf_drop,
+ NET_TRANSPORT_LOCAL,
+ "Delete the complete configuration.",
+ "net conf drop\n"
+ " Delete the complete configuration."
+ },
+ {
+ "showshare",
+ net_conf_showshare,
+ NET_TRANSPORT_LOCAL,
+ "Show the definition of a share.",
+ "net conf showshare\n"
+ " Show the definition of a share."
+ },
+ {
+ "addshare",
+ net_conf_addshare,
+ NET_TRANSPORT_LOCAL,
+ "Create a new share.",
+ "net conf addshare\n"
+ " Create a new share."
+ },
+ {
+ "delshare",
+ net_conf_delshare,
+ NET_TRANSPORT_LOCAL,
+ "Delete a share.",
+ "net conf delshare\n"
+ " Delete a share."
+ },
+ {
+ "setparm",
+ net_conf_setparm,
+ NET_TRANSPORT_LOCAL,
+ "Store a parameter.",
+ "net conf setparm\n"
+ " Store a parameter."
+ },
+ {
+ "getparm",
+ net_conf_getparm,
+ NET_TRANSPORT_LOCAL,
+ "Retrieve the value of a parameter.",
+ "net conf getparm\n"
+ " Retrieve the value of a parameter."
+ },
+ {
+ "delparm",
+ net_conf_delparm,
+ NET_TRANSPORT_LOCAL,
+ "Delete a parameter.",
+ "net conf delparm\n"
+ " Delete a parameter."
+ },
+ {
+ "getincludes",
+ net_conf_getincludes,
+ NET_TRANSPORT_LOCAL,
+ "Show the includes of a share definition.",
+ "net conf getincludes\n"
+ " Show the includes of a share definition."
+ },
+ {
+ "setincludes",
+ net_conf_setincludes,
+ NET_TRANSPORT_LOCAL,
+ "Set includes for a share.",
+ "net conf setincludes\n"
+ " Set includes for a share."
+ },
+ {
+ "delincludes",
+ net_conf_delincludes,
+ NET_TRANSPORT_LOCAL,
+ "Delete includes from a share definition.",
+ "net conf setincludes\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_dns.c b/source3/utils/net_dns.c
new file mode 100644
index 0000000000..14d45e2b0f
--- /dev/null
+++ b/source3/utils/net_dns.c
@@ -0,0 +1,210 @@
+
+/*
+ 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 "dns.h"
+
+#if defined(WITH_DNS_UPDATES)
+
+/*
+ * Silly prototype to get rid of a warning
+ */
+
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName, const char *pszHostName,
+ const struct sockaddr_storage *sslist,
+ size_t num_addrs );
+
+/*********************************************************************
+*********************************************************************/
+
+DNS_ERROR DoDNSUpdate(char *pszServerName,
+ const char *pszDomainName, const char *pszHostName,
+ const struct sockaddr_storage *sslist, size_t num_addrs )
+{
+ DNS_ERROR err;
+ struct dns_connection *conn;
+ TALLOC_CTX *mem_ctx;
+ OM_uint32 minor;
+ struct dns_update_request *req, *resp;
+
+ if ( (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;
+ }
+
+ /*
+ * 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)) goto error;
+
+ if (dns_response_code(resp->flags) == DNS_NO_ERROR) {
+ TALLOC_FREE(mem_ctx);
+ return ERROR_DNS_SUCCESS;
+ }
+
+ /*
+ * First try without signing
+ */
+
+ err = dns_create_update_request(mem_ctx, pszDomainName, pszHostName,
+ sslist, num_addrs, &req);
+ 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;
+
+ if (dns_response_code(resp->flags) == DNS_NO_ERROR) {
+ TALLOC_FREE(mem_ctx);
+ return ERROR_DNS_SUCCESS;
+ }
+
+ /*
+ * Okay, we have to try with signing
+ */
+ {
+ gss_ctx_id_t gss_context;
+ char *keyname;
+
+ 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;
+ }
+
+
+error:
+ TALLOC_FREE(mem_ctx);
+ return err;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+int get_my_ip_address( struct sockaddr_storage **pp_ss )
+
+{
+ struct iface_struct nics[MAX_INTERFACES];
+ int i, n;
+ struct sockaddr_storage *list = NULL;
+ int count = 0;
+
+ /* find the first non-loopback address from our list of interfaces */
+
+ n = get_interfaces(nics, MAX_INTERFACES);
+
+ if (n <= 0) {
+ return -1;
+ }
+
+ if ( (list = SMB_MALLOC_ARRAY( struct sockaddr_storage, n )) == NULL ) {
+ return -1;
+ }
+
+ for ( i=0; i<n; i++ ) {
+ if (is_loopback_addr(&nics[i].ip)) {
+ continue;
+ }
+#if defined(HAVE_IPV6)
+ if ((nics[i].ip.ss_family == AF_INET6)) {
+ memcpy(&list[count++], &nics[i].ip,
+ sizeof(struct sockaddr_storage));
+ } else
+#endif
+ if (nics[i].ip.ss_family == AF_INET) {
+ memcpy(&list[count++], &nics[i].ip,
+ sizeof(struct sockaddr_storage));
+ }
+ }
+ *pp_ss = list;
+
+ return count;
+}
+
+/*
+ * Silly prototype to get rid of a warning
+ */
+
+DNS_ERROR do_gethostbyname(const char *server, const char *host);
+
+DNS_ERROR do_gethostbyname(const char *server, const char *host)
+{
+ struct dns_connection *conn;
+ struct dns_request *req, *resp;
+ DNS_ERROR err;
+
+ err = dns_open_connection(server, DNS_UDP, NULL, &conn);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_create_query(conn, host, QTYPE_A, DNS_CLASS_IN, &req);
+ if (!ERR_DNS_IS_OK(err)) goto error;
+
+ err = dns_transaction(conn, conn, req, &resp);
+
+ error:
+ TALLOC_FREE(conn);
+ return err;
+}
+
+#endif /* defined(WITH_DNS_UPDATES) */
diff --git a/source3/utils/net_dom.c b/source3/utils/net_dom.c
new file mode 100644
index 0000000000..132630de55
--- /dev/null
+++ b/source3/utils/net_dom.c
@@ -0,0 +1,265 @@
+/*
+ Samba Unix/Linux SMB client library
+ net dom commands for remote join/unjoin
+ Copyright (C) 2007 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"
+
+int net_dom_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("usage: net dom join "
+ "<domain=DOMAIN> <ou=OU> <account=ACCOUNT> "
+ "<password=PASSWORD> <reboot>\n Join a remote machine\n");
+ d_printf("usage: net dom unjoin "
+ "<account=ACCOUNT> <password=PASSWORD> <reboot>\n"
+ " Unjoin a remote machine\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;
+ 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.syntax_id,
+ 0, rpc_init_shutdown_internals,
+ argc, argv);
+ if (ret == 0) {
+ goto done;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_winreg.syntax_id, 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);
+ }
+
+ 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.syntax_id, 0,
+ rpc_init_shutdown_internals,
+ argc, argv);
+ if (ret == 0) {
+ goto done;
+ }
+
+ ret = run_rpc_command(c, cli, &ndr_table_winreg.syntax_id, 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,
+ "Join a remote machine",
+ "net dom join <domain=DOMAIN> <ou=OU> "
+ "<account=ACCOUNT> <password=PASSWORD> <reboot>\n"
+ " Join a remote machine"
+ },
+ {
+ "unjoin",
+ net_dom_unjoin,
+ NET_TRANSPORT_LOCAL,
+ "Unjoin a remote machine",
+ "net dom unjoin <account=ACCOUNT> <password=PASSWORD> "
+ "<reboot>\n"
+ " Unjoin a remote machine"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_init(&c->netapi_ctx);
+ if (status != 0) {
+ return -1;
+ }
+
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ return net_run_function(c, argc, argv, "net dom", func);
+}
diff --git a/source3/utils/net_file.c b/source3/utils/net_file.c
new file mode 100644
index 0000000000..8aa1b0e443
--- /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\n");
+ d_printf("net [<method>] file USER <username> "\
+ "[misc. options] [targets]"\
+ "\n\tlists all files opened by username on file server\n\n");
+ d_printf("net [<method>] file CLOSE <id> [misc. options] [targets]\n"\
+ "\tcloses specified file on target server\n\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(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_group.c b/source3/utils/net_group.c
new file mode 100644
index 0000000000..2d5dba8b58
--- /dev/null
+++ b/source3/utils/net_group.c
@@ -0,0 +1,65 @@
+/*
+ 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(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);
+
+ if (argc == 0 && net_rpc_check(c, NET_FLAGS_PDC))
+ return net_rpc_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 0000000000..b160d840a0
--- /dev/null
+++ b/source3/utils/net_groupmap.c
@@ -0,0 +1,906 @@
+/*
+ * 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 "utils/net.h"
+
+/*********************************************************
+ Figure out if the input was an NT group or a SID string.
+ Return the SID.
+**********************************************************/
+static bool get_sid_from_input(DOM_SID *sid, char *input)
+{
+ GROUP_MAP map;
+
+ if (StrnCaseCmp( 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);
+ return false;
+ } else {
+ *sid = map.sid;
+ }
+ } else {
+ if (!string_to_sid(sid, input)) {
+ printf("converting sid %s from a string failed!\n", input);
+ return false;
+ }
+ }
+ return true;
+}
+
+/*********************************************************
+ Dump a GROUP_MAP entry to stdout (long or short listing)
+**********************************************************/
+
+static void print_map_entry ( GROUP_MAP map, bool long_list )
+{
+ if (!long_list)
+ d_printf("%s (%s) -> %s\n", map.nt_name,
+ sid_string_tos(&map.sid), gidtoname(map.gid));
+ else {
+ d_printf("%s\n", map.nt_name);
+ d_printf("\tSID : %s\n", sid_string_tos(&map.sid));
+ d_printf("\tUnix gid : %d\n", 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[] = "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("Usage:\n%s\n", 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(argv[i], "verbose")) {
+ long_list = true;
+ }
+ else if ( !StrnCaseCmp(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(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("Usage:\n%s\n", list_usage_str);
+ return -1;
+ }
+ }
+
+ /* list a single group is given a name */
+ if ( ntgroup[0] || sid_string[0] ) {
+ DOM_SID sid;
+ GROUP_MAP map;
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string);
+
+ if (!get_sid_from_input(&sid, ntgroup)) {
+ return -1;
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_fprintf(stderr, "Failure to local group SID in the database\n");
+ return -1;
+ }
+
+ print_map_entry( map, long_list );
+ }
+ else {
+ GROUP_MAP *map=NULL;
+ /* enumerate all group mappings */
+ if (!pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN, &map, &entries, ENUM_ALL_MAPPED))
+ return -1;
+
+ for (i=0; i<entries; i++) {
+ print_map_entry( map[i], long_list );
+ }
+
+ SAFE_FREE(map);
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Add a new group mapping entry
+**********************************************************/
+
+static int net_groupmap_add(struct net_context *c, int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring unixgrp = "";
+ fstring string_sid = "";
+ fstring type = "";
+ fstring ntcomment = "";
+ enum lsa_SidType sid_type = SID_NAME_DOM_GRP;
+ uint32 rid = 0;
+ gid_t gid;
+ int i;
+ GROUP_MAP map;
+
+ const char *name_type;
+ const char add_usage_str[] = "net groupmap add {rid=<int>|sid=<string>}"
+ " unixgroup=<string> "
+ "[type=<domain|local|builtin>] "
+ "[ntgroup=<string>] [comment=<string>]";
+
+ ZERO_STRUCT(map);
+
+ /* Default is domain group. */
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ name_type = "domain group";
+
+ if (c->display_usage) {
+ d_printf("Usage\n%s\n", add_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(argv[i], "rid", strlen("rid")) ) {
+ rid = get_int_param(argv[i]);
+ if ( rid < DOMAIN_GROUP_RID_ADMINS ) {
+ d_fprintf(stderr, "RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1);
+ return -1;
+ }
+ }
+ else if ( !StrnCaseCmp(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(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(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(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(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("Usage:\n%s\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;
+ }
+
+ {
+ if (pdb_getgrgid(&map, gid)) {
+ d_printf("Unix group %s already mapped to SID %s\n",
+ unixgrp, sid_string_tos(&map.sid));
+ return -1;
+ }
+ }
+
+ if ( (rid == 0) && (string_sid[0] == '\0') ) {
+ d_printf("No rid or sid specified, choosing a RID\n");
+ if (pdb_rid_algorithm()) {
+ rid = algorithmic_pdb_gid_to_group_rid(gid);
+ } else {
+ if (!pdb_new_rid(&rid)) {
+ d_printf("Could not get new RID\n");
+ }
+ }
+ 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_copy(&sid, get_global_sam_sid());
+ sid_append_rid(&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] )
+ fstrcpy( ntgroup, unixgrp );
+
+ 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)
+{
+ DOM_SID sid;
+ GROUP_MAP map;
+ 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[] = "net groupmap modify "
+ "{ntgroup=<string>|sid=<SID>} "
+ "[comment=<string>] "
+ "[unixgroup=<string>] "
+ "[type=<domain|local>]";
+
+ if (c->display_usage) {
+ d_printf("Usage:\n%s\n", modify_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(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(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(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(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(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("Usage:\n%s\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;
+ }
+ }
+
+ /* Get the current mapping from the database */
+ if(!pdb_getgrsid(&map, sid)) {
+ d_fprintf(stderr, "Failure to local group SID in the database\n");
+ 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");
+ return -1;
+ }
+
+ if (map.sid_name_use == SID_NAME_WKN_GRP) {
+ d_fprintf(stderr, "You can only change between domain and local groups.\n");
+ return -1;
+ }
+
+ map.sid_name_use=sid_type;
+
+ /* Change comment if new one */
+ if ( ntcomment[0] )
+ fstrcpy( map.comment, ntcomment );
+
+ if ( ntgroup[0] )
+ fstrcpy( map.nt_name, ntgroup );
+
+ 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);
+ 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");
+ return -1;
+ }
+
+ d_printf("Updated mapping entry for %s\n", map.nt_name);
+
+ return 0;
+}
+
+static int net_groupmap_delete(struct net_context *c, int argc, const char **argv)
+{
+ DOM_SID sid;
+ fstring ntgroup = "";
+ fstring sid_string = "";
+ int i;
+ const char delete_usage_str[] = "net groupmap delete "
+ "{ntgroup=<string>|sid=<SID>}";
+
+ if (c->display_usage) {
+ d_printf("Usage:\n%s\n", delete_usage_str);
+ return 0;
+ }
+
+ /* get the options */
+ for ( i=0; i<argc; i++ ) {
+ if ( !StrnCaseCmp(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(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("Usage:\n%s\n", delete_usage_str);
+ return -1;
+ }
+
+ /* give preference to the SID if we have that */
+
+ if ( sid_string[0] )
+ fstrcpy( ntgroup, sid_string );
+
+ 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 removing group %s from the mapping db!\n", ntgroup);
+ return -1;
+ }
+
+ d_printf("Sucessfully 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("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;
+ }
+ }
+
+ have_map = pdb_getgrnam(&map, ntgroup);
+
+ if (!have_map) {
+ 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);
+ return -1;
+ }
+
+ map.gid = grp->gr_gid;
+
+ if (c->opt_rid == 0) {
+ if ( pdb_rid_algorithm() )
+ c->opt_rid = algorithmic_pdb_gid_to_group_rid(map.gid);
+ else {
+ if ( !pdb_new_rid((uint32*)&c->opt_rid) ) {
+ d_fprintf( stderr, "Could not allocate new RID\n");
+ return -1;
+ }
+ }
+ }
+
+ sid_copy(&map.sid, get_global_sam_sid());
+ sid_append_rid(&map.sid, c->opt_rid);
+
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.nt_name, ntgroup);
+ fstrcpy(map.comment, "");
+
+ if (!NT_STATUS_IS_OK(pdb_add_group_mapping_entry(&map))) {
+ d_fprintf(stderr, "Could not add mapping entry for %s\n",
+ ntgroup);
+ 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);
+ 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 (strlen(c->opt_comment) > 0)
+ fstrcpy(map.comment, c->opt_comment);
+
+ if (strlen(c->opt_newntname) > 0)
+ fstrcpy(map.nt_name, c->opt_newntname);
+
+ 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);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int net_groupmap_cleanup(struct net_context *c, int argc, const char **argv)
+{
+ GROUP_MAP *map = NULL;
+ size_t i, entries;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net groupmap cleanup\n"
+ " Delete all group mappings\n");
+ return 0;
+ }
+
+ if (!pdb_enum_group_mapping(NULL, SID_NAME_UNKNOWN, &map, &entries,
+ ENUM_ALL_MAPPED)) {
+ d_fprintf(stderr, "Could not list group mappings\n");
+ return -1;
+ }
+
+ for (i=0; i<entries; i++) {
+
+ if (map[i].gid == -1)
+ printf("Group %s is not mapped\n", map[i].nt_name);
+
+ if (!sid_check_is_in_our_domain(&map[i].sid)) {
+ printf("Deleting mapping for NT Group %s, sid %s\n",
+ map[i].nt_name,
+ sid_string_tos(&map[i].sid));
+ pdb_delete_group_mapping_entry(map[i].sid);
+ }
+ }
+
+ SAFE_FREE(map);
+
+ return 0;
+}
+
+static int net_groupmap_addmem(struct net_context *c, int argc, const char **argv)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("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)
+{
+ DOM_SID alias, member;
+
+ if ( (argc != 2) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ||
+ !string_to_sid(&member, argv[1]) ) {
+ d_printf("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)
+{
+ DOM_SID alias;
+ DOM_SID *members;
+ size_t i, num;
+
+ if ( (argc != 1) ||
+ c->display_usage ||
+ !string_to_sid(&alias, argv[0]) ) {
+ d_printf("Usage: net groupmap listmem alias-sid\n");
+ return -1;
+ }
+
+ members = NULL;
+ num = 0;
+
+ if (!NT_STATUS_IS_OK(pdb_enum_aliasmem(&alias, &members, &num))) {
+ d_fprintf(stderr, "Could not list members for sid %s\n", argv[0]);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ printf("%s\n", sid_string_tos(&(members[i])));
+ }
+
+ TALLOC_FREE(members);
+
+ return 0;
+}
+
+static bool print_alias_memberships(TALLOC_CTX *mem_ctx,
+ const DOM_SID *domain_sid,
+ const DOM_SID *member)
+{
+ uint32 *alias_rids;
+ size_t i, num_alias_rids;
+
+ 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",
+ sid_string_tos(member));
+ return false;
+ }
+
+ for (i = 0; i < num_alias_rids; i++) {
+ DOM_SID alias;
+ sid_copy(&alias, domain_sid);
+ sid_append_rid(&alias, alias_rids[i]);
+ printf("%s\n", sid_string_tos(&alias));
+ }
+
+ return true;
+}
+
+static int net_groupmap_memberships(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ DOM_SID *domain_sid, *builtin_sid, member;
+
+ if ( (argc != 1) ||
+ c->display_usage ||
+ !string_to_sid(&member, argv[0]) ) {
+ d_printf("Usage: net groupmap memberof 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();
+ builtin_sid = string_sid_talloc(mem_ctx, "S-1-5-32");
+ if ((domain_sid == NULL) || (builtin_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, builtin_sid, &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,
+ "Create a new group mapping",
+ "net groupmap add\n"
+ " Create a new group mapping"
+ },
+ {
+ "modify",
+ net_groupmap_modify,
+ NET_TRANSPORT_LOCAL,
+ "Update a group mapping",
+ "net groupmap modify\n"
+ " Modify an existing group mapping"
+ },
+ {
+ "delete",
+ net_groupmap_delete,
+ NET_TRANSPORT_LOCAL,
+ "Remove a group mapping",
+ "net groupmap delete\n"
+ " Remove a group mapping"
+ },
+ {
+ "set",
+ net_groupmap_set,
+ NET_TRANSPORT_LOCAL,
+ "Set group mapping",
+ "net groupmap set\n"
+ " Set a group mapping"
+ },
+ {
+ "cleanup",
+ net_groupmap_cleanup,
+ NET_TRANSPORT_LOCAL,
+ "Remove foreign group mapping entries",
+ "net groupmap cleanup\n"
+ " Remove foreign group mapping entries"
+ },
+ {
+ "addmem",
+ net_groupmap_addmem,
+ NET_TRANSPORT_LOCAL,
+ "Add a foreign alias member",
+ "net groupmap addmem\n"
+ " Add a foreign alias member"
+ },
+ {
+ "delmem",
+ net_groupmap_delmem,
+ NET_TRANSPORT_LOCAL,
+ "Delete foreign alias member",
+ "net groupmap delmem\n"
+ " Delete foreign alias member"
+ },
+ {
+ "listmem",
+ net_groupmap_listmem,
+ NET_TRANSPORT_LOCAL,
+ "List foreign group members",
+ "net groupmap listmem\n"
+ " List foreign alias members"
+ },
+ {
+ "memberships",
+ net_groupmap_memberships,
+ NET_TRANSPORT_LOCAL,
+ "List foreign group memberships",
+ "net groupmap memberships\n"
+ " List foreign group memberships"
+ },
+ {
+ "list",
+ net_groupmap_list,
+ NET_TRANSPORT_LOCAL,
+ "List current group map",
+ "net groupmap list\n"
+ " List current group map"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ /* we shouldn't have silly checks like this */
+ if (getuid() != 0) {
+ d_fprintf(stderr, "You must be root to edit group mappings.\n");
+ return -1;
+ }
+
+ 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 0000000000..0502373aa2
--- /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(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 0000000000..edf083905a
--- /dev/null
+++ b/source3/utils/net_help_common.c
@@ -0,0 +1,55 @@
+/*
+ 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 or --server=<server>\t\tserver name\n");
+ d_printf("\t-I or --ipaddress=<ipaddr>\taddress of target server\n");
+ d_printf("\t-w or --workgroup=<wg>\t\ttarget workgroup or domain\n");
+
+ d_printf("\n");
+ d_printf("Valid miscellaneous options are:\n"); /* misc options */
+ d_printf("\t-p or --port=<port>\t\tconnection port on target\n");
+ d_printf("\t-W or --myworkgroup=<wg>\tclient workgroup\n");
+ d_printf("\t-d or --debuglevel=<level>\tdebug level (0-10)\n");
+ d_printf("\t-n or --myname=<name>\t\tclient name\n");
+ d_printf("\t-U or --user=<name>\t\tuser name\n");
+ d_printf("\t-s or --configfile=<path>\tpathname of smb.conf file\n");
+ d_printf("\t-l or --long\t\t\tDisplay full information\n");
+ d_printf("\t-V or --version\t\t\tPrint samba version information\n");
+ d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n");
+ d_printf("\t-e or --encrypt\t\t\tEncrypt SMB transport (UNIX extended servers only)\n");
+ d_printf("\t-k or --kerberos\t\tUse kerberos (active directory) authentication\n");
+ return -1;
+}
+
diff --git a/source3/utils/net_help_common.h b/source3/utils/net_help_common.h
new file mode 100644
index 0000000000..ed859936e4
--- /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 0000000000..bd363922f6
--- /dev/null
+++ b/source3/utils/net_idmap.c
@@ -0,0 +1,441 @@
+/*
+ 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 "utils/net.h"
+
+#define ALLOC_CHECK(mem) do { \
+ if (!mem) { \
+ d_fprintf(stderr, "Out of memory!\n"); \
+ talloc_free(ctx); \
+ return -1; \
+ } } while(0)
+
+/***********************************************************
+ Helper function for net_idmap_dump. Dump one entry.
+ **********************************************************/
+static int net_idmap_dump_one_entry(TDB_CONTEXT *tdb,
+ TDB_DATA key,
+ TDB_DATA data,
+ void *unused)
+{
+ if (strcmp((char *)key.dptr, "USER HWM") == 0) {
+ printf("USER HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strcmp((char *)key.dptr, "GROUP HWM") == 0) {
+ printf("GROUP HWM %d\n", IVAL(data.dptr,0));
+ return 0;
+ }
+
+ if (strncmp((char *)key.dptr, "S-", 2) != 0)
+ return 0;
+
+ printf("%s %s\n", data.dptr, key.dptr);
+ return 0;
+}
+
+/***********************************************************
+ Dump the current idmap
+ **********************************************************/
+static int net_idmap_dump(struct net_context *c, int argc, const char **argv)
+{
+ TDB_CONTEXT *idmap_tdb;
+
+ if ( argc != 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net idmap dump <inputfile>\n"
+ " Dump current ID mapping.\n"
+ " inputfile\tTDB file to read mappings from.\n");
+ return c->display_usage?0:-1;
+ }
+
+ idmap_tdb = tdb_open_log(argv[0], 0, TDB_DEFAULT, O_RDONLY, 0);
+
+ if (idmap_tdb == NULL) {
+ d_fprintf(stderr, "Could not open idmap: %s\n", argv[0]);
+ return -1;
+ }
+
+ tdb_traverse(idmap_tdb, net_idmap_dump_one_entry, NULL);
+
+ tdb_close(idmap_tdb);
+
+ return 0;
+}
+
+/***********************************************************
+ Write entries from stdin to current local idmap
+ **********************************************************/
+
+static int net_idmap_restore(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *ctx;
+ FILE *input;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net idmap restore [inputfile]\n"
+ " Restore ID mappings from file\n"
+ " inputfile\tFile to load ID mappings from. If not "
+ "given, load data from stdin.\n");
+ return 0;
+ }
+
+ if (! winbind_ping()) {
+ d_fprintf(stderr, "To use net idmap Winbindd must be running.\n");
+ return -1;
+ }
+
+ ctx = talloc_new(NULL);
+ ALLOC_CHECK(ctx);
+
+ if (argc == 1) {
+ input = fopen(argv[0], "r");
+ } else {
+ input = stdin;
+ }
+
+ while (!feof(input)) {
+ char line[128], sid_string[128];
+ int len;
+ struct wbcDomainSid sid;
+ enum id_type type = ID_TYPE_NOT_SPECIFIED;
+ unsigned long idval;
+ wbcErr wbc_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 %128s", &idval, sid_string) == 2) {
+ type = ID_TYPE_GID;
+ } else if (sscanf(line, "UID %lu %128s", &idval, sid_string) == 2) {
+ type = ID_TYPE_UID;
+ } else if (sscanf(line, "USER HWM %lu", &idval) == 1) {
+ /* set uid hwm */
+ wbc_status = wbcSetUidHwm(idval);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "Could not set USER HWM: %s\n",
+ wbcErrorString(wbc_status));
+ }
+ continue;
+ } else if (sscanf(line, "GROUP HWM %lu", &idval) == 1) {
+ /* set gid hwm */
+ wbc_status = wbcSetGidHwm(idval);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "Could not set GROUP HWM: %s\n",
+ wbcErrorString(wbc_status));
+ }
+ continue;
+ } else {
+ d_fprintf(stderr, "ignoring invalid line [%s]\n", line);
+ continue;
+ }
+
+ wbc_status = wbcStringToSid(sid_string, &sid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "ignoring invalid sid [%s]: %s\n",
+ sid_string, wbcErrorString(wbc_status));
+ continue;
+ }
+
+ if (type == ID_TYPE_UID) {
+ wbc_status = wbcSetUidMapping(idval, &sid);
+ } else {
+ wbc_status = wbcSetGidMapping(idval, &sid);
+ }
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ d_fprintf(stderr, "Could not set mapping of %s %lu to sid %s: %s\n",
+ (type == ID_TYPE_GID) ? "GID" : "UID",
+ idval, sid_string,
+ wbcErrorString(wbc_status));
+ continue;
+ }
+ }
+
+ if (input != stdin) {
+ fclose(input);
+ }
+
+ talloc_free(ctx);
+ return 0;
+}
+
+/***********************************************************
+ Delete a SID mapping from a winbindd_idmap.tdb
+ **********************************************************/
+static int net_idmap_delete(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("Not Implemented yet\n");
+ return -1;
+}
+
+static int net_idmap_set(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("Not Implemented yet\n");
+ return -1;
+}
+bool idmap_store_secret(const char *backend, bool alloc,
+ const char *domain, const char *identity,
+ const char *secret)
+{
+ char *tmp;
+ int r;
+ bool ret;
+
+ if (alloc) {
+ r = asprintf(&tmp, "IDMAP_ALLOC_%s", backend);
+ } else {
+ r = asprintf(&tmp, "IDMAP_%s_%s", backend, domain);
+ }
+
+ if (r < 0) return false;
+
+ strupper_m(tmp); /* make sure the key is case insensitive */
+ 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("Usage:\n"
+ "net idmap secret {<DOMAIN>|alloc} <secret>\n"
+ " Set the secret for the specified domain "
+ "(or alloc module)\n"
+ " DOMAIN\tDomain to set secret for.\n"
+ " alloc\tSet secret for the alloc module\n"
+ " secret\tNew secret to set.\n");
+ return c->display_usage?0:-1;
+ }
+
+ secret = argv[1];
+
+ ctx = talloc_new(NULL);
+ ALLOC_CHECK(ctx);
+
+ if (strcmp(argv[0], "alloc") == 0) {
+ domain = NULL;
+ backend = lp_idmap_alloc_backend();
+ } else {
+ 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"))) {
+ d_fprintf(stderr, "The only currently supported backend is LDAP\n");
+ talloc_free(ctx);
+ return -1;
+ }
+
+ if (domain) {
+
+ 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", false, domain, dn, secret);
+ } else {
+ dn = lp_parm_const_string(-1, "idmap alloc config", "ldap_user_dn", NULL);
+ if ( ! dn) {
+ d_fprintf(stderr, "Missing ldap_user_dn option for alloc backend\n");
+ talloc_free(ctx);
+ return -1;
+ }
+
+ ret = idmap_store_secret("ldap", true, NULL, dn, secret);
+ }
+
+ if ( ! ret) {
+ d_fprintf(stderr, "Failed to store secret\n");
+ talloc_free(ctx);
+ return -1;
+ }
+
+ d_printf("Secret stored\n");
+ return 0;
+}
+
+int net_help_idmap(struct net_context *c, int argc, const char **argv)
+{
+ d_printf("net idmap dump <inputfile>\n"
+ " Dump current id mapping\n");
+
+ d_printf("net idmap restore\n"
+ " Restore entries from stdin\n");
+
+ /* Deliberately *not* document net idmap delete */
+
+ d_printf("net idmap secret <DOMAIN>|alloc <secret>\n"
+ " Set the secret for the specified DOMAIN (or the alloc module)\n");
+
+ return -1;
+}
+
+static int net_idmap_aclmapset(struct net_context *c, int argc, const char **argv)
+{
+ TALLOC_CTX *mem_ctx;
+ int result = -1;
+ DOM_SID src_sid, dst_sid;
+ char *src, *dst;
+ struct db_context *db;
+ struct db_record *rec;
+ NTSTATUS status;
+
+ if (argc != 3 || c->display_usage) {
+ d_fprintf(stderr, "usage: net idmap aclmapset <tdb> "
+ "<src-sid> <dst-sid>\n");
+ return -1;
+ }
+
+ if (!(mem_ctx = talloc_init("net idmap aclmapset"))) {
+ d_fprintf(stderr, "talloc_init failed\n");
+ return -1;
+ }
+
+ if (!(db = db_open(mem_ctx, argv[0], 0, TDB_DEFAULT,
+ O_RDWR|O_CREAT, 0600))) {
+ d_fprintf(stderr, "db_open failed: %s\n", strerror(errno));
+ goto fail;
+ }
+
+ if (!string_to_sid(&src_sid, argv[1])) {
+ d_fprintf(stderr, "%s is not a valid sid\n", argv[1]);
+ goto fail;
+ }
+
+ if (!string_to_sid(&dst_sid, argv[2])) {
+ d_fprintf(stderr, "%s is not a valid sid\n", argv[2]);
+ goto fail;
+ }
+
+ if (!(src = sid_string_talloc(mem_ctx, &src_sid))
+ || !(dst = sid_string_talloc(mem_ctx, &dst_sid))) {
+ d_fprintf(stderr, "talloc_strdup failed\n");
+ goto fail;
+ }
+
+ if (!(rec = db->fetch_locked(
+ db, mem_ctx, string_term_tdb_data(src)))) {
+ d_fprintf(stderr, "could not fetch db record\n");
+ goto fail;
+ }
+
+ status = rec->store(rec, string_term_tdb_data(dst), 0);
+ TALLOC_FREE(rec);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "could not store record: %s\n",
+ nt_errstr(status));
+ goto fail;
+ }
+
+ result = 0;
+fail:
+ TALLOC_FREE(mem_ctx);
+ return result;
+}
+
+/***********************************************************
+ 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,
+ "Dump the current ID mappings",
+ "net idmap dump\n"
+ " Dump the current ID mappings"
+ },
+ {
+ "restore",
+ net_idmap_restore,
+ NET_TRANSPORT_LOCAL,
+ "Restore entries from stdin",
+ "net idmap restore\n"
+ " Restore entries from stdin"
+ },
+ {
+ "setmap",
+ net_idmap_set,
+ NET_TRANSPORT_LOCAL,
+ "Not implemented yet",
+ "net idmap setmap\n"
+ " Not implemented yet"
+ },
+ {
+ "delete",
+ net_idmap_delete,
+ NET_TRANSPORT_LOCAL,
+ "Not implemented yet",
+ "net idmap delete\n"
+ " Not implemented yet"
+ },
+ {
+ "secret",
+ net_idmap_secret,
+ NET_TRANSPORT_LOCAL,
+ "Set secret for specified domain",
+ "net idmap secret {<DOMAIN>|alloc} <secret>\n"
+ " Set secret for specified domain or alloc module"
+ },
+ {
+ "aclmapset",
+ net_idmap_aclmapset,
+ NET_TRANSPORT_LOCAL,
+ "Set acl map",
+ "net idmap aclmapset\n"
+ " Set acl map"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net idmap", func);
+}
+
+
diff --git a/source3/utils/net_join.c b/source3/utils/net_join.c
new file mode 100644
index 0000000000..98188aae5f
--- /dev/null
+++ b/source3/utils/net_join.c
@@ -0,0 +1,54 @@
+/*
+ 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 < 1)
+ return net_join_usage(c, argc, argv);
+
+ if (StrCaseCmp(argv[0], "HELP") == 0) {
+ net_join_usage(c, argc, argv);
+ return 0;
+ }
+
+ 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 0000000000..8b9ddb62ba
--- /dev/null
+++ b/source3/utils/net_lookup.c
@@ -0,0 +1,447 @@
+/*
+ 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"
+
+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]\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)) {
+ /* 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, int numdcs )
+{
+ struct sockaddr_storage ss;
+ int i;
+
+ for ( i=0; i<numdcs; i++ ) {
+ if (resolve_name(dclist[i].hostname, &ss, 0x20) ) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &ss);
+#ifdef HAVE_IPV6
+ if (ss.ss_family == AF_INET6) {
+ d_printf("[%s]:%d\n", addr, dclist[i].port);
+ }
+#endif
+ if (ss.ss_family == AF_INET) {
+ d_printf("%s:%d\n", addr, dclist[i].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;
+ int numdcs = 0;
+ char *sitename;
+ TALLOC_CTX *ctx;
+ NTSTATUS status;
+ int ret;
+ char h_name[MAX_DNS_NAME_LENGTH];
+
+ if (argc > 0)
+ domain = argv[0];
+ else
+ domain = c->opt_target_workgroup;
+
+ sitename = sitename_fetch(domain);
+
+ if ( (ctx = talloc_init("net_lookup_ldap")) == NULL ) {
+ d_fprintf(stderr, "net_lookup_ldap: talloc_init() failed!\n");
+ SAFE_FREE(sitename);
+ return -1;
+ }
+
+ DEBUG(9, ("Lookup up ldap for domain %s\n", domain));
+
+ status = ads_dns_query_dcs( ctx, domain, sitename, &dcs, &numdcs );
+ if ( NT_STATUS_IS_OK(status) && numdcs ) {
+ print_ldap_srvlist(dcs, numdcs);
+ TALLOC_FREE( ctx );
+ SAFE_FREE(sitename);
+ return 0;
+ }
+
+ DEBUG(9, ("Looking up PDC for domain %s\n", domain));
+ if (!get_pdc_ip(domain, &ss)) {
+ TALLOC_FREE( ctx );
+ SAFE_FREE(sitename);
+ 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 );
+ SAFE_FREE(sitename);
+ return -1;
+ }
+
+ DEBUG(9, ("Found PDC with DNS name %s\n", h_name));
+ domain = strchr(h_name, '.');
+ if (!domain) {
+ TALLOC_FREE( ctx );
+ SAFE_FREE(sitename);
+ return -1;
+ }
+ domain++;
+
+ DEBUG(9, ("Looking up ldap for domain %s\n", domain));
+
+ status = ads_dns_query_dcs( ctx, domain, sitename, &dcs, &numdcs );
+ if ( NT_STATUS_IS_OK(status) && numdcs ) {
+ print_ldap_srvlist(dcs, numdcs);
+ TALLOC_FREE( ctx );
+ SAFE_FREE(sitename);
+ return 0;
+ }
+
+ TALLOC_FREE( ctx );
+ SAFE_FREE(sitename);
+
+ 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 ip_service *ip_list;
+ struct sockaddr_storage ss;
+ char *pdc_str = NULL;
+ const char *domain = NULL;
+ char *sitename = NULL;
+ int count, i;
+ char addr[INET6_ADDRSTRLEN];
+ bool sec_ads = (lp_security() == SEC_ADS);
+
+ 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);
+ asprintf(&pdc_str, "%s", addr);
+ d_printf("%s\n", pdc_str);
+
+ sitename = sitename_fetch(domain);
+ if (!NT_STATUS_IS_OK(get_sorted_dc_list(domain, sitename,
+ &ip_list, &count, sec_ads))) {
+ SAFE_FREE(pdc_str);
+ SAFE_FREE(sitename);
+ return 0;
+ }
+ SAFE_FREE(sitename);
+ for (i=0;i<count;i++) {
+ print_sockaddr(addr, sizeof(addr), &ip_list[i].ss);
+ if (!strequal(pdc_str, addr))
+ d_printf("%s\n", addr);
+ }
+ 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);
+ asprintf(&pdc_str, "%s", addr);
+ 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 sockaddr_in *addrs;
+ int num_kdcs,i;
+ krb5_data realm;
+ char **realms;
+
+ initialize_krb5_error_table();
+ rc = krb5_init_context(&ctx);
+ if (rc) {
+ DEBUG(1,("krb5_init_context failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+
+ if (argc>0) {
+ realm.data = CONST_DISCARD(char *, argv[0]);
+ realm.length = strlen(argv[0]);
+ } else if (lp_realm() && *lp_realm()) {
+ realm.data = lp_realm();
+ realm.length = strlen((const char *)realm.data);
+ } else {
+ rc = krb5_get_host_realm(ctx, NULL, &realms);
+ if (rc) {
+ DEBUG(1,("krb5_gethost_realm failed (%s)\n",
+ error_message(rc)));
+ return -1;
+ }
+ realm.data = (char *) *realms;
+ realm.length = strlen((const char *)realm.data);
+ }
+
+ rc = smb_krb5_locate_kdc(ctx, &realm, (struct sockaddr **)(void *)&addrs, &num_kdcs, 0);
+ if (rc) {
+ DEBUG(1, ("smb_krb5_locate_kdc failed (%s)\n", error_message(rc)));
+ return -1;
+ }
+ for (i=0;i<num_kdcs;i++)
+ if (addrs[i].sin_family == AF_INET)
+ d_printf("%s:%hd\n", inet_ntoa(addrs[i].sin_addr),
+ ntohs(addrs[i].sin_port));
+ 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;
+ DOM_SID sid;
+ enum lsa_SidType type;
+
+ if (argc != 1) {
+ d_printf("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", sid_string_tos(&sid),
+ 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;
+ DOM_SID sid;
+ enum lsa_SidType type;
+
+ if (argc != 1) {
+ d_printf("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", sid_string_tos(&sid),
+ 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("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) {
+ flags |= DS_DIRECTORY_SERVICE_REQUIRED;
+ }
+
+ if (argc == 3) {
+ site_name = argv[2];
+ }
+
+ status = dsgetdcname(mem_ctx, NULL, 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[] = {
+ {"HOST", net_lookup_host},
+ {"LDAP", net_lookup_ldap},
+ {"DC", net_lookup_dc},
+ {"PDC", net_lookup_pdc},
+ {"MASTER", net_lookup_master},
+ {"KDC", net_lookup_kdc},
+ {"NAME", net_lookup_name},
+ {"SID", net_lookup_sid},
+ {"DSGETDCNAME", net_lookup_dsgetdcname},
+ {NULL, NULL}
+ };
+
+ if (argc < 1) {
+ d_printf("\nUsage: \n");
+ return net_lookup_usage(c, argc, argv);
+ }
+ for (i=0; table[i].funcname; i++) {
+ if (StrCaseCmp(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_proto.h b/source3/utils/net_proto.h
new file mode 100644
index 0000000000..1e355e54a3
--- /dev/null
+++ b/source3/utils/net_proto.h
@@ -0,0 +1,489 @@
+/*
+ * 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_
+
+
+/* The following definitions come from auth/token_util.c */
+
+bool nt_token_check_sid ( const DOM_SID *sid, const NT_USER_TOKEN *token );
+bool nt_token_check_domain_rid( NT_USER_TOKEN *token, uint32 rid );
+NT_USER_TOKEN *get_root_nt_token( void );
+NTSTATUS add_aliases(const DOM_SID *domain_sid,
+ struct nt_user_token *token);
+struct nt_user_token *create_local_nt_token(TALLOC_CTX *mem_ctx,
+ const DOM_SID *user_sid,
+ bool is_guest,
+ int num_groupsids,
+ const DOM_SID *groupsids);
+void debug_nt_user_token(int dbg_class, int dbg_lev, NT_USER_TOKEN *token);
+void debug_unix_user_token(int dbg_class, int dbg_lev, uid_t uid, gid_t gid,
+ int n_groups, gid_t *groups);
+
+/* The following definitions come from utils/net.c */
+
+uint32 get_sec_channel_type(const char *param);
+
+/* The following definitions come from utils/net_ads.c */
+
+ADS_STATUS ads_startup(struct net_context *c, bool only_own_domain, ADS_STRUCT **ads);
+ADS_STATUS ads_startup_nobind(struct net_context *c, bool only_own_domain, 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(struct net_context *c, int argc, const char **argv);
+
+/* 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 */
+
+bool idmap_store_secret(const char *backend, bool alloc,
+ const char *domain, const char *identity,
+ const char *secret);
+int net_help_idmap(struct net_context *c, int argc, const char **argv);
+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 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,
+ DOM_SID **domain_sid,
+ const char **domain_name);
+int run_rpc_command(struct net_context *c,
+ struct cli_state *cli_arg,
+ const struct ndr_syntax_id *interface,
+ 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_join(struct net_context *c, int argc, const char **argv);
+NTSTATUS rpc_info_internals(struct net_context *c,
+ const DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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_join.c */
+
+NTSTATUS net_rpc_join_ok(struct net_context *c, const char *domain,
+ const char *server, struct sockaddr_storage *pss);
+int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv);
+int net_rpc_testjoin(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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_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 */
+
+NTSTATUS rpc_samdump_internals(struct net_context *c,
+ const DOM_SID *domain_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 rpc_vampire_usage(struct net_context *c, int argc, const char **argv);
+NTSTATUS rpc_vampire_internals(struct net_context *c,
+ const DOM_SID *domain_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 rpc_vampire_ldif(struct net_context *c, int argc, const char **argv);
+NTSTATUS rpc_vampire_ldif_internals(struct net_context *c,
+ const DOM_SID *domain_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_vampire_keytab_internals(struct net_context *c,
+ const DOM_SID *domain_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 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 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_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, DOM_SID *ret_sid,
+ enum lsa_SidType *ret_type);
+NTSTATUS connect_to_service(struct net_context *c,
+ struct cli_state **cli_ctx,
+ 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,
+ struct sockaddr_storage *server_ss,
+ const char *server_name);
+NTSTATUS connect_to_ipc_anonymous(struct net_context *c,
+ struct cli_state **cli_ctx,
+ struct sockaddr_storage *server_ss,
+ const char *server_name);
+NTSTATUS connect_to_ipc_krb5(struct net_context *c,
+ struct cli_state **cli_ctx,
+ 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_syntax_id *interface);
+int net_use_krb_machine_account(struct net_context *c);
+int net_use_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,
+ 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);
+
+/* The following definitions come from utils/netlookup.c */
+
+NTSTATUS net_lookup_name_from_sid(struct net_context *c,
+ TALLOC_CTX *ctx,
+ 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, DOM_SID *pret_sid);
+
+/* The following definitions come from utils/passwd_util.c */
+
+char *stdin_new_passwd( void);
+char *get_pass( const char *prompt, bool stdin_get);
+
+#endif /* _NET_PROTO_H_ */
diff --git a/source3/utils/net_rap.c b/source3/utils/net_rap.c
new file mode 100644
index 0000000000..883524dc2d
--- /dev/null
+++ b/source3/utils/net_rap.c
@@ -0,0 +1,1374 @@
+/*
+ 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 "utils/net.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"
+
+const char *share_type[] = {
+ "Disk",
+ "Print",
+ "Dev",
+ "IPC"
+};
+
+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 perms,
+ uint16 locks, uint32 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 perms,
+ uint16 locks, uint32 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,
+ "Close specified file on server",
+ "net rap file close\n"
+ " Close specified file on server"
+ },
+ {
+ "user",
+ rap_file_user,
+ NET_TRANSPORT_RAP,
+ "List all files opened by username",
+ "net rap file user\n"
+ " List all files opened by username"
+ },
+ {
+ "info",
+ rap_file_info,
+ NET_TRANSPORT_RAP,
+ "Display info about an opened file",
+ "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 type,
+ const char *comment, void *state)
+{
+ d_printf("%-12s %-8.8s %-50s\n",
+ share_name, share_type[type], comment);
+}
+
+static void share_fn(const char *share_name, uint32 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;
+
+ 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(sinfo.share_name, sharename, sizeof(sinfo.share_name));
+ sinfo.reserved1 = '\0';
+ sinfo.share_type = 0;
+ sinfo.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,
+ "Delete a share from server",
+ "net rap share delete\n"
+ " Delete a share from server"
+ },
+ {
+ "close",
+ rap_share_delete,
+ NET_TRANSPORT_RAP,
+ "Delete a share from server",
+ "net rap share close\n"
+ " Delete a share from server\n"
+ " Alias for net rap share delete"
+ },
+ {
+ "add",
+ rap_share_add,
+ NET_TRANSPORT_RAP,
+ "Add a share to server",
+ "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 conns,
+ uint16 opens, uint16 users, uint32 sess_time,
+ uint32 idle_time, uint32 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 conns, uint16 opens, uint16 users,
+ uint32 sess_time, uint32 idle_time,
+ uint32 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 conn_id, uint16 conn_type, uint16 opens,
+ uint16 users, uint32 conn_time,
+ const char *username, const char *netname)
+{
+ d_printf("%-14.14s %-8.8s %5d\n",
+ netname, share_type[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,
+ "Display information about session",
+ "net rap session info\n"
+ " Display information about session"
+ },
+ {
+ "delete",
+ rap_session_delete,
+ NET_TRANSPORT_RAP,
+ "Close specified session",
+ "net rap session delete\n"
+ " Close specified session\n"
+ " Alias for net rap session close"
+ },
+ {
+ "close",
+ rap_session_delete,
+ NET_TRANSPORT_RAP,
+ "Close specified session",
+ "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 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("Usage:\n"
+ "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("Usage:\n"
+ "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,
+ "Get the name of the server",
+ "net rap server name\n"
+ " Get the name of the server"
+ },
+ {
+ "domain",
+ net_rap_server_domain,
+ NET_TRANSPORT_RAP,
+ "Get the servers in this domain/workgroup",
+ "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 list [<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 pri, uint16 start,
+ uint16 until, const char *sep, const char *pproc,
+ const char *dest, const char *qparms,
+ const char *qcomment, uint16 status, uint16 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 jobid, const char *ownername,
+ const char *notifyname, const char *datatype,
+ const char *jparms, uint16 pos, uint16 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, cli->desthost); /* 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;
+ 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;
+
+ ret = cli_printjob_del(cli, atoi(argv[0]));
+ cli_shutdown(cli);
+ return ret;
+}
+
+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,
+ "Display info about print job",
+ "net rap printq info\n"
+ " Display info about print job"
+ },
+ {
+ "delete",
+ rap_printq_delete,
+ NET_TRANSPORT_RAP,
+ "Delete print job(s)",
+ "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, cli->desthost); /* 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;
+ 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;
+
+ safe_strcpy(userinfo.user_name, argv[0], sizeof(userinfo.user_name)-1);
+ if (c->opt_flags == -1)
+ c->opt_flags = 0x21;
+
+ userinfo.userflags = c->opt_flags;
+ userinfo.reserved1 = '\0';
+ userinfo.comment = smb_xstrdup(c->opt_comment);
+ userinfo.priv = 1;
+ userinfo.home_dir = NULL;
+ userinfo.logon_script = NULL;
+
+ 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,
+ "Add specified user",
+ "net rap user add\n"
+ " Add specified user"
+ },
+ {
+ "info",
+ rap_user_info,
+ NET_TRANSPORT_RAP,
+ "List domain groups of specified user",
+ "net rap user info\n"
+ " List domain groups of specified user"
+
+ },
+ {
+ "delete",
+ rap_user_delete,
+ NET_TRANSPORT_RAP,
+ "Remove specified user",
+ "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;
+ 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 */
+ safe_strcpy(grinfo.group_name, argv[0], sizeof(grinfo.group_name)-1);
+ grinfo.reserved1 = '\0';
+ grinfo.comment = smb_xstrdup(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,
+ "Add specified group",
+ "net rap group add\n"
+ " Add specified group"
+ },
+ {
+ "delete",
+ rap_group_delete,
+ NET_TRANSPORT_RAP,
+ "Delete specified group",
+ "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");
+ d_printf("-----------------------------\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 sepcified 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,
+ "Add specified user to group",
+ "net rap groupmember add\n"
+ " Add specified user to group"
+ },
+ {
+ "list",
+ rap_groupmember_list,
+ NET_TRANSPORT_RAP,
+ "List users in group",
+ "net rap groupmember list\n"
+ " List users in group"
+ },
+ {
+ "delete",
+ rap_groupmember_delete,
+ NET_TRANSPORT_RAP,
+ "Remove user from group",
+ "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,
+ "Start service on remote server",
+ "net rap service start\n"
+ " Start service on remote server"
+ },
+ {
+ "stop",
+ rap_service_stop,
+ NET_TRANSPORT_RAP,
+ "Stop named serve on remote server",
+ "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");
+ d_printf("-----------------------------\n");
+ ret = cli_RNetServiceEnum(cli, long_group_fn, NULL);
+ }
+ 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,
+ "List open files",
+ "net rap file\n"
+ " List open files"
+ },
+ {
+ "share",
+ net_rap_share,
+ NET_TRANSPORT_RAP,
+ "List shares exported by server",
+ "net rap share\n"
+ " List shares exported by server"
+ },
+ {
+ "session",
+ net_rap_session,
+ NET_TRANSPORT_RAP,
+ "List open sessions",
+ "net rap session\n"
+ " List open sessions"
+ },
+ {
+ "server",
+ net_rap_server,
+ NET_TRANSPORT_RAP,
+ "List servers in workgroup",
+ "net rap server\n"
+ " List servers in domain/workgroup"
+ },
+ {
+ "domain",
+ net_rap_domain,
+ NET_TRANSPORT_RAP,
+ "List domains in network",
+ "net rap domain\n"
+ " List domains in network"
+ },
+ {
+ "printq",
+ net_rap_printq,
+ NET_TRANSPORT_RAP,
+ "List printer queues on server",
+ "net rap printq\n"
+ " List printer queues on server"
+ },
+ {
+ "user",
+ net_rap_user,
+ NET_TRANSPORT_RAP,
+ "List users",
+ "net rap user\n"
+ " List users"
+ },
+ {
+ "group",
+ net_rap_group,
+ NET_TRANSPORT_RAP,
+ "List user groups",
+ "net rap group\n"
+ " List user groups"
+ },
+ {
+ "validate",
+ net_rap_validate,
+ NET_TRANSPORT_RAP,
+ "Check username/password",
+ "net rap validate\n"
+ " Check username/password"
+ },
+ {
+ "groupmember",
+ net_rap_groupmember,
+ NET_TRANSPORT_RAP,
+ "List/modify group memberships",
+ "net rap groupmember\n"
+ " List/modify group memberships"
+ },
+ {
+ "admin",
+ net_rap_admin,
+ NET_TRANSPORT_RAP,
+ "Execute commands on remote OS/2",
+ "net rap admin\n"
+ " Execute commands on remote OS/2"
+ },
+ {
+ "service",
+ net_rap_service,
+ NET_TRANSPORT_RAP,
+ "Start/stop remote service",
+ "net rap service\n"
+ " Start/stop remote service"
+ },
+ {
+ "password",
+ net_rap_password,
+ NET_TRANSPORT_RAP,
+ "Change user password",
+ "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 0000000000..26710b3580
--- /dev/null
+++ b/source3/utils/net_registry.c
@@ -0,0 +1,531 @@
+/*
+ * 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 "utils/net.h"
+#include "utils/net_registry_util.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 desired_access,
+ struct registry_key **hive,
+ char **subkeyname)
+{
+ WERROR werr;
+ NT_USER_TOKEN *token = NULL;
+ char *hivename = NULL;
+ char *tmp_subkeyname = NULL;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ if ((hive == NULL) || (subkeyname == NULL)) {
+ werr = WERR_INVALID_PARAM;
+ 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_NOMEM;
+ 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 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_PARAM;
+ }
+
+ 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", dos_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",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ werr = WERR_OK;
+
+done:
+ TALLOC_FREE(tmp_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;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ char *subkey_name;
+ NTTIME modtime;
+ uint32_t count;
+ char *valname = NULL;
+ struct registry_value *valvalue = NULL;
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("Usage: net registry enumerate <path>\n");
+ d_printf("Example: net registry enumerate "
+ "'HKLM\\Software\\Samba'\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", dos_errstr(werr));
+ goto done;
+ }
+
+ 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;
+ }
+
+ 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;
+ 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("Usage: net registry createkey <path>\n");
+ d_printf("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", dos_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",
+ dos_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(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ char *subkeyname;
+ struct registry_key *hivekey = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ int ret = -1;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("Usage: net registry deletekey <path>\n");
+ d_printf("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 failed: %s\n", dos_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_deletekey(hivekey, subkeyname);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "reg_deletekey failed: %s\n",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+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, "usage: net rpc 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", dos_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",
+ dos_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_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, "usage: net rpc 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")) {
+ value.type = REG_DWORD;
+ value.v.dword = strtoul(argv[3], NULL, 10);
+ } else if (strequal(argv[2], "sz")) {
+ value.type = REG_SZ;
+ value.v.sz.len = strlen(argv[3])+1;
+ value.v.sz.str = CONST_DISCARD(char *, argv[3]);
+ } 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", dos_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",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ 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, "usage: net rpc 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", dos_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_deletevalue(key, argv[1]);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "reg_deletekey failed: %s\n",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ return ret;
+}
+
+static int net_registry_getsd(struct net_context *c, int argc,
+ const char **argv)
+{
+ WERROR werr;
+ int ret = -1;
+ struct registry_key *key = NULL;
+ struct security_descriptor *secdesc = NULL;
+ TALLOC_CTX *ctx = talloc_stackframe();
+ uint32_t access_mask = REG_KEY_READ |
+ SEC_RIGHT_MAXIMUM_ALLOWED |
+ SEC_RIGHT_SYSTEM_SECURITY;
+
+ /*
+ * net_rpc_regsitry uses SEC_RIGHT_SYSTEM_SECURITY, but access
+ * is denied with these perms right now...
+ */
+ access_mask = REG_KEY_READ;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("Usage: net registry getsd <path>\n");
+ d_printf("Example: net registry getsd "
+ "'HKLM\\Software\\Samba'\n");
+ goto done;
+ }
+ if (strlen(argv[0]) == 0) {
+ d_fprintf(stderr, "error: zero length key name given\n");
+ goto done;
+ }
+
+ werr = open_key(ctx, argv[0], access_mask, &key);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "open_key failed: %s\n", dos_errstr(werr));
+ goto done;
+ }
+
+ werr = reg_getkeysecurity(ctx, key, &secdesc);
+ if (!W_ERROR_IS_OK(werr)) {
+ d_fprintf(stderr, "reg_getkeysecurity failed: %s\n",
+ dos_errstr(werr));
+ goto done;
+ }
+
+ display_sec_desc(secdesc);
+
+ ret = 0;
+
+done:
+ TALLOC_FREE(ctx);
+ 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,
+ "Enumerate registry keys and values",
+ "net registry enumerate\n"
+ " Enumerate registry keys and values"
+ },
+ {
+ "createkey",
+ net_registry_createkey,
+ NET_TRANSPORT_LOCAL,
+ "Create a new registry key",
+ "net registry createkey\n"
+ " Create a new registry key"
+ },
+ {
+ "deletekey",
+ net_registry_deletekey,
+ NET_TRANSPORT_LOCAL,
+ "Delete a registry key",
+ "net registry deletekey\n"
+ " Delete a registry key"
+ },
+ {
+ "getvalue",
+ net_registry_getvalue,
+ NET_TRANSPORT_LOCAL,
+ "Print a registry value",
+ "net registry getvalue\n"
+ " Print a registry value"
+ },
+ {
+ "getvalueraw",
+ net_registry_getvalueraw,
+ NET_TRANSPORT_LOCAL,
+ "Print a registry value (raw format)",
+ "net registry getvalueraw\n"
+ " Print a registry value (raw format)"
+ },
+ {
+ "setvalue",
+ net_registry_setvalue,
+ NET_TRANSPORT_LOCAL,
+ "Set a new registry value",
+ "net registry setvalue\n"
+ " Set a new registry value"
+ },
+ {
+ "deletevalue",
+ net_registry_deletevalue,
+ NET_TRANSPORT_LOCAL,
+ "Delete a registry value",
+ "net registry deletevalue\n"
+ " Delete a registry value"
+ },
+ {
+ "getsd",
+ net_registry_getsd,
+ NET_TRANSPORT_LOCAL,
+ "Get security descriptor",
+ "net registry getsd\n"
+ " Get security descriptor"
+ },
+ { NULL, NULL, 0, NULL, NULL }
+ };
+
+ 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_util.c b/source3/utils/net_registry_util.c
new file mode 100644
index 0000000000..278377867a
--- /dev/null
+++ b/source3/utils/net_registry_util.c
@@ -0,0 +1,143 @@
+/*
+ * 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 "utils/net_registry_util.h"
+
+void print_registry_key(const char *keyname, NTTIME *modtime)
+{
+ d_printf("Keyname = %s\n", keyname);
+ d_printf("Modtime = %s\n",
+ modtime
+ ? http_timestring(nt_time_to_unix(*modtime))
+ : "None");
+ d_printf("\n");
+}
+
+void print_registry_value(const struct registry_value *valvalue, bool raw)
+{
+ if (!raw) {
+ d_printf("Type = %s\n",
+ reg_type_lookup(valvalue->type));
+ }
+ switch(valvalue->type) {
+ case REG_DWORD:
+ if (!raw) {
+ d_printf("Value = ");
+ }
+ d_printf("%d\n", valvalue->v.dword);
+ break;
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ if (!raw) {
+ d_printf("Value = \"");
+ }
+ d_printf("%s", valvalue->v.sz.str);
+ if (!raw) {
+ d_printf("\"");
+ }
+ d_printf("\n");
+ break;
+ case REG_MULTI_SZ: {
+ uint32 j;
+ for (j = 0; j < valvalue->v.multi_sz.num_strings; j++) {
+ if (!raw) {
+ d_printf("Value[%3.3d] = \"", j);
+ }
+ d_printf("%s", valvalue->v.multi_sz.strings[j]);
+ if (!raw) {
+ d_printf("\"");
+ }
+ d_printf("\n");
+ }
+ break;
+ }
+ case REG_BINARY:
+ if (!raw) {
+ d_printf("Value = ");
+ }
+ d_printf("%d bytes\n", (int)valvalue->v.binary.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:
+ * - 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_PARAM;
+ }
+
+ if (strlen(path) == 0) {
+ return WERR_INVALID_PARAM;
+ }
+
+ *hivename = talloc_string_sub(ctx, path, "/", "\\");
+ if (*hivename == NULL) {
+ return WERR_NOMEM;
+ }
+
+ /* 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_NOMEM;
+ }
+
+ return WERR_OK;
+}
diff --git a/source3/utils/net_registry_util.h b/source3/utils/net_registry_util.h
new file mode 100644
index 0000000000..61fd834a3c
--- /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 0000000000..a849ec4c10
--- /dev/null
+++ b/source3/utils/net_rpc.c
@@ -0,0 +1,7209 @@
+/*
+ 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"
+
+static int net_mode_share;
+static bool sync_files(struct copy_clistate *cp_clistate, const char *mask);
+
+extern const char *share_type[];
+
+/**
+ * @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, execpt that the commands should change
+ * less often, and the fucntionality 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,
+ DOM_SID **domain_sid,
+ const char **domain_name)
+{
+ struct rpc_pipe_client *lsa_pipe;
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ union lsa_PolicyInformation *info = NULL;
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not initialise lsa pipe\n");
+ return result;
+ }
+
+ result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "open_policy failed: %s\n",
+ nt_errstr(result));
+ return result;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(lsa_pipe, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "lsaquery failed: %s\n",
+ nt_errstr(result));
+ return result;
+ }
+
+ *domain_name = info->account_domain.name.string;
+ *domain_sid = info->account_domain.sid;
+
+ rpccli_lsa_Close(lsa_pipe, mem_ctx, &pol);
+ 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_syntax_id *interface,
+ 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;
+ DOM_SID *domain_sid;
+ const char *domain_name;
+
+ /* 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"));
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ nt_status = net_get_remote_domain_sid(cli, mem_ctx, &domain_sid,
+ &domain_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(cli);
+ return -1;
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (lp_client_schannel()
+ && (ndr_syntax_id_equal(interface,
+ &ndr_table_netlogon.syntax_id))) {
+ /* Always try and create an schannel netlogon pipe. */
+ nt_status = cli_rpc_pipe_open_schannel(
+ cli, interface,
+ PIPE_AUTH_LEVEL_PRIVACY, domain_name,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise schannel netlogon pipe. Error was %s\n",
+ nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ return -1;
+ }
+ } else {
+ if (conn_flags & NET_FLAGS_SEAL) {
+ nt_status = cli_rpc_pipe_open_ntlmssp(
+ cli, interface,
+ PIPE_AUTH_LEVEL_PRIVACY,
+ lp_workgroup(), c->opt_user_name,
+ c->opt_password, &pipe_hnd);
+ } else {
+ nt_status = cli_rpc_pipe_open_noauth(
+ cli, interface,
+ &pipe_hnd);
+ }
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Could not initialise pipe %s. Error was %s\n",
+ cli_get_pipe_name_from_iface(
+ debug_ctx(), cli, interface),
+ nt_errstr(nt_status) ));
+ cli_shutdown(cli);
+ return -1;
+ }
+ }
+ }
+
+ 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 {
+ DEBUG(5, ("rpc command function succedded\n"));
+ }
+
+ if (!(conn_flags & NET_FLAGS_NO_PIPE)) {
+ if (pipe_hnd) {
+ TALLOC_FREE(pipe_hnd);
+ }
+ }
+
+ /* close the connection only if it was opened here */
+ if (!cli_arg) {
+ cli_shutdown(cli);
+ }
+
+ talloc_destroy(mem_ctx);
+ return (!NT_STATUS_IS_OK(nt_status));
+}
+
+/**
+ * Force a change of the trust acccount 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 DOM_SID *domain_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 trust_pw_find_change_and_store_it(pipe_hnd, mem_ctx, c->opt_target_workgroup);
+}
+
+/**
+ * Force a change of the trust acccount 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)
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc changetrustpw\n"
+ " Change the machine trust password\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id,
+ NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_changetrustpw_internals,
+ argc, argv);
+}
+
+/**
+ * Join a domain, the old way.
+ *
+ * This uses 'machinename' as the inital password, and changes it.
+ *
+ * The password should be created with 'server manager' or equiv first.
+ *
+ * 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_oldjoin_internals(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+
+ fstring trust_passwd;
+ unsigned char orig_trust_passwd_hash[16];
+ NTSTATUS result;
+ uint32 sec_channel_type;
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("rpc_oldjoin_internals: netlogon pipe open to machine %s failed. "
+ "error was %s\n",
+ cli->desthost,
+ nt_errstr(result) ));
+ return result;
+ }
+
+ /*
+ check what type of join - if the user want's to join as
+ a BDC, the server must agree that we are a BDC.
+ */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ fstrcpy(trust_passwd, global_myname());
+ strlower_m(trust_passwd);
+
+ /*
+ * Machine names can be 15 characters, but the max length on
+ * a password is 14. --jerry
+ */
+
+ trust_passwd[14] = '\0';
+
+ E_md4hash(trust_passwd, orig_trust_passwd_hash);
+
+ result = trust_pw_change_and_store_it(pipe_hnd, mem_ctx, c->opt_target_workgroup,
+ orig_trust_passwd_hash,
+ sec_channel_type);
+
+ if (NT_STATUS_IS_OK(result))
+ printf("Joined domain %s.\n", c->opt_target_workgroup);
+
+
+ if (!secrets_store_domain_sid(c->opt_target_workgroup, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", c->opt_target_workgroup));
+ result = NT_STATUS_UNSUCCESSFUL;
+ }
+
+ return result;
+}
+
+/**
+ * Join a domain, the old way.
+ *
+ * @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_perform_oldjoin(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id,
+ NET_FLAGS_NO_PIPE | NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ rpc_oldjoin_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".
+ *
+ * @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)
+{
+ int rc = -1;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc oldjoin\n"
+ " Join a domain the old way\n");
+ return 0;
+ }
+
+ rc = net_rpc_perform_oldjoin(c, argc, argv);
+
+ if (rc) {
+ d_fprintf(stderr, "Failed to join domain\n");
+ }
+
+ return rc;
+}
+
+/**
+ * '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)
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "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;
+ }
+
+ if (strlen(global_myname()) > 15) {
+ d_printf("Our netbios name can be at most 15 chars long, "
+ "\"%s\" is %u chars long\n",
+ global_myname(), (unsigned int)strlen(global_myname()));
+ return -1;
+ }
+
+ if ((net_rpc_perform_oldjoin(c, argc, argv) == 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union samr_DomainInfo *info = NULL;
+ fstring sid_str;
+
+ sid_to_fstring(sid_str, domain_sid);
+
+ /* Get sam policy handle */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not connect to SAM: %s\n", nt_errstr(result));
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not open domain: %s\n", nt_errstr(result));
+ goto done;
+ }
+
+ result = rpccli_samr_QueryDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 2,
+ &info);
+ if (NT_STATUS_IS_OK(result)) {
+ d_printf("Domain Name: %s\n", info->info2.domain_name.string);
+ d_printf("Domain SID: %s\n", sid_str);
+ d_printf("Sequence number: %llu\n",
+ (unsigned long long)info->info2.sequence_num);
+ d_printf("Num users: %u\n", info->info2.num_users);
+ d_printf("Num domain groups: %u\n", info->info2.num_groups);
+ d_printf("Num local groups: %u\n", info->info2.num_aliases);
+ }
+
+ done:
+ return result;
+}
+
+/**
+ * '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("Usage:\n"
+ "net rpc info\n"
+ " Display information about the domain\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_samr.syntax_id,
+ 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ fstring sid_str;
+
+ sid_to_fstring(sid_str, domain_sid);
+ d_printf("Storing SID %s for Domain %s in secrets.tdb\n",
+ 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)
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc getsid\n"
+ " Fetch domain SID into local secrets.tdb\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_samr.syntax_id,
+ NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC,
+ 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: %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;
+}
+
+/**
+ * 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;
+
+ if (argc < 1 || c->display_usage) {
+ rpc_user_usage(c, argc, argv);
+ return 0;
+ }
+
+ if (argv[1]) {
+ u1003.usri1003_password = argv[1];
+ } else {
+ asprintf(&prompt, "Enter new password for %s:", argv[0]);
+ u1003.usri1003_password = getpass(prompt);
+ SAFE_FREE(prompt);
+ }
+
+ 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: %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;
+ int 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 **)&u0,
+ (uint32_t)-1,
+ &entries_read,
+ &total_entries);
+ if (status != 0) {
+ d_fprintf(stderr, "Failed to get groups for '%s' with: %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;
+
+ 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,
+ "Add specified user",
+ "net rpc user add\n"
+ " Add specified user"
+ },
+ {
+ "info",
+ rpc_user_info,
+ NET_TRANSPORT_RPC,
+ "List domain groups of user",
+ "net rpc user info\n"
+ " Lis domain groups of user"
+ },
+ {
+ "delete",
+ rpc_user_delete,
+ NET_TRANSPORT_RPC,
+ "Remove specified user",
+ "net rpc user delete\n"
+ " Remove specified user"
+ },
+ {
+ "password",
+ rpc_user_password,
+ NET_TRANSPORT_RPC,
+ "Change user password",
+ "net rpc user password\n"
+ " Change user password"
+ },
+ {
+ "rename",
+ rpc_user_rename,
+ NET_TRANSPORT_RPC,
+ "Rename specified user",
+ "net rpc user rename\n"
+ " Rename specified user"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_init(&c->netapi_ctx);
+ if (status != 0) {
+ return -1;
+ }
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("Usage:\n");
+ d_printf("net rpc user\n"
+ " List all users\n");
+ 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,
+ POLICY_HND *user_hnd,
+ int argc, const char **argv))
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ DOM_SID sid;
+ uint32 rid;
+ enum lsa_SidType type;
+
+ if (argc == 0) {
+ d_fprintf(stderr, "usage: %s <username>\n", ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ ZERO_STRUCT(connect_pol);
+ ZERO_STRUCT(domain_pol);
+ ZERO_STRUCT(user_pol);
+
+ result = net_rpc_lookup_name(c, mem_ctx, rpc_pipe_np_smb_conn(pipe_hnd),
+ argv[0], NULL, NULL, &sid, &type);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not lookup %s: %s\n", argv[0],
+ nt_errstr(result));
+ goto done;
+ }
+
+ if (type != SID_NAME_USER) {
+ d_fprintf(stderr, "%s is a %s, not a user\n", argv[0],
+ sid_type_lookup(type));
+ result = 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]);
+ result = NT_STATUS_NO_SUCH_USER;
+ goto done;
+ }
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ ctx->domain_sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &user_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = fn(c, mem_ctx, ctx, pipe_hnd, &user_pol, argc-1, argv+1);
+
+ done:
+ if (is_valid_policy_hnd(&user_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ }
+ if (is_valid_policy_hnd(&domain_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+ }
+ if (is_valid_policy_hnd(&connect_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ }
+ return result;
+}
+
+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,
+ POLICY_HND *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ union samr_UserInfo *info = NULL;
+
+ if (argc != 0) {
+ d_fprintf(stderr, "usage: %s show <username>\n", ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ result = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+ user_hnd,
+ 21,
+ &info);
+ 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,
+ POLICY_HND *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *username;
+ const char *oldval = "";
+ union samr_UserInfo *info = NULL;
+
+ if (argc > 1) {
+ d_fprintf(stderr, "usage: %s <username> [new value|NULL]\n",
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ result = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+ user_hnd,
+ 21,
+ &info);
+ 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);
+
+ result = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+ user_hnd,
+ 21,
+ info);
+
+ d_printf("Set %s's %s from [%s] to [%s]\n", username,
+ ctx->thiscmd, oldval, argv[0]);
+
+ done:
+
+ return result;
+}
+
+#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,
+ POLICY_HND *user_hnd,
+ int argc, const char **argv)
+{
+ NTSTATUS result;
+ const char *username;
+ const char *oldval = "unknown";
+ uint32 oldflags, newflags;
+ bool newval;
+ union samr_UserInfo *info = NULL;
+
+ if ((argc > 1) ||
+ ((argc == 1) && !strequal(argv[0], "yes") &&
+ !strequal(argv[0], "no"))) {
+ d_fprintf(stderr, "usage: %s <username> [yes|no]\n",
+ ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ newval = strequal(argv[0], "yes");
+
+ result = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+ user_hnd,
+ 21,
+ &info);
+ 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;
+
+ result = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+ user_hnd,
+ 21,
+ info);
+
+ 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 result;
+}
+
+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.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's full name" },
+
+ { "homedir", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's home directory" },
+
+ { "homedrive", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's home drive" },
+
+ { "logonscript", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's logon script" },
+
+ { "profilepath", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's profile path" },
+
+ { "description", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_str_edit,
+ "Show/Set a user's description" },
+
+ { "disabled", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_flag_edit,
+ "Show/Set whether a user is disabled" },
+
+ { "autolock", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_flag_edit,
+ "Show/Set whether a user locked out" },
+
+ { "pwnotreq", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_flag_edit,
+ "Show/Set whether a user does not need a password" },
+
+ { "pwnoexp", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_flag_edit,
+ "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.syntax_id, rpc_sh_user_list,
+ "List available users" },
+
+ { "info", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_info,
+ "List the domain groups a user is member of" },
+
+ { "show", NULL, &ndr_table_samr.syntax_id, rpc_sh_user_show,
+ "Show info about a user" },
+
+ { "edit", net_rpc_user_edit_cmds, 0, NULL,
+ "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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, group_pol, user_pol;
+ bool group_is_primary = false;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32_t group_rid;
+ struct samr_RidTypeArray *rids = NULL;
+ /* char **names; */
+ int i;
+ /* DOM_GID *user_gids; */
+
+ 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? */
+ }
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Request samr_Connect2 failed\n");
+ goto done;
+ }
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Request open_domain failed\n");
+ goto done;
+ }
+
+ init_lsa_String(&lsa_acct_name, argv[0]);
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &group_rids,
+ &name_types);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Lookup of '%s' failed\n",argv[0]);
+ goto done;
+ }
+
+ switch (name_types.ids[0])
+ {
+ case SID_NAME_DOM_GRP:
+ result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rids.ids[0],
+ &group_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Request open_group failed");
+ goto done;
+ }
+
+ group_rid = group_rids.ids[0];
+
+ result = rpccli_samr_QueryGroupMember(pipe_hnd, mem_ctx,
+ &group_pol,
+ &rids);
+
+ if (!NT_STATUS_IS_OK(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++)
+ {
+ result = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rids->rids[i],
+ &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Unable to open group member %d\n",
+ rids->rids[i]);
+ goto done;
+ }
+
+ result = rpccli_samr_QueryUserInfo(pipe_hnd, mem_ctx,
+ &user_pol,
+ 21,
+ &info);
+
+ if (!NT_STATUS_IS_OK(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;
+ }
+
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ }
+
+ if (group_is_primary) {
+ d_fprintf(stderr, "Unable to delete group because some "
+ "of it's members have it as primary group\n");
+ result = 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]);
+ result = rpccli_samr_DeleteGroupMember(pipe_hnd, mem_ctx,
+ &group_pol,
+ rids->rids[i]);
+
+ if (NT_STATUS_IS_OK(result)) {
+ if (c->opt_verbose)
+ d_printf("ok\n");
+ } else {
+ if (c->opt_verbose)
+ d_printf("failed\n");
+ goto done;
+ }
+ }
+
+ result = rpccli_samr_DeleteDomainGroup(pipe_hnd, mem_ctx,
+ &group_pol);
+
+ break;
+ /* removing a local group is easier... */
+ case SID_NAME_ALIAS:
+ result = rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rids.ids[0],
+ &group_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Request open_alias failed\n");
+ goto done;
+ }
+
+ result = rpccli_samr_DeleteDomAlias(pipe_hnd, mem_ctx,
+ &group_pol);
+ 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]));
+ result = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ if (NT_STATUS_IS_OK(result)) {
+ 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(result));
+ }
+
+ done:
+ return result;
+
+}
+
+static int rpc_group_delete(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr.syntax_id, 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: %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: %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,
+ DOM_SID *sid,
+ enum lsa_SidType *type)
+{
+ DOM_SID *sids = NULL;
+ enum lsa_SidType *types = NULL;
+ struct rpc_pipe_client *pipe_hnd;
+ POLICY_HND lsa_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, false,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &lsa_pol, 1,
+ &name, NULL, 1, &sids, &types);
+
+ if (NT_STATUS_IS_OK(result)) {
+ sid_copy(sid, &sids[0]);
+ *type = types[0];
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
+
+ done:
+ if (pipe_hnd) {
+ TALLOC_FREE(pipe_hnd);
+ }
+
+ if (!NT_STATUS_IS_OK(result) && (StrnCaseCmp(name, "S-", 2) == 0)) {
+
+ /* Try as S-1-5-whatever */
+
+ DOM_SID tmp_sid;
+
+ if (string_to_sid(&tmp_sid, name)) {
+ sid_copy(sid, &tmp_sid);
+ *type = SID_NAME_UNKNOWN;
+ result = NT_STATUS_OK;
+ }
+ }
+
+ return result;
+}
+
+static NTSTATUS rpc_add_groupmem(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *group_sid,
+ const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* Get sam policy handle */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ init_lsa_String(&lsa_acct_name, member);
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid,
+ &group_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_AddGroupMember(pipe_hnd, mem_ctx,
+ &group_pol,
+ rids.ids[0],
+ 0x0005); /* unknown flags */
+
+ done:
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS rpc_add_aliasmem(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid,
+ const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum lsa_SidType member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ result = get_sid_from_name(rpc_pipe_np_smb_conn(pipe_hnd), 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 */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid,
+ &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ result = rpccli_samr_AddAliasMember(pipe_hnd, mem_ctx,
+ &alias_pol,
+ &member_sid);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ done:
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS rpc_group_addmem_internals(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ DOM_SID group_sid;
+ enum lsa_SidType group_type;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("Usage:\n"
+ "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, 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.syntax_id, 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 DOM_SID *group_sid,
+ const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 group_rid;
+ POLICY_HND group_pol;
+
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, group_sid);
+
+ if (!sid_split_rid(&sid, &group_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ /* Get sam policy handle */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ init_lsa_String(&lsa_acct_name, member);
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not lookup up group member %s\n", member);
+ goto done;
+ }
+
+ result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ group_rid,
+ &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = rpccli_samr_DeleteGroupMember(pipe_hnd, mem_ctx,
+ &group_pol,
+ rids.ids[0]);
+
+ done:
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS rpc_del_aliasmem(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ const DOM_SID *alias_sid,
+ const char *member)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result;
+ uint32 alias_rid;
+ POLICY_HND alias_pol;
+
+ DOM_SID member_sid;
+ enum lsa_SidType member_type;
+
+ DOM_SID sid;
+
+ sid_copy(&sid, alias_sid);
+
+ if (!sid_split_rid(&sid, &alias_rid))
+ return NT_STATUS_UNSUCCESSFUL;
+
+ result = get_sid_from_name(rpc_pipe_np_smb_conn(pipe_hnd), 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 */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ alias_rid,
+ &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = rpccli_samr_DeleteAliasMember(pipe_hnd, mem_ctx,
+ &alias_pol,
+ &member_sid);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ done:
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ return result;
+}
+
+static NTSTATUS rpc_group_delmem_internals(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ DOM_SID group_sid;
+ enum lsa_SidType group_type;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("Usage:\n"
+ "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, 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.syntax_id, 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ uint32 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;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "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 */
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(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;
+
+ get_query_dispinfo_params(
+ loop_count, &max_entries, &max_size);
+
+ result = rpccli_samr_QueryDisplayInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 3,
+ start_idx,
+ max_entries,
+ max_size,
+ &total_size,
+ &returned_size,
+ &info);
+ 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;
+
+ result = rpccli_samr_EnumDomainAliases(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ 0xffff,
+ &num_entries);
+ 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) {
+
+ POLICY_HND alias_pol;
+ union samr_AliasInfo *info = NULL;
+
+ if ((NT_STATUS_IS_OK(rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups->entries[i].idx,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(rpccli_samr_QueryAliasInfo(pipe_hnd, mem_ctx,
+ &alias_pol,
+ 3,
+ &info))) &&
+ (NT_STATUS_IS_OK(rpccli_samr_Close(pipe_hnd, mem_ctx,
+ &alias_pol)))) {
+ 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));
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+ /* Get builtin policy handle */
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, &global_sid_Builtin),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ /* query builtin aliases */
+ start_idx = 0;
+ do {
+ if (!builtin) break;
+
+ result = rpccli_samr_EnumDomainAliases(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ max_entries,
+ &num_entries);
+ 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) {
+
+ POLICY_HND alias_pol;
+ union samr_AliasInfo *info = NULL;
+
+ if ((NT_STATUS_IS_OK(rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 0x8,
+ groups->entries[i].idx,
+ &alias_pol))) &&
+ (NT_STATUS_IS_OK(rpccli_samr_QueryAliasInfo(pipe_hnd, mem_ctx,
+ &alias_pol,
+ 3,
+ &info))) &&
+ (NT_STATUS_IS_OK(rpccli_samr_Close(pipe_hnd, mem_ctx,
+ &alias_pol)))) {
+ 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));
+
+ done:
+ return result;
+}
+
+static int rpc_group_list(struct net_context *c, int argc, const char **argv)
+{
+ return run_rpc_command(c, NULL, &ndr_table_samr.syntax_id, 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 DOM_SID *domain_sid,
+ POLICY_HND *domain_pol,
+ uint32 rid)
+{
+ NTSTATUS result;
+ POLICY_HND group_pol;
+ uint32 num_members, *group_rids;
+ int i;
+ struct samr_RidTypeArray *rids = NULL;
+ struct lsa_Strings names;
+ struct samr_Ids types;
+
+ fstring sid_str;
+ sid_to_fstring(sid_str, domain_sid);
+
+ result = rpccli_samr_OpenGroup(pipe_hnd, mem_ctx,
+ domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &group_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = rpccli_samr_QueryGroupMember(pipe_hnd, mem_ctx,
+ &group_pol,
+ &rids);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ num_members = rids->count;
+ group_rids = rids->rids;
+
+ while (num_members > 0) {
+ int this_time = 512;
+
+ if (num_members < this_time)
+ this_time = num_members;
+
+ result = rpccli_samr_LookupRids(pipe_hnd, mem_ctx,
+ domain_pol,
+ this_time,
+ group_rids,
+ &names,
+ &types);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* 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) {
+ printf("%s-%d %s\\%s %d\n", sid_str,
+ group_rids[i], 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,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *domain_pol,
+ uint32 rid)
+{
+ NTSTATUS result;
+ struct rpc_pipe_client *lsa_pipe;
+ POLICY_HND alias_pol, lsa_pol;
+ uint32 num_members;
+ DOM_SID *alias_sids;
+ char **domains;
+ char **names;
+ enum lsa_SidType *types;
+ int i;
+ struct lsa_SidArray sid_array;
+
+ result = rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ rid,
+ &alias_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = rpccli_samr_GetMembersInAlias(pipe_hnd, mem_ctx,
+ &alias_pol,
+ &sid_array);
+
+ 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(rpc_pipe_np_smb_conn(pipe_hnd),
+ &ndr_table_lsarpc.syntax_id,
+ &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_RIGHTS_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, 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++) {
+ fstring sid_str;
+ sid_to_fstring(sid_str, &alias_sids[i]);
+
+ if (c->opt_long_list_entries) {
+ printf("%s %s\\%s %d\n", sid_str,
+ 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);
+ }
+ }
+
+ TALLOC_FREE(lsa_pipe);
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rpc_group_members_internals(struct net_context *c,
+ const DOM_SID *domain_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;
+ POLICY_HND connect_pol, domain_pol;
+ struct samr_Ids rids, rid_types;
+ struct lsa_String lsa_acct_name;
+
+ /* Get sam policy handle */
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ /* Get domain policy handle */
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ init_lsa_String(&lsa_acct_name, argv[0]); /* sure? */
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+
+ /* Ok, did not find it in the global sam, try with builtin */
+
+ DOM_SID sid_Builtin;
+
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+
+ sid_copy(&sid_Builtin, &global_sid_Builtin);
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ &sid_Builtin,
+ &domain_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Couldn't find group %s\n", argv[0]);
+ return result;
+ }
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &rids,
+ &rid_types);
+
+ 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 result;
+ }
+
+ 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, 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.syntax_id, 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: '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,
+ "Create specified group",
+ "net rpc group add\n"
+ " Create specified group"
+ },
+ {
+ "delete",
+ rpc_group_delete,
+ NET_TRANSPORT_RPC,
+ "Delete specified group",
+ "net rpc group delete\n"
+ " Delete specified group"
+ },
+ {
+ "addmem",
+ rpc_group_addmem,
+ NET_TRANSPORT_RPC,
+ "Add member to group",
+ "net rpc group addmem\n"
+ " Add member to group"
+ },
+ {
+ "delmem",
+ rpc_group_delmem,
+ NET_TRANSPORT_RPC,
+ "Remove member from group",
+ "net rpc group delmem\n"
+ " Remove member from group"
+ },
+ {
+ "list",
+ rpc_group_list,
+ NET_TRANSPORT_RPC,
+ "List groups",
+ "net rpc group list\n"
+ " List groups"
+ },
+ {
+ "members",
+ rpc_group_members,
+ NET_TRANSPORT_RPC,
+ "List group members",
+ "net rpc group members\n"
+ " List group members"
+ },
+ {
+ "rename",
+ rpc_group_rename,
+ NET_TRANSPORT_RPC,
+ "Rename group",
+ "net rpc group rename\n"
+ " Rename group"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_init(&c->netapi_ctx);
+ if (status != 0) {
+ return -1;
+ }
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ 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.syntax_id, 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 type = STYPE_DISKTREE; /* only allow disk shares to be added */
+ uint32 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,
+ share_type[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 level,
+ int argc,
+ const char **argv,
+ struct srvsvc_NetShareInfoCtr *info_ctr)
+{
+ WERROR result;
+ NTSTATUS status;
+ union srvsvc_NetShareInfo info;
+
+ /* 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 = rpccli_srvsvc_NetShareEnumAll(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ info_ctr,
+ preferred_len,
+ &total_entries,
+ &resume_handle,
+ &result);
+ return result;
+ }
+
+ /* request just one share */
+ status = rpccli_srvsvc_NetShareGetInfo(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ argv[0],
+ level,
+ &info,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !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_P(mem_ctx, struct srvsvc_NetShareCtr1);
+ W_ERROR_HAVE_NO_MEMORY(ctr1);
+
+ ctr1->count = 1;
+ ctr1->array = info.info1;
+
+ info_ctr->ctr.ctr1 = ctr1;
+ }
+ case 2:
+ {
+ struct srvsvc_NetShareCtr2 *ctr2;
+
+ ctr2 = TALLOC_ZERO_P(mem_ctx, struct srvsvc_NetShareCtr2);
+ W_ERROR_HAVE_NO_MEMORY(ctr2);
+
+ ctr2->count = 1;
+ ctr2->array = info.info2;
+
+ info_ctr->ctr.ctr2 = ctr2;
+ }
+ case 502:
+ {
+ struct srvsvc_NetShareCtr502 *ctr502;
+
+ ctr502 = TALLOC_ZERO_P(mem_ctx, struct srvsvc_NetShareCtr502);
+ W_ERROR_HAVE_NO_MEMORY(ctr502);
+
+ ctr502->count = 1;
+ ctr502->array = info.info502;
+
+ info_ctr->ctr.ctr502 = ctr502;
+ }
+ } /* 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("Usage\n"
+ "net rpc share list\n"
+ " List shares on remote server\n");
+ return 0;
+ }
+
+ status = NetShareEnum(c->opt_host,
+ level,
+ (uint8_t **)&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)
+{
+ if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+ d_printf("skipping [%s]: not a file share.\n", netname);
+ return false;
+ }
+
+ if (!cli_tdis(cli))
+ return false;
+
+ return true;
+}
+
+static bool check_share_sanity(struct net_context *c, struct cli_state *cli,
+ const char *netname, uint32 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 DOM_SID *domain_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 i;
+ struct rpc_pipe_client *srvsvc_pipe = NULL;
+ struct cli_state *cli_dst = NULL;
+ uint32 level = 502; /* includes secdesc */
+ uint32_t parm_error = 0;
+
+ 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.syntax_id);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+
+ 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 = rpccli_srvsvc_NetShareAdd(srvsvc_pipe, mem_ctx,
+ srvsvc_pipe->desthost,
+ 502,
+ &info,
+ &parm_error,
+ &result);
+
+ if (W_ERROR_V(result) == W_ERROR_V(WERR_ALREADY_EXISTS)) {
+ printf(" [%s] does already exist\n",
+ info502.name);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status) || !W_ERROR_IS_OK(result)) {
+ printf("cannot add share: %s\n", dos_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("Usage:\n"
+ "net rpc share migrate shares\n"
+ " Migrate shares to local server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 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 void copy_fn(const char *mnt, 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;
+
+ DEBUG(3,("got mask: %s, name: %s\n", mask, f->name));
+
+ /* DIRECTORY */
+ if (f->mode & aDIR) {
+
+ 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;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ printf("could not handle dir %s: %s\n",
+ dir, nt_errstr(nt_status));
+
+ /* search below that directory */
+ fstrcpy(new_mask, dir);
+ fstrcat(new_mask, "\\*");
+
+ old_dir = local_state->cwd;
+ local_state->cwd = dir;
+ if (!sync_files(local_state, new_mask))
+ printf("could not handle files\n");
+ local_state->cwd = old_dir;
+
+ return;
+ }
+
+
+ /* 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;
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status))
+ printf("could not handle file %s: %s\n",
+ filename, nt_errstr(nt_status));
+
+}
+
+/**
+ * sync files, can be called recursivly 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 bool sync_files(struct copy_clistate *cp_clistate, const char *mask)
+{
+ struct cli_state *targetcli;
+ char *targetpath = NULL;
+
+ DEBUG(3,("calling cli_list with mask: %s\n", mask));
+
+ if ( !cli_resolve_path(talloc_tos(), "", cp_clistate->cli_share_src,
+ mask, &targetcli, &targetpath ) ) {
+ d_fprintf(stderr, "cli_resolve_path %s failed with error: %s\n",
+ mask, cli_errstr(cp_clistate->cli_share_src));
+ return false;
+ }
+
+ if (cli_list(targetcli, targetpath, cp_clistate->attribute, copy_fn, cp_clistate) == -1) {
+ d_fprintf(stderr, "listing %s failed with error: %s\n",
+ mask, cli_errstr(targetcli));
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * 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 DOM_SID *domain_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 i;
+ uint32 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 = aSYSTEM | aHIDDEN | aDIR;
+ cp_clistate.c = c;
+
+ /* open share source */
+ nt_status = connect_to_service(c, &cp_clistate.cli_share_src,
+ &cli->dest_ss, cli->desthost,
+ 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;
+ }
+
+ if (!sync_files(&cp_clistate, mask)) {
+ d_fprintf(stderr, "could not handle files for share: %s\n", info502.name);
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ 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("Usage:\n"
+ "net share migrate files\n"
+ " Migrate files to local server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 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 DOM_SID *domain_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 i;
+ struct rpc_pipe_client *srvsvc_pipe = NULL;
+ struct cli_state *cli_dst = NULL;
+ uint32 level = 502; /* includes secdesc */
+ uint32_t parm_error = 0;
+
+ 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.syntax_id);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+
+ 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 = rpccli_srvsvc_NetShareSetInfo(srvsvc_pipe, mem_ctx,
+ srvsvc_pipe->desthost,
+ info502.name,
+ level,
+ &info,
+ &parm_error,
+ &result);
+ if (!NT_STATUS_IS_OK(nt_status) || !W_ERROR_IS_OK(result)) {
+ printf("cannot set share-acl: %s\n", dos_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("Usage:\n"
+ "net rpc share migrate security\n"
+ " Migrate share-acls to local server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 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("Usage:\n"
+ "net rpc share migrate all\n"
+ " Migrates shares including all share settings\n");
+ 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.syntax_id, 0,
+ rpc_share_migrate_shares_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 0,
+ rpc_share_migrate_files_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 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,
+ "Migrate shares from remote to local server",
+ "net rpc share migrate all\n"
+ " Migrate shares from remote to local server"
+ },
+ {
+ "files",
+ rpc_share_migrate_files,
+ NET_TRANSPORT_RPC,
+ "Migrate files from remote to local server",
+ "net rpc share migrate files\n"
+ " Migrate files from remote to local server"
+ },
+ {
+ "security",
+ rpc_share_migrate_security,
+ NET_TRANSPORT_RPC,
+ "Migrate share-ACLs from remote to local server",
+ "net rpc share migrate security\n"
+ " Migrate share-ACLs from remote to local server"
+ },
+ {
+ "shares",
+ rpc_share_migrate_shares,
+ NET_TRANSPORT_RPC,
+ "Migrate shares from remote to local server",
+ "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 {
+ DOM_SID sid;
+ uint32 num_members;
+ 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(TALLOC_CTX *mem_ctx, struct full_alias *alias)
+{
+ if (server_aliases == NULL)
+ server_aliases = SMB_MALLOC_ARRAY(struct full_alias, 100);
+
+ 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,
+ POLICY_HND *connect_pol,
+ const DOM_SID *domain_sid)
+{
+ uint32 start_idx, max_entries, num_entries, i;
+ struct samr_SamArray *groups = NULL;
+ NTSTATUS result;
+ POLICY_HND domain_pol;
+
+ /* Get domain policy handle */
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ start_idx = 0;
+ max_entries = 250;
+
+ do {
+ result = rpccli_samr_EnumDomainAliases(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &start_idx,
+ &groups,
+ max_entries,
+ &num_entries);
+ for (i = 0; i < num_entries; i++) {
+
+ POLICY_HND alias_pol;
+ struct full_alias alias;
+ struct lsa_SidArray sid_array;
+ int j;
+
+ result = rpccli_samr_OpenAlias(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ groups->entries[i].idx,
+ &alias_pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = rpccli_samr_GetMembersInAlias(pipe_hnd, mem_ctx,
+ &alias_pol,
+ &sid_array);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ alias.num_members = sid_array.num_sids;
+
+ result = rpccli_samr_Close(pipe_hnd, mem_ctx, &alias_pol);
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ alias.members = NULL;
+
+ if (alias.num_members > 0) {
+ alias.members = SMB_MALLOC_ARRAY(DOM_SID, alias.num_members);
+
+ for (j = 0; j < alias.num_members; j++)
+ sid_copy(&alias.members[j],
+ sid_array.sids[j].sid);
+ }
+
+ sid_copy(&alias.sid, domain_sid);
+ sid_append_rid(&alias.sid, groups->entries[i].idx);
+
+ push_alias(mem_ctx, &alias);
+ }
+ } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES));
+
+ result = NT_STATUS_OK;
+
+ done:
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+
+ return result;
+}
+
+/*
+ * Dump server_aliases as names for debugging purposes.
+ */
+
+static NTSTATUS rpc_aliaslist_dump(struct net_context *c,
+ const DOM_SID *domain_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 i;
+ NTSTATUS result;
+ POLICY_HND lsa_pol;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_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"));
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
+
+ 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 DOM_SID *domain_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;
+ POLICY_HND connect_pol;
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol,
+ &global_sid_Builtin);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ result = rpc_fetch_domain_aliases(pipe_hnd, mem_ctx, &connect_pol,
+ domain_sid);
+
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ done:
+ return result;
+}
+
+static void init_user_token(NT_USER_TOKEN *token, DOM_SID *user_sid)
+{
+ token->num_sids = 4;
+
+ if (!(token->user_sids = SMB_MALLOC_ARRAY(DOM_SID, 4))) {
+ d_fprintf(stderr, "malloc failed\n");
+ token->num_sids = 0;
+ return;
+ }
+
+ token->user_sids[0] = *user_sid;
+ sid_copy(&token->user_sids[1], &global_sid_World);
+ sid_copy(&token->user_sids[2], &global_sid_Network);
+ sid_copy(&token->user_sids[3], &global_sid_Authenticated_Users);
+}
+
+static void free_user_token(NT_USER_TOKEN *token)
+{
+ SAFE_FREE(token->user_sids);
+}
+
+static bool is_sid_in_token(NT_USER_TOKEN *token, DOM_SID *sid)
+{
+ int i;
+
+ for (i=0; i<token->num_sids; i++) {
+ if (sid_compare(sid, &token->user_sids[i]) == 0)
+ return true;
+ }
+ return false;
+}
+
+static void add_sid_to_token(NT_USER_TOKEN *token, DOM_SID *sid)
+{
+ if (is_sid_in_token(token, sid))
+ return;
+
+ token->user_sids = SMB_REALLOC_ARRAY(token->user_sids, DOM_SID, token->num_sids+1);
+ if (!token->user_sids) {
+ return;
+ }
+
+ sid_copy(&token->user_sids[token->num_sids], sid);
+
+ token->num_sids += 1;
+}
+
+struct user_token {
+ fstring name;
+ NT_USER_TOKEN token;
+};
+
+static void dump_user_token(struct user_token *token)
+{
+ int i;
+
+ d_printf("%s\n", token->name);
+
+ for (i=0; i<token->token.num_sids; i++) {
+ d_printf(" %s\n", sid_string_tos(&token->token.user_sids[i]));
+ }
+}
+
+static bool is_alias_member(DOM_SID *sid, struct full_alias *alias)
+{
+ int i;
+
+ for (i=0; i<alias->num_members; i++) {
+ if (sid_compare(sid, &alias->members[i]) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static void collect_sid_memberships(NT_USER_TOKEN *token, 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(NT_USER_TOKEN *token)
+{
+ int num_global_sids = token->num_sids;
+ int i;
+
+ for (i=0; i<num_global_sids; i++) {
+ collect_sid_memberships(token, token->user_sids[i]);
+ }
+}
+
+static bool get_user_sids(const char *domain, const char *user, NT_USER_TOKEN *token)
+{
+ wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+ enum wbcSidType type;
+ fstring full_name;
+ struct wbcDomainSid wsid;
+ char *sid_str = NULL;
+ 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;
+ }
+
+ wbc_status = wbcSidToString(&wsid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ return false;
+ }
+
+ if (type != SID_NAME_USER) {
+ wbcFreeMemory(sid_str);
+ DEBUG(1, ("%s is not a user\n", full_name));
+ return false;
+ }
+
+ string_to_sid(&user_sid, sid_str);
+ wbcFreeMemory(sid_str);
+ sid_str = NULL;
+
+ init_user_token(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];
+ DOM_SID sid;
+
+ wbc_status = wbcGidToSid(gid, &wsid);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ DEBUG(1, ("winbind could not find SID of gid %d: %s\n",
+ gid, wbcErrorString(wbc_status)));
+ wbcFreeMemory(groups);
+ return false;
+ }
+
+ wbc_status = wbcSidToString(&wsid, &sid_str);
+ if (!WBC_ERROR_IS_OK(wbc_status)) {
+ wbcFreeMemory(groups);
+ return false;
+ }
+
+ DEBUG(3, (" %s\n", sid_str));
+
+ string_to_sid(&sid, sid_str);
+ wbcFreeMemory(sid_str);
+ sid_str = NULL;
+
+ 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 = SMB_MALLOC_ARRAY(struct user_token, num_users);
+
+ if (result == NULL) {
+ DEBUG(1, ("Could not malloc sid 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]);
+ strupper_m(domain);
+ fstrcpy(user, p);
+ }
+
+ get_user_sids(domain, user, &(result[i].token));
+ i+=1;
+ }
+ 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 (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+
+ if (line[0] == ' ') {
+ /* We have a SID */
+
+ DOM_SID sid;
+ string_to_sid(&sid, &line[1]);
+
+ if (token == NULL) {
+ DEBUG(0, ("File does not begin with username"));
+ return false;
+ }
+
+ add_sid_to_token(&token->token, &sid);
+ continue;
+ }
+
+ /* And a new user... */
+
+ *num_tokens += 1;
+ *tokens = SMB_REALLOC_ARRAY(*tokens, struct user_token, *num_tokens);
+ if (*tokens == NULL) {
+ DEBUG(0, ("Could not realloc tokens\n"));
+ return false;
+ }
+
+ token = &((*tokens)[*num_tokens-1]);
+
+ fstrcpy(token->name, line);
+ token->token.num_sids = 0;
+ token->token.user_sids = NULL;
+ 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,
+ TALLOC_CTX *mem_ctx,
+ const char *netname,
+ int num_tokens,
+ struct user_token *tokens)
+{
+ int fnum;
+ SEC_DESC *share_sd = NULL;
+ SEC_DESC *root_sd = NULL;
+ struct cli_state *cli = rpc_pipe_np_smb_conn(pipe_hnd);
+ int i;
+ union srvsvc_NetShareInfo info;
+ WERROR result;
+ NTSTATUS status;
+ uint16 cnum;
+
+ status = rpccli_srvsvc_NetShareGetInfo(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ netname,
+ 502,
+ &info,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ DEBUG(1, ("Coult 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));
+ }
+
+ cnum = cli->cnum;
+
+ if (!cli_send_tconX(cli, netname, "A:", "", 0)) {
+ return;
+ }
+
+ fnum = cli_nt_create(cli, "\\", READ_CONTROL_ACCESS);
+
+ if (fnum != -1) {
+ root_sd = cli_query_secdesc(cli, fnum, mem_ctx);
+ }
+
+ for (i=0; i<num_tokens; i++) {
+ uint32 acc_granted;
+
+ if (share_sd != NULL) {
+ if (!se_access_check(share_sd, &tokens[i].token,
+ 1, &acc_granted, &status)) {
+ DEBUG(1, ("Could not check share_sd for "
+ "user %s\n",
+ tokens[i].name));
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status))
+ continue;
+ }
+
+ if (root_sd == NULL) {
+ d_printf(" %s\n", tokens[i].name);
+ continue;
+ }
+
+ if (!se_access_check(root_sd, &tokens[i].token,
+ 1, &acc_granted, &status)) {
+ DEBUG(1, ("Could not check root_sd for user %s\n",
+ tokens[i].name));
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(status))
+ continue;
+
+ d_printf(" %s\n", tokens[i].name);
+ }
+
+ if (fnum != -1)
+ cli_close(cli, fnum);
+ cli_tdis(cli);
+ cli->cnum = cnum;
+
+ return;
+}
+
+struct share_list {
+ int num_shares;
+ char **shares;
+};
+
+static void collect_share(const char *name, uint32 m,
+ const char *comment, void *state)
+{
+ struct share_list *share_list = (struct share_list *)state;
+
+ if (m != STYPE_DISKTREE)
+ return;
+
+ share_list->num_shares += 1;
+ share_list->shares = SMB_REALLOC_ARRAY(share_list->shares, char *, share_list->num_shares);
+ if (!share_list->shares) {
+ share_list->num_shares = 0;
+ return;
+ }
+ share_list->shares[share_list->num_shares-1] = SMB_STRDUP(name);
+}
+
+/**
+ * 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 DOM_SID *domain_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 ret;
+ bool r;
+ ENUM_HND hnd;
+ uint32 i;
+ FILE *f;
+
+ struct user_token *tokens = NULL;
+ int num_tokens = 0;
+
+ struct share_list share_list;
+
+ if (argc == 0) {
+ f = stdin;
+ } else {
+ f = fopen(argv[0], "r");
+ }
+
+ 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);
+
+ init_enum_hnd(&hnd, 0);
+
+ share_list.num_shares = 0;
+ share_list.shares = NULL;
+
+ ret = cli_RNetShareEnum(cli, collect_share, &share_list);
+
+ if (ret == -1) {
+ DEBUG(0, ("Error returning browse list: %s\n",
+ cli_errstr(cli)));
+ goto done;
+ }
+
+ for (i = 0; i < share_list.num_shares; i++) {
+ char *netname = share_list.shares[i];
+
+ if (netname[strlen(netname)-1] == '$')
+ continue;
+
+ d_printf("%s\n", netname);
+
+ show_userlist(pipe_hnd, mem_ctx, netname,
+ num_tokens, tokens);
+ }
+ done:
+ for (i=0; i<num_tokens; i++) {
+ free_user_token(&tokens[i].token);
+ }
+ SAFE_FREE(tokens);
+ SAFE_FREE(share_list.shares);
+
+ return NT_STATUS_OK;
+}
+
+static int rpc_share_allowedusers(struct net_context *c, int argc,
+ const char **argv)
+{
+ int result;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc share allowedusers\n"
+ " List allowed users\n");
+ return 0;
+ }
+
+ result = run_rpc_command(c, NULL, &ndr_table_samr.syntax_id, 0,
+ rpc_aliaslist_internals,
+ argc, argv);
+ if (result != 0)
+ return result;
+
+ result = run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 0,
+ rpc_aliaslist_dump,
+ argc, argv);
+ if (result != 0)
+ return result;
+
+ return run_rpc_command(c, NULL, &ndr_table_srvsvc.syntax_id, 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 0;
+ }
+
+ for (i=0; i<num_tokens; i++) {
+ dump_user_token(&tokens[i]);
+ free_user_token(&tokens[i].token);
+ }
+
+ SAFE_FREE(tokens);
+ return 1;
+}
+
+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,
+ "Add share",
+ "net rpc share add\n"
+ " Add share"
+ },
+ {
+ "delete",
+ rpc_share_delete,
+ NET_TRANSPORT_RPC,
+ "Remove share",
+ "net rpc share delete\n"
+ " Remove share"
+ },
+ {
+ "allowedusers",
+ rpc_share_allowedusers,
+ NET_TRANSPORT_RPC,
+ "Modify allowed users",
+ "net rpc share allowedusers\n"
+ " Modify allowed users"
+ },
+ {
+ "migrate",
+ rpc_share_migrate,
+ NET_TRANSPORT_RPC,
+ "Migrate share to local server",
+ "net rpc share migrate\n"
+ " Migrate share to local server"
+ },
+ {
+ "list",
+ rpc_share_list,
+ NET_TRANSPORT_RPC,
+ "List shares",
+ "net rpc share list\n"
+ " List shares"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_init(&c->netapi_ctx);
+ if (status != 0) {
+ return -1;
+ }
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "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, "usage: %s <share>\n", 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;
+
+ if (argc != 1) {
+ d_fprintf(stderr, "usage: %s <share>\n", ctx->whoami);
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_srvsvc_NetShareGetInfo(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ argv[0],
+ 2,
+ &info,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !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.syntax_id, rpc_sh_share_list,
+ "List available shares" },
+
+ { "add", NULL, &ndr_table_srvsvc.syntax_id, rpc_sh_share_add,
+ "Add a share" },
+
+ { "delete", NULL, &ndr_table_srvsvc.syntax_id, rpc_sh_share_delete,
+ "Delete a share" },
+
+ { "info", NULL, &ndr_table_srvsvc.syntax_id, rpc_sh_share_info,
+ "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.1d %-20.20s 0x%-4.2x %-6.1d %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 preferred_len = 0xffffffff, i;
+ const 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 **)&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:
+ 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,
+ "Close opened file",
+ "net rpc file close\n"
+ " Close opened file"
+ },
+ {
+ "user",
+ rpc_file_user,
+ NET_TRANSPORT_RPC,
+ "List files opened by user",
+ "net rpc file user\n"
+ " List files opened by user"
+ },
+#if 0
+ {
+ "info",
+ rpc_file_info,
+ NET_TRANSPORT_RPC,
+ "Display information about opened file",
+ "net rpc file info\n"
+ " Display information about opened file"
+ },
+#endif
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ status = libnetapi_init(&c->netapi_ctx);
+ if (status != 0) {
+ return -1;
+ }
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ 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 DOM_SID *domain_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;
+
+ result = rpccli_initshutdown_Abort(pipe_hnd, mem_ctx, NULL, NULL);
+
+ if (NT_STATUS_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 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 DOM_SID *domain_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;
+
+ result = rpccli_winreg_AbortSystemShutdown(pipe_hnd, mem_ctx, NULL, NULL);
+
+ if (NT_STATUS_IS_OK(result)) {
+ 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 result;
+}
+
+/**
+ * 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("Usage:\n"
+ "net rpc abortshutdown\n"
+ " Abort a scheduled shutdown\n");
+ return 0;
+ }
+
+ rc = run_rpc_command(c, NULL, &ndr_table_initshutdown.syntax_id, 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.syntax_id, 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 DOM_SID *domain_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;
+ const char *msg = "This machine will be shutdown shortly";
+ uint32 timeout = 20;
+ struct initshutdown_String msg_string;
+ struct initshutdown_String_sub s;
+
+ if (c->opt_comment) {
+ msg = c->opt_comment;
+ }
+ if (c->opt_timeout) {
+ timeout = c->opt_timeout;
+ }
+
+ s.name = msg;
+ msg_string.name = &s;
+
+ /* create an entry */
+ result = rpccli_initshutdown_Init(pipe_hnd, mem_ctx, NULL,
+ &msg_string, timeout, c->opt_force, c->opt_reboot,
+ NULL);
+
+ if (NT_STATUS_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 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 DOM_SID *domain_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 = "This machine will be shutdown shortly";
+ uint32 timeout = 20;
+ struct initshutdown_String msg_string;
+ struct initshutdown_String_sub s;
+ NTSTATUS result;
+ WERROR werr;
+
+ if (c->opt_comment) {
+ msg = c->opt_comment;
+ }
+ s.name = msg;
+ msg_string.name = &s;
+
+ if (c->opt_timeout) {
+ timeout = c->opt_timeout;
+ }
+
+ /* create an entry */
+ result = rpccli_winreg_InitiateSystemShutdown(pipe_hnd, mem_ctx, NULL,
+ &msg_string, timeout, c->opt_force, c->opt_reboot,
+ &werr);
+
+ if (NT_STATUS_IS_OK(result)) {
+ 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", dos_errstr(werr));
+ }
+
+ return result;
+}
+
+/**
+ * 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("Usage:\n"
+ "net rpc shutdown\n"
+ " Shut down a remote RPC server\n");
+ return 0;
+ }
+
+ rc = run_rpc_command(c, NULL, &ndr_table_initshutdown.syntax_id, 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.syntax_id, 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ struct lsa_String lsa_acct_name;
+ uint32 acb_info;
+ uint32 acct_flags=0;
+ uint32 user_rid;
+ uint32_t access_granted = 0;
+ union samr_UserInfo info;
+ unsigned int orig_timeout;
+
+ if (argc != 2) {
+ d_printf("Usage: net rpc trustdom add <domain_name> "
+ "<trust password>\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * Make valid trusting domain account (ie. uppercased and with '$' appended)
+ */
+
+ if (asprintf(&acct_name, "%s$", argv[0]) < 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ strupper_m(acct_name);
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ /* Get samr policy handle */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(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;
+
+ result = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &lsa_acct_name,
+ acb_info,
+ acct_flags,
+ &user_pol,
+ &access_granted,
+ &user_rid);
+
+ /* And restore our original timeout. */
+ rpccli_set_timeout(pipe_hnd, orig_timeout);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("net rpc trustdom add: create user %s failed %s\n",
+ acct_name, nt_errstr(result));
+ goto done;
+ }
+
+ {
+ NTTIME notime;
+ struct samr_LogonHours hours;
+ struct lsa_BinaryString parameters;
+ const int units_per_week = 168;
+ struct samr_CryptPassword crypt_pwd;
+
+ ZERO_STRUCT(notime);
+ ZERO_STRUCT(hours);
+ ZERO_STRUCT(parameters);
+
+ hours.bits = talloc_array(mem_ctx, uint8_t, units_per_week);
+ if (!hours.bits) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ hours.units_per_week = units_per_week;
+ memset(hours.bits, 0xFF, units_per_week);
+
+ init_samr_CryptPassword(argv[1],
+ &cli->user_session_key,
+ &crypt_pwd);
+
+ init_samr_user_info23(&info.info23,
+ notime, notime, notime,
+ notime, notime, notime,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, &parameters,
+ 0, 0, ACB_DOMTRUST, SAMR_FIELD_ACCT_FLAGS,
+ hours,
+ 0, 0, 0, 0, 0, 0, 0,
+ crypt_pwd.data, 24);
+
+ result = rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
+ &user_pol,
+ 23,
+ &info);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("Could not set trust account password: %s\n",
+ nt_errstr(result)));
+ goto done;
+ }
+ }
+
+ done:
+ SAFE_FREE(acct_name);
+ return result;
+}
+
+/**
+ * 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.syntax_id, 0,
+ rpc_trustdom_add_internals, argc, argv);
+ } else {
+ d_printf("Usage:\n"
+ "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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND connect_pol, domain_pol, user_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ char *acct_name;
+ DOM_SID trust_acct_sid;
+ struct samr_Ids user_rids, name_types;
+ struct lsa_String lsa_acct_name;
+
+ if (argc != 1) {
+ d_printf("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;
+
+ strupper_m(acct_name);
+
+ /* Get samr policy handle */
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ CONST_DISCARD(struct dom_sid2 *, domain_sid),
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ result = rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &user_rids,
+ &name_types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("net rpc trustdom del: LookupNames on user %s failed %s\n",
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+
+ result = rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ user_rids.ids[0],
+ &user_pol);
+
+ if (!NT_STATUS_IS_OK(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 */
+ sid_copy(&trust_acct_sid, domain_sid);
+ if (!sid_append_rid(&trust_acct_sid, user_rids.ids[0])) {
+ goto done;
+ }
+
+ /* remove the sid */
+
+ result = rpccli_samr_RemoveMemberFromForeignDomain(pipe_hnd, mem_ctx,
+ &user_pol,
+ &trust_acct_sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("net rpc trustdom del: RemoveMemberFromForeignDomain on user %s failed %s\n",
+ acct_name, nt_errstr(result) );
+ goto done;
+ }
+
+ /* Delete user */
+
+ result = rpccli_samr_DeleteUser(pipe_hnd, mem_ctx,
+ &user_pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ 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 result;
+}
+
+/**
+ * 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.syntax_id, 0,
+ rpc_trustdom_del_internals, argc, argv);
+ } else {
+ d_printf("Usage:\n"
+ "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;
+
+ /* 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.syntax_id,
+ &netr);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = rpccli_netr_GetDcName(netr, mem_ctx,
+ cli->desthost,
+ domain_name,
+ &buffer,
+ NULL);
+ TALLOC_FREE(netr);
+
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(1,("netr_GetDcName error: Couldn't find primary domain controller\
+ for domain %s\n", domain_name));
+
+ return status;
+}
+
+/**
+ * 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;
+ POLICY_HND connect_hnd;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS nt_status;
+ DOM_SID *domain_sid;
+
+ char* domain_name;
+ char* acct_name;
+ fstring pdc_name;
+ union lsa_PolicyInformation *info = NULL;
+
+ /*
+ * Connect to \\server\ipc$ as 'our domain' account with password
+ */
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc trustdom establish <domain_name>\n");
+ return -1;
+ }
+
+ domain_name = smb_xstrdup(argv[0]);
+ strupper_m(domain_name);
+
+ /* account name used at first is our domain's name with '$' */
+ asprintf(&acct_name, "%s$", lp_workgroup());
+ strupper_m(acct_name);
+
+ /*
+ * 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);
+ };
+
+ c->opt_user_name = acct_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.syntax_id,
+ &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;
+ }
+
+ nt_status = rpccli_lsa_open_policy2(pipe_hnd, mem_ctx, true, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ }
+
+ /* Querying info level 5 */
+
+ nt_status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info);
+ 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;
+ }
+
+ 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
+ */
+
+ if (!pdb_set_trusteddom_pw(domain_name, c->opt_password, 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 = rpccli_lsa_Close(pipe_hnd, mem_ctx, &connect_hnd);
+ 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("Usage:\n"
+ "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]);
+ strupper_m(domain_name);
+
+ /* 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ fstring str_sid;
+ sid_to_fstring(str_sid, domain_sid);
+ d_printf("%s\n", str_sid);
+ return NT_STATUS_OK;
+}
+
+static void print_trusted_domain(DOM_SID *dom_sid, const char *trusted_dom_name)
+{
+ fstring ascii_sid, padding;
+ int pad_len, col_len = 20;
+
+ /* convert sid into ascii string */
+ sid_to_fstring(ascii_sid, dom_sid);
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(trusted_dom_name);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ d_printf("%s%s%s\n", trusted_dom_name, padding, ascii_sid);
+}
+
+static NTSTATUS vampire_trusted_domain(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *pol,
+ DOM_SID dom_sid,
+ const char *trusted_dom_name)
+{
+ NTSTATUS nt_status;
+ union lsa_TrustedDomainInfo *info = NULL;
+ char *cleartextpwd = NULL;
+ uint8_t nt_hash[16];
+ DATA_BLOB data;
+
+ nt_status = rpccli_lsa_QueryTrustedDomainInfoBySid(pipe_hnd, mem_ctx,
+ pol,
+ &dom_sid,
+ LSA_TRUSTED_DOMAIN_INFO_PASSWORD,
+ &info);
+ 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;
+ }
+
+ data = data_blob(info->password.password->data,
+ info->password.password->length);
+
+ if (!rpccli_get_pwd_hash(pipe_hnd, nt_hash)) {
+ DEBUG(0, ("Could not retrieve password hash\n"));
+ goto done;
+ }
+
+ cleartextpwd = decrypt_trustdom_secret(nt_hash, &data);
+
+ 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
+ DEBUG(100,("successfully vampired trusted domain [%s], sid: [%s], "
+ "password: [%s]\n", trusted_dom_name,
+ sid_string_dbg(&dom_sid), 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;
+ const char *domain_name = NULL;
+ DOM_SID *queried_dom_sid;
+ POLICY_HND connect_hnd;
+ union lsa_PolicyInformation *info = NULL;
+
+ /* trusted domains listing variables */
+ unsigned int enum_ctx = 0;
+ int i;
+ struct lsa_DomainList dom_list;
+ fstring pdc_name;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc trustdom vampire\n"
+ " Vampire trust relationship from remote server\n");
+ 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(c->opt_workgroup, lp_workgroup())) {
+ domain_name = c->opt_workgroup;
+ c->opt_target_workgroup = c->opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, global_myname());
+ 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.syntax_id,
+ &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;
+ };
+
+ nt_status = rpccli_lsa_open_policy2(pipe_hnd, mem_ctx, false, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info);
+
+ 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;
+ }
+
+ queried_dom_sid = info->account_domain.sid;
+
+ /*
+ * Keep calling LsaEnumTrustdom over opened pipe until
+ * the end of enumeration is reached
+ */
+
+ d_printf("Vampire trusted domains:\n\n");
+
+ do {
+ nt_status = rpccli_lsa_EnumTrustDom(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t)-1);
+ 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;
+ };
+
+ 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 = rpccli_lsa_Close(pipe_hnd, mem_ctx, &connect_hnd);
+ 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;
+ const char *domain_name = NULL;
+ DOM_SID *queried_dom_sid;
+ fstring padding;
+ int ascii_dom_name_len;
+ POLICY_HND connect_hnd;
+ union lsa_PolicyInformation *info = NULL;
+
+ /* trusted domains listing variables */
+ unsigned int num_domains, enum_ctx = 0;
+ int i, pad_len, col_len = 20;
+ struct lsa_DomainList dom_list;
+ fstring pdc_name;
+
+ /* trusting domains listing variables */
+ POLICY_HND domain_hnd;
+ struct samr_SamArray *trusts = NULL;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc trustdom list\n"
+ " List trust relationships\n");
+ 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(c->opt_workgroup, lp_workgroup())) {
+ domain_name = c->opt_workgroup;
+ c->opt_target_workgroup = c->opt_workgroup;
+ } else {
+ fstrcpy(pdc_name, global_myname());
+ 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.syntax_id,
+ &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;
+ };
+
+ nt_status = rpccli_lsa_open_policy2(pipe_hnd, mem_ctx, false, SEC_RIGHTS_QUERY_VALUE,
+ &connect_hnd);
+ if (NT_STATUS_IS_ERR(nt_status)) {
+ DEBUG(0, ("Couldn't open policy handle. Error was %s\n",
+ nt_errstr(nt_status)));
+ cli_shutdown(cli);
+ talloc_destroy(mem_ctx);
+ return -1;
+ };
+
+ /* query info level 5 to obtain sid of a domain being queried */
+ nt_status = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info);
+
+ 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;
+ }
+
+ 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");
+
+ do {
+ nt_status = rpccli_lsa_EnumTrustDom(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ &enum_ctx,
+ &dom_list,
+ (uint32_t)-1);
+ 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;
+ };
+
+ for (i = 0; i < dom_list.count; i++) {
+ print_trusted_domain(dom_list.domains[i].sid,
+ dom_list.domains[i].name.string);
+ };
+
+ /*
+ * 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 = rpccli_lsa_Close(pipe_hnd, mem_ctx, &connect_hnd);
+ 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.syntax_id,
+ &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;
+ };
+
+ /* SamrConnect2 */
+ nt_status = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ SA_RIGHT_SAM_OPEN_DOMAIN,
+ &connect_hnd);
+ 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;
+ };
+
+ /* SamrOpenDomain - we have to open domain policy handle in order to be
+ able to enumerate accounts*/
+ nt_status = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_hnd,
+ SA_RIGHT_DOMAIN_ENUM_ACCOUNTS,
+ queried_dom_sid,
+ &domain_hnd);
+ 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;
+ };
+
+ /*
+ * perform actual enumeration
+ */
+
+ enum_ctx = 0; /* reset enumeration context from last enumeration */
+ do {
+
+ nt_status = rpccli_samr_EnumDomainUsers(pipe_hnd, mem_ctx,
+ &domain_hnd,
+ &enum_ctx,
+ ACB_DOMTRUST,
+ &trusts,
+ 0xffff,
+ &num_domains);
+ 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;
+ };
+
+ for (i = 0; i < num_domains; i++) {
+
+ char *str = CONST_DISCARD(char *, trusts->entries[i].name.string);
+
+ /*
+ * 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';
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(str);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len);
+
+ /* set opt_* variables to remote domain */
+ strupper_m(str);
+ c->opt_workgroup = talloc_strdup(mem_ctx, str);
+ c->opt_target_workgroup = c->opt_workgroup;
+
+ d_printf("%s%s", str, padding);
+
+ /* 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.syntax_id, 0,
+ rpc_query_domain_sid, argc,
+ argv))
+ d_fprintf(stderr, "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));
+ };
+ };
+
+ if (!num_domains) d_printf("none\n");
+
+ } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES));
+
+ /* close opened samr and domain policy handles */
+ nt_status = rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_hnd);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name));
+ };
+
+ nt_status = rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_hnd);
+ 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,
+ "Add trusted domain's account",
+ "net rpc trustdom add\n"
+ " Add trusted domain's account"
+ },
+ {
+ "del",
+ rpc_trustdom_del,
+ NET_TRANSPORT_RPC,
+ "Remove trusted domain's account",
+ "net rpc trustdom del\n"
+ " Remove trusted domain's account"
+ },
+ {
+ "establish",
+ rpc_trustdom_establish,
+ NET_TRANSPORT_RPC,
+ "Establish trust relationship",
+ "net rpc trustdom establish\n"
+ " Establish trust relationship"
+ },
+ {
+ "revoke",
+ rpc_trustdom_revoke,
+ NET_TRANSPORT_RPC,
+ "Revoke trust relationship",
+ "net rpc trustdom revoke\n"
+ " Revoke trust relationship"
+ },
+ {
+ "list",
+ rpc_trustdom_list,
+ NET_TRANSPORT_RPC,
+ "List domain trusts",
+ "net rpc trustdom list\n"
+ " List domain trusts"
+ },
+ {
+ "vampire",
+ rpc_trustdom_vampire,
+ NET_TRANSPORT_RPC,
+ "Vampire trusts from remote server",
+ "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;
+
+ if ((cli = cli_initialise()) == NULL) {
+ return false;
+ }
+
+ status = cli_connect(cli, server_name, &server_ss);
+ if (!NT_STATUS_IS_OK(status))
+ goto done;
+ if (!attempt_netbios_session_request(&cli, global_myname(),
+ server_name, &server_ss))
+ goto done;
+ if (!cli_negprot(cli))
+ goto done;
+ if (cli->protocol < PROTOCOL_NT1)
+ goto done;
+
+ ret = true;
+ done:
+ cli_shutdown(cli);
+ return ret;
+}
+
+/* dump sam database via samsync rpc calls */
+static int rpc_samdump(struct net_context *c, int argc, const char **argv) {
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc samdump\n"
+ " Dump remote SAM database\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id,
+ NET_FLAGS_ANONYMOUS,
+ rpc_samdump_internals, argc, argv);
+}
+
+/* syncronise sam database via samsync rpc calls */
+static int rpc_vampire(struct net_context *c, int argc, const char **argv)
+{
+ struct functable func[] = {
+ {
+ "ldif",
+ rpc_vampire_ldif,
+ NET_TRANSPORT_RPC,
+ "Dump remote SAM database to ldif",
+ "net rpc vampire ldif\n"
+ " Dump remote SAM database to LDIF file or stdout"
+ },
+ {
+ "keytab",
+ rpc_vampire_keytab,
+ NET_TRANSPORT_RPC,
+ "Dump remote SAM database to Kerberos Keytab",
+ "net rpc vampire keytab\n"
+ " Dump remote SAM database to Kerberos keytab file"
+ },
+
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ if (argc == 0) {
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc vampire\n"
+ " Vampire remote SAM database\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id,
+ NET_FLAGS_ANONYMOUS,
+ rpc_vampire_internals,
+ 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("Usage:\n"
+ "net rpc printer migrate all\n"
+ " Migrate everything from a print server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ ret = run_rpc_command(c, NULL, &syntax_spoolss, 0,
+ rpc_printer_migrate_printers_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &syntax_spoolss, 0,
+ rpc_printer_migrate_drivers_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &syntax_spoolss, 0,
+ rpc_printer_migrate_forms_internals, argc, argv);
+ if (ret)
+ return ret;
+
+ ret = run_rpc_command(c, NULL, &syntax_spoolss, 0,
+ rpc_printer_migrate_settings_internals, argc,
+ argv);
+ if (ret)
+ return ret;
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer migrate drivers\n"
+ " Migrate print-drivers from a print-server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer migrate forms\n"
+ " Migrate print-forms from a print-server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer migrate printers\n"
+ " Migrate printers from a print-server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer migrate security\n"
+ " Migrate printer-ACLs from a print-server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer migrate settings\n"
+ " Migrate printer-settings from a print-server\n");
+ return 0;
+ }
+
+ if (!c->opt_host) {
+ d_printf("no server to migrate\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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,
+ "Migrate all from remote to local print server",
+ "net rpc printer migrate all\n"
+ " Migrate all from remote to local print server"
+ },
+ {
+ "drivers",
+ rpc_printer_migrate_drivers,
+ NET_TRANSPORT_RPC,
+ "Migrate drivers to local server",
+ "net rpc printer migrate drivers\n"
+ " Migrate drivers to local server"
+ },
+ {
+ "forms",
+ rpc_printer_migrate_forms,
+ NET_TRANSPORT_RPC,
+ "Migrate froms to local server",
+ "net rpc printer migrate forms\n"
+ " Migrate froms to local server"
+ },
+ {
+ "printers",
+ rpc_printer_migrate_printers,
+ NET_TRANSPORT_RPC,
+ "Migrate printers to local server",
+ "net rpc printer migrate printers\n"
+ " Migrate printers to local server"
+ },
+ {
+ "security",
+ rpc_printer_migrate_security,
+ NET_TRANSPORT_RPC,
+ "Mirgate printer ACLs to local server",
+ "net rpc printer migrate security\n"
+ " Mirgate printer ACLs to local server"
+ },
+ {
+ "settings",
+ rpc_printer_migrate_settings,
+ NET_TRANSPORT_RPC,
+ "Migrate printer settings to local server",
+ "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("Usage:\n"
+ "net rpc printer list\n"
+ " List printers on a remote RPC server\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer driver\n"
+ " List printer-drivers on a remote RPC server\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer publish publish\n"
+ " Publish printer in ADS via MSRPC\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer publish update\n"
+ " Update printer in ADS via MSRPC\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer publish unpublish\n"
+ " UnPublish printer in ADS via MSRPC\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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("Usage:\n"
+ "net rpc printer publish list\n"
+ " List published printers via MSRPC\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &syntax_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,
+ "Publish printer in AD",
+ "net rpc printer publish publish\n"
+ " Publish printer in AD"
+ },
+ {
+ "update",
+ rpc_printer_publish_update,
+ NET_TRANSPORT_RPC,
+ "Update printer in AD",
+ "net rpc printer publish update\n"
+ " Update printer in AD"
+ },
+ {
+ "unpublish",
+ rpc_printer_publish_unpublish,
+ NET_TRANSPORT_RPC,
+ "Unpublish printer",
+ "net rpc printer publish unpublish\n"
+ " Unpublish printer"
+ },
+ {
+ "list",
+ rpc_printer_publish_list,
+ NET_TRANSPORT_RPC,
+ "List published printers",
+ "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, &syntax_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,
+ "List all printers on print server",
+ "net rpc printer list\n"
+ " List all printers on print server"
+ },
+ {
+ "migrate",
+ rpc_printer_migrate,
+ NET_TRANSPORT_RPC,
+ "Migrate printer to local server",
+ "net rpc printer migrate\n"
+ " Migrate printer to local server"
+ },
+ {
+ "driver",
+ rpc_printer_driver_list,
+ NET_TRANSPORT_RPC,
+ "List printer drivers",
+ "net rpc printer driver\n"
+ " List printer drivers"
+ },
+ {
+ "publish",
+ rpc_printer_publish,
+ NET_TRANSPORT_RPC,
+ "Publish printer in AD",
+ "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, &syntax_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)
+{
+ struct functable func[] = {
+ {
+ "audit",
+ net_rpc_audit,
+ NET_TRANSPORT_RPC,
+ "Modify global audit settings",
+ "net rpc audit\n"
+ " Modify global audit settings"
+ },
+ {
+ "info",
+ net_rpc_info,
+ NET_TRANSPORT_RPC,
+ "Show basic info about a domain",
+ "net rpc info\n"
+ " Show basic info about a domain"
+ },
+ {
+ "join",
+ net_rpc_join,
+ NET_TRANSPORT_RPC,
+ "Join a domain",
+ "net rpc join\n"
+ " Join a domain"
+ },
+ {
+ "oldjoin",
+ net_rpc_oldjoin,
+ NET_TRANSPORT_RPC,
+ "Join a domain created in server manager",
+ "net rpc oldjoin\n"
+ " Join a domain created in server manager"
+ },
+ {
+ "testjoin",
+ net_rpc_testjoin,
+ NET_TRANSPORT_RPC,
+ "Test that a join is valid",
+ "net rpc testjoin\n"
+ " Test that a join is valid"
+ },
+ {
+ "user",
+ net_rpc_user,
+ NET_TRANSPORT_RPC,
+ "List/modify users",
+ "net rpc user\n"
+ " List/modify users"
+ },
+ {
+ "password",
+ rpc_user_password,
+ NET_TRANSPORT_RPC,
+ "Change a user password",
+ "net rpc password\n"
+ " Change a user password\n"
+ " Alias for net rpc user password"
+ },
+ {
+ "group",
+ net_rpc_group,
+ NET_TRANSPORT_RPC,
+ "List/modify groups",
+ "net rpc group\n"
+ " List/modify groups"
+ },
+ {
+ "share",
+ net_rpc_share,
+ NET_TRANSPORT_RPC,
+ "List/modify shares",
+ "net rpc share\n"
+ " List/modify shares"
+ },
+ {
+ "file",
+ net_rpc_file,
+ NET_TRANSPORT_RPC,
+ "List open files",
+ "net rpc file\n"
+ " List open files"
+ },
+ {
+ "printer",
+ net_rpc_printer,
+ NET_TRANSPORT_RPC,
+ "List/modify printers",
+ "net rpc printer\n"
+ " List/modify printers"
+ },
+ {
+ "changetrustpw",
+ net_rpc_changetrustpw,
+ NET_TRANSPORT_RPC,
+ "Change trust account password",
+ "net rpc changetrustpw\n"
+ " Change trust account password"
+ },
+ {
+ "trustdom",
+ rpc_trustdom,
+ NET_TRANSPORT_RPC,
+ "Modify domain trusts",
+ "net rpc trustdom\n"
+ " Modify domain trusts"
+ },
+ {
+ "abortshutdown",
+ rpc_shutdown_abort,
+ NET_TRANSPORT_RPC,
+ "Abort a remote shutdown",
+ "net rpc abortshutdown\n"
+ " Abort a remote shutdown"
+ },
+ {
+ "shutdown",
+ rpc_shutdown,
+ NET_TRANSPORT_RPC,
+ "Shutdown a remote server",
+ "net rpc shutdown\n"
+ " Shutdown a remote server"
+ },
+ {
+ "samdump",
+ rpc_samdump,
+ NET_TRANSPORT_RPC,
+ "Dump SAM data of remote NT PDC",
+ "net rpc samdump\n"
+ " Dump SAM data of remote NT PDC"
+ },
+ {
+ "vampire",
+ rpc_vampire,
+ NET_TRANSPORT_RPC,
+ "Sync a remote NT PDC's data into local passdb",
+ "net rpc vampire\n"
+ " Sync a remote NT PDC's data into local passdb"
+ },
+ {
+ "getsid",
+ net_rpc_getsid,
+ NET_TRANSPORT_RPC,
+ "Fetch the domain sid into local secrets.tdb",
+ "net rpc getsid\n"
+ " Fetch the domain sid into local secrets.tdb"
+ },
+ {
+ "rights",
+ net_rpc_rights,
+ NET_TRANSPORT_RPC,
+ "Manage privileges assigned to SID",
+ "net rpc rights\n"
+ " Manage privileges assigned to SID"
+ },
+ {
+ "service",
+ net_rpc_service,
+ NET_TRANSPORT_RPC,
+ "Start/stop/query remote services",
+ "net rpc service\n"
+ " Start/stop/query remote services"
+ },
+ {
+ "registry",
+ net_rpc_registry,
+ NET_TRANSPORT_RPC,
+ "Manage registry hives",
+ "net rpc registry\n"
+ " Manage registry hives"
+ },
+ {
+ "shell",
+ net_rpc_shell,
+ NET_TRANSPORT_RPC,
+ "Open interactive shell on remote server",
+ "net rpc shell\n"
+ " Open interactive shell on remote server"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+ 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 0000000000..dc4c796c17
--- /dev/null
+++ b/source3/utils/net_rpc_audit.c
@@ -0,0 +1,494 @@
+/*
+ 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"
+
+/********************************************************************
+********************************************************************/
+
+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)
+{
+ fstring padding;
+ int pad_len, col_len = 30;
+
+ if (policy == NULL) {
+ policy = "Unknown";
+ }
+ if (value == NULL) {
+ value = "Invalid";
+ }
+
+ /* calculate padding space for d_printf to look nicer */
+ pad_len = col_len - strlen(policy);
+ padding[pad_len] = 0;
+ do padding[--pad_len] = ' '; while (pad_len > 0);
+
+ d_printf("\t%s%s%s\n", policy, padding, value);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_get_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union lsa_PolicyInformation *info = NULL;
+ int i;
+ uint32_t audit_category;
+
+ 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;
+ }
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info);
+
+ if (!NT_STATUS_IS_OK(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(result)) {
+ d_printf("failed to get auditing policy: %s\n",
+ nt_errstr(result));
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_set_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union lsa_PolicyInformation *info = NULL;
+ uint32_t audit_policy, audit_category;
+
+ 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;
+ }
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ info->audit_events.settings[audit_category] = audit_policy;
+
+ result = rpccli_lsa_SetInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ info);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info);
+ {
+ 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(result)) {
+ d_printf("failed to set audit policy: %s\n", nt_errstr(result));
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_enable_internal_ext(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv,
+ bool enable)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union lsa_PolicyInformation *info = NULL;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ info->audit_events.auditing_mode = enable;
+
+ result = rpccli_lsa_SetInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ info);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ done:
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("failed to %s audit policy: %s\n",
+ enable ? "enable":"disable", nt_errstr(result));
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_audit_disable_internal(struct net_context *c,
+ const DOM_SID *domain_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 DOM_SID *domain_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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union lsa_PolicyInformation *info = NULL;
+ int i;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &pol,
+ LSA_POLICY_INFO_AUDIT_EVENTS,
+ &info);
+ if (!NT_STATUS_IS_OK(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(result)) {
+ d_printf("failed to list auditing policies: %s\n",
+ nt_errstr(result));
+ }
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_audit_get(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc audit get\n"
+ " View configured audit setting\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 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("Usage:\n"
+ "net rpc audit set\n"
+ " Set audit policies\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 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("Usage:\n"
+ "net rpc audit enable\n"
+ " Enable auditing\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 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("Usage:\n"
+ "net rpc audit disable\n"
+ " Disable auditing\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 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("Usage:\n"
+ "net rpc audit list\n"
+ " List auditing settings\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_lsarpc.syntax_id, 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,
+ "View configured auditing settings",
+ "net rpc audit get\n"
+ " View configured auditing settings"
+ },
+ {
+ "set",
+ rpc_audit_set,
+ NET_TRANSPORT_RPC,
+ "Set auditing policies",
+ "net rpc audit set\n"
+ " Set auditing policies"
+ },
+ {
+ "enable",
+ rpc_audit_enable,
+ NET_TRANSPORT_RPC,
+ "Enable auditing",
+ "net rpc audit enable\n"
+ " Enable auditing"
+ },
+ {
+ "disable",
+ rpc_audit_disable,
+ NET_TRANSPORT_RPC,
+ "Disable auditing",
+ "net rpc audit disable\n"
+ " Disable auditing"
+ },
+ {
+ "list",
+ rpc_audit_list,
+ NET_TRANSPORT_RPC,
+ "List configured auditing settings",
+ "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_join.c b/source3/utils/net_rpc_join.c
new file mode 100644
index 0000000000..5bc38f979f
--- /dev/null
+++ b/source3/utils/net_rpc_join.c
@@ -0,0 +1,506 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org)
+ Copyright (C) Tim Potter 2001
+ Copyright (C) 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"
+
+/* Macro for checking RPC error codes to make things more readable */
+
+#define CHECK_RPC_ERR(rpc, msg) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, (msg ": %s\n", nt_errstr(result))); \
+ goto done; \
+ }
+
+#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \
+ if (!NT_STATUS_IS_OK(result = rpc)) { \
+ DEBUG(0, debug_args); \
+ goto done; \
+ }
+
+/**
+ * confirm that a domain join is still valid
+ *
+ * @return A shell status integer (0 for success)
+ *
+ **/
+NTSTATUS net_rpc_join_ok(struct net_context *c, const char *domain,
+ const char *server, struct sockaddr_storage *pss)
+{
+ enum security_types sec;
+ unsigned int conn_flags = NET_FLAGS_PDC;
+ uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ struct cli_state *cli = NULL;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+ struct rpc_pipe_client *netlogon_pipe = NULL;
+ NTSTATUS ntret = NT_STATUS_UNSUCCESSFUL;
+
+ sec = (enum security_types)lp_security();
+
+ if (sec == SEC_ADS) {
+ /* Connect to IPC$ using machine account's credentials. We don't use anonymous
+ connection here, as it may be denied by server's local policy. */
+ net_use_machine_account(c);
+
+ } else {
+ /* some servers (e.g. WinNT) don't accept machine-authenticated
+ smb connections */
+ conn_flags |= NET_FLAGS_ANONYMOUS;
+ }
+
+ /* Connect to remote machine */
+ ntret = net_make_ipc_connection_ex(c, domain, server, pss, conn_flags,
+ &cli);
+ if (!NT_STATUS_IS_OK(ntret)) {
+ return ntret;
+ }
+
+ /* Setup the creds as though we're going to do schannel... */
+ ntret = get_schannel_session_key(cli, domain, &neg_flags,
+ &netlogon_pipe);
+
+ /* We return NT_STATUS_INVALID_NETWORK_RESPONSE if the server is refusing
+ to negotiate schannel, but the creds were set up ok. That'll have to do. */
+
+ if (!NT_STATUS_IS_OK(ntret)) {
+ if (NT_STATUS_EQUAL(ntret, NT_STATUS_INVALID_NETWORK_RESPONSE)) {
+ cli_shutdown(cli);
+ return NT_STATUS_OK;
+ } else {
+ DEBUG(0,("net_rpc_join_ok: failed to get schannel session "
+ "key from server %s for domain %s. Error was %s\n",
+ cli->desthost, domain, nt_errstr(ntret) ));
+ cli_shutdown(cli);
+ return ntret;
+ }
+ }
+
+ /* Only do the rest of the schannel test if the client is allowed to do this. */
+ if (!lp_client_schannel()) {
+ cli_shutdown(cli);
+ /* We're good... */
+ return ntret;
+ }
+
+ ntret = cli_rpc_pipe_open_schannel_with_key(
+ cli, &ndr_table_netlogon.syntax_id, PIPE_AUTH_LEVEL_PRIVACY,
+ domain, netlogon_pipe->dc, &pipe_hnd);
+
+ if (!NT_STATUS_IS_OK(ntret)) {
+ DEBUG(0,("net_rpc_join_ok: failed to open schannel session "
+ "on netlogon pipe to server %s for domain %s. Error was %s\n",
+ cli->desthost, domain, nt_errstr(ntret) ));
+ /*
+ * Note: here, we have:
+ * (pipe_hnd != NULL) if and only if NT_STATUS_IS_OK(ntret)
+ */
+ }
+
+ cli_shutdown(cli);
+ return ntret;
+}
+
+/**
+ * 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)
+ *
+ **/
+
+int net_rpc_join_newstyle(struct net_context *c, int argc, const char **argv)
+{
+
+ /* libsmb variables */
+
+ struct cli_state *cli;
+ TALLOC_CTX *mem_ctx;
+ uint32 acb_info = ACB_WSTRUST;
+ uint32_t neg_flags = NETLOGON_NEG_AUTH2_ADS_FLAGS;
+ uint32 sec_channel_type;
+ struct rpc_pipe_client *pipe_hnd = NULL;
+
+ /* rpc variables */
+
+ POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol;
+ DOM_SID *domain_sid;
+ uint32 user_rid;
+
+ /* Password stuff */
+
+ char *clear_trust_password = NULL;
+ struct samr_CryptPassword crypt_pwd;
+ uchar md4_trust_password[16];
+ union samr_UserInfo set_info;
+
+ /* Misc */
+
+ NTSTATUS result;
+ int retval = 1;
+ const char *domain = NULL;
+ char *acct_name;
+ struct lsa_String lsa_acct_name;
+ uint32 acct_flags=0;
+ uint32_t access_granted = 0;
+ union lsa_PolicyInformation *info = NULL;
+ struct samr_Ids user_rids;
+ struct samr_Ids name_types;
+
+ /* check what type of join */
+ if (argc >= 0) {
+ sec_channel_type = get_sec_channel_type(argv[0]);
+ } else {
+ sec_channel_type = get_sec_channel_type(NULL);
+ }
+
+ switch (sec_channel_type) {
+ case SEC_CHAN_WKSTA:
+ acb_info = ACB_WSTRUST;
+ break;
+ case SEC_CHAN_BDC:
+ acb_info = ACB_SVRTRUST;
+ break;
+#if 0
+ case SEC_CHAN_DOMAIN:
+ acb_info = ACB_DOMTRUST;
+ break;
+#endif
+ }
+
+ /* Make authenticated connection to remote machine */
+
+ result = net_make_ipc_connection(c, NET_FLAGS_PDC, &cli);
+ if (!NT_STATUS_IS_OK(result)) {
+ return 1;
+ }
+
+ if (!(mem_ctx = talloc_init("net_rpc_join_newstyle"))) {
+ DEBUG(0, ("Could not initialise talloc context\n"));
+ goto done;
+ }
+
+ /* Fetch domain sid */
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error connecting to LSA pipe. Error was %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+
+ CHECK_RPC_ERR(rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &lsa_pol),
+ "error opening lsa policy handle");
+
+ CHECK_RPC_ERR(rpccli_lsa_QueryInfoPolicy(pipe_hnd, mem_ctx,
+ &lsa_pol,
+ LSA_POLICY_INFO_ACCOUNT_DOMAIN,
+ &info),
+ "error querying info policy");
+
+ domain = info->account_domain.name.string;
+ domain_sid = info->account_domain.sid;
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &lsa_pol);
+ TALLOC_FREE(pipe_hnd); /* Done with this pipe */
+
+ /* Bail out if domain didn't get set. */
+ if (!domain) {
+ DEBUG(0, ("Could not get domain name.\n"));
+ goto done;
+ }
+
+ /* Create domain user */
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_samr.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error connecting to SAM pipe. Error was %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ CHECK_RPC_ERR(rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &sam_pol),
+ "could not connect to SAM database");
+
+
+ CHECK_RPC_ERR(rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &sam_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ domain_sid,
+ &domain_pol),
+ "could not open domain");
+
+ /* Create domain user */
+ if ((acct_name = talloc_asprintf(mem_ctx, "%s$", global_myname())) == NULL) {
+ result = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ strlower_m(acct_name);
+
+ init_lsa_String(&lsa_acct_name, acct_name);
+
+ 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;
+
+ DEBUG(10, ("Creating account with flags: %d\n",acct_flags));
+
+ result = rpccli_samr_CreateUser2(pipe_hnd, mem_ctx,
+ &domain_pol,
+ &lsa_acct_name,
+ acb_info,
+ acct_flags,
+ &user_pol,
+ &access_granted,
+ &user_rid);
+
+ if (!NT_STATUS_IS_OK(result) &&
+ !NT_STATUS_EQUAL(result, NT_STATUS_USER_EXISTS)) {
+ d_fprintf(stderr, "Creation of workstation account failed\n");
+
+ /* If NT_STATUS_ACCESS_DENIED then we have a valid
+ username/password combo but the user does not have
+ administrator access. */
+
+ if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED))
+ d_fprintf(stderr, "User specified does not have administrator privileges\n");
+
+ goto done;
+ }
+
+ /* We *must* do this.... don't ask... */
+
+ if (NT_STATUS_IS_OK(result)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ }
+
+ CHECK_RPC_ERR_DEBUG(rpccli_samr_LookupNames(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &lsa_acct_name,
+ &user_rids,
+ &name_types),
+ ("error looking up rid for user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ if (name_types.ids[0] != SID_NAME_USER) {
+ DEBUG(0, ("%s is not a user account (type=%d)\n", acct_name, name_types.ids[0]));
+ goto done;
+ }
+
+ user_rid = user_rids.ids[0];
+
+ /* Open handle on user */
+
+ CHECK_RPC_ERR_DEBUG(
+ rpccli_samr_OpenUser(pipe_hnd, mem_ctx,
+ &domain_pol,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ user_rid,
+ &user_pol),
+ ("could not re-open existing user %s: %s\n",
+ acct_name, nt_errstr(result)));
+
+ /* Create a random machine account password */
+
+ {
+ char *str;
+ str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH);
+ clear_trust_password = SMB_STRDUP(str);
+ E_md4hash(clear_trust_password, md4_trust_password);
+ }
+
+ /* Set password on machine account */
+
+ init_samr_CryptPassword(clear_trust_password,
+ &cli->user_session_key,
+ &crypt_pwd);
+
+ init_samr_user_info24(&set_info.info24, crypt_pwd.data, 24);
+
+ CHECK_RPC_ERR(rpccli_samr_SetUserInfo2(pipe_hnd, mem_ctx,
+ &user_pol,
+ 24,
+ &set_info),
+ "error setting trust account password");
+
+ /* Why do we have to try to (re-)set the ACB to be the same as what
+ we passed in the samr_create_dom_user() call? When a NT
+ workstation is joined to a domain by an administrator the
+ acb_info is set to 0x80. For a normal user with "Add
+ workstations to the domain" rights the acb_info is 0x84. I'm
+ not sure whether it is supposed to make a difference or not. NT
+ seems to cope with either value so don't bomb out if the set
+ userinfo2 level 0x10 fails. -tpot */
+
+ set_info.info16.acct_flags = acb_info;
+
+ /* Ignoring the return value is necessary for joining a domain
+ as a normal user with "Add workstation to domain" privilege. */
+
+ result = rpccli_samr_SetUserInfo(pipe_hnd, mem_ctx,
+ &user_pol,
+ 16,
+ &set_info);
+
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &user_pol);
+ TALLOC_FREE(pipe_hnd); /* Done with this pipe */
+
+ /* Now check the whole process from top-to-bottom */
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_netlogon.syntax_id,
+ &pipe_hnd);
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0,("Error connecting to NETLOGON pipe. Error was %s\n",
+ nt_errstr(result) ));
+ goto done;
+ }
+
+ result = rpccli_netlogon_setup_creds(pipe_hnd,
+ cli->desthost, /* server name */
+ domain, /* domain */
+ global_myname(), /* client name */
+ global_myname(), /* machine account name */
+ md4_trust_password,
+ sec_channel_type,
+ &neg_flags);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error in domain join verification (credential setup failed): %s\n\n",
+ nt_errstr(result)));
+
+ if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) &&
+ (sec_channel_type == SEC_CHAN_BDC) ) {
+ d_fprintf(stderr, "Please make sure that no computer account\n"
+ "named like this machine (%s) exists in the domain\n",
+ global_myname());
+ }
+
+ goto done;
+ }
+
+ /* We can only check the schannel connection if the client is allowed
+ to do this and the server supports it. If not, just assume success
+ (after all the rpccli_netlogon_setup_creds() succeeded, and we'll
+ do the same again (setup creds) in net_rpc_join_ok(). JRA. */
+
+ if (lp_client_schannel() && (neg_flags & NETLOGON_NEG_SCHANNEL)) {
+ struct rpc_pipe_client *netlogon_schannel_pipe;
+
+ result = cli_rpc_pipe_open_schannel_with_key(
+ cli, &ndr_table_netlogon.syntax_id,
+ PIPE_AUTH_LEVEL_PRIVACY, domain, pipe_hnd->dc,
+ &netlogon_schannel_pipe);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ DEBUG(0, ("Error in domain join verification (schannel setup failed): %s\n\n",
+ nt_errstr(result)));
+
+ if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) &&
+ (sec_channel_type == SEC_CHAN_BDC) ) {
+ d_fprintf(stderr, "Please make sure that no computer account\n"
+ "named like this machine (%s) exists in the domain\n",
+ global_myname());
+ }
+
+ goto done;
+ }
+ TALLOC_FREE(netlogon_schannel_pipe);
+ }
+
+ TALLOC_FREE(pipe_hnd);
+
+ /* Now store the secret in the secrets database */
+
+ strupper_m(CONST_DISCARD(char *, domain));
+
+ if (!secrets_store_domain_sid(domain, domain_sid)) {
+ DEBUG(0, ("error storing domain sid for %s\n", domain));
+ goto done;
+ }
+
+ if (!secrets_store_machine_password(clear_trust_password, domain, sec_channel_type)) {
+ DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain));
+ }
+
+ /* double-check, connection from scratch */
+ result = net_rpc_join_ok(c, domain, cli->desthost, &cli->dest_ss);
+ retval = NT_STATUS_IS_OK(result) ? 0 : -1;
+
+done:
+
+ /* Display success or failure */
+
+ if (domain) {
+ if (retval != 0) {
+ fprintf(stderr,"Unable to join domain %s.\n",domain);
+ } else {
+ printf("Joined domain %s.\n",domain);
+ }
+ }
+
+ cli_shutdown(cli);
+
+ SAFE_FREE(clear_trust_password);
+
+ return retval;
+}
+
+/**
+ * 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)
+{
+ char *domain = smb_xstrdup(c->opt_target_workgroup);
+ NTSTATUS nt_status;
+
+ if (c->display_usage) {
+ d_printf("Usage\n"
+ "net rpc testjoin\n"
+ " Test if a join is OK\n");
+ return 0;
+ }
+
+ /* Display success or failure */
+ nt_status = net_rpc_join_ok(c, domain, NULL, NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ fprintf(stderr,"Join to domain '%s' is not valid: %s\n",
+ domain, nt_errstr(nt_status));
+ free(domain);
+ return -1;
+ }
+
+ printf("Join to '%s' is OK\n",domain);
+ free(domain);
+ return 0;
+}
diff --git a/source3/utils/net_rpc_printer.c b/source3/utils/net_rpc_printer.c
new file mode 100644
index 0000000000..f4b305d4ab
--- /dev/null
+++ b/source3/utils/net_rpc_printer.c
@@ -0,0 +1,2517 @@
+/*
+ Samba Unix/Linux SMB client library
+ Distributed SMB/CIFS Server Management Utility
+ Copyright (C) 2004 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"
+
+struct table_node {
+ const char *long_archi;
+ const char *short_archi;
+ int version;
+};
+
+
+/* support itanium as well */
+static const struct table_node archi_table[]= {
+
+ {"Windows 4.0", "WIN40", 0 },
+ {"Windows NT x86", "W32X86", 2 },
+ {"Windows NT x86", "W32X86", 3 },
+ {"Windows NT R4000", "W32MIPS", 2 },
+ {"Windows NT Alpha_AXP", "W32ALPHA", 2 },
+ {"Windows NT PowerPC", "W32PPC", 2 },
+ {"Windows IA64", "IA64", 3 },
+ {"Windows x64", "x64", 3 },
+ {NULL, "", -1 }
+};
+
+
+/**
+ * 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_driver_3(DRIVER_INFO_3 *i1)
+{
+ fstring name = "";
+ fstring architecture = "";
+ fstring driverpath = "";
+ fstring datafile = "";
+ fstring configfile = "";
+ fstring helpfile = "";
+ fstring dependentfiles = "";
+ fstring monitorname = "";
+ fstring defaultdatatype = "";
+
+ int length=0;
+ bool valid = true;
+
+ if (i1 == NULL)
+ return;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(architecture, i1->architecture.buffer, sizeof(architecture), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+ rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+ rpcstr_pull(monitorname, i1->monitorname.buffer, sizeof(monitorname), -1, STR_TERMINATE);
+ rpcstr_pull(defaultdatatype, i1->defaultdatatype.buffer, sizeof(defaultdatatype), -1, STR_TERMINATE);
+
+ d_printf ("Printer Driver Info 3:\n");
+ d_printf ("\tVersion: [%x]\n", i1->version);
+ d_printf ("\tDriver Name: [%s]\n",name);
+ d_printf ("\tArchitecture: [%s]\n", architecture);
+ d_printf ("\tDriver Path: [%s]\n", driverpath);
+ d_printf ("\tDatafile: [%s]\n", datafile);
+ d_printf ("\tConfigfile: [%s]\n", configfile);
+ d_printf ("\tHelpfile: [%s]\n\n", helpfile);
+
+ while (valid) {
+ rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+
+ length+=strlen(dependentfiles)+1;
+
+ if (strlen(dependentfiles) > 0) {
+ d_printf ("\tDependentfiles: [%s]\n", dependentfiles);
+ } else {
+ valid = false;
+ }
+ }
+
+ printf ("\n");
+
+ d_printf ("\tMonitorname: [%s]\n", monitorname);
+ d_printf ("\tDefaultdatatype: [%s]\n\n", defaultdatatype);
+
+ return;
+}
+
+static void display_reg_value(const char *subkey, REGISTRY_VALUE value)
+{
+ char *text;
+
+ switch(value.type) {
+ case REG_DWORD:
+ d_printf("\t[%s:%s]: REG_DWORD: 0x%08x\n", subkey, value.valuename,
+ *((uint32 *) value.data_p));
+ break;
+
+ case REG_SZ:
+ rpcstr_pull_talloc(talloc_tos(),
+ &text,
+ value.data_p,
+ value.size,
+ STR_TERMINATE);
+ if (!text) {
+ break;
+ }
+ d_printf("\t[%s:%s]: REG_SZ: %s\n", subkey, value.valuename, text);
+ break;
+
+ case REG_BINARY:
+ d_printf("\t[%s:%s]: REG_BINARY: unknown length value not displayed\n",
+ subkey, value.valuename);
+ break;
+
+ case REG_MULTI_SZ: {
+ uint32 i, num_values;
+ char **values;
+
+ if (!W_ERROR_IS_OK(reg_pull_multi_sz(NULL, value.data_p,
+ value.size, &num_values,
+ &values))) {
+ d_printf("reg_pull_multi_sz failed\n");
+ break;
+ }
+
+ for (i=0; i<num_values; i++) {
+ d_printf("%s\n", values[i]);
+ }
+ TALLOC_FREE(values);
+ break;
+ }
+
+ default:
+ d_printf("\t%s: unknown type %d\n", value.valuename, 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 = NT_STATUS_UNSUCCESSFUL;
+ int fnum_src = 0;
+ int fnum_dst = 0;
+ SEC_DESC *sd = NULL;
+ uint16 attr;
+ time_t f_atime, f_ctime, f_mtime;
+
+
+ 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));
+
+ fnum_src = cli_nt_create(cli_share_src, src_name, READ_CONTROL_ACCESS);
+ if (fnum_src == -1) {
+ DEBUGADD(0,("cannot open %s %s on originating server %s\n",
+ is_file?"file":"dir", src_name, cli_errstr(cli_share_src)));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+
+
+ if (copy_acls) {
+
+ /* get the security descriptor */
+ sd = cli_query_secdesc(cli_share_src, fnum_src, mem_ctx);
+ if (!sd) {
+ DEBUG(0,("failed to get security descriptor: %s\n",
+ cli_errstr(cli_share_src)));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+
+ if (c->opt_verbose && DEBUGLEVEL >= 3)
+ display_sec_desc(sd);
+ }
+
+
+ if (copy_attrs || copy_timestamps) {
+
+ /* get file attributes */
+ if (!cli_getattrE(cli_share_src, fnum_src, &attr, NULL,
+ &f_ctime, &f_atime, &f_mtime)) {
+ DEBUG(0,("failed to get file-attrs: %s\n",
+ cli_errstr(cli_share_src)));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+ }
+
+
+ /* open the file/dir on the destination server */
+
+ fnum_dst = cli_nt_create(cli_share_dst, dst_name, WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS);
+ if (fnum_dst == -1) {
+ DEBUG(0,("failed to open %s on the destination server: %s: %s\n",
+ is_file?"file":"dir", dst_name, cli_errstr(cli_share_dst)));
+ nt_status = cli_nt_error(cli_share_dst);
+ goto out;
+ }
+
+ if (copy_timestamps) {
+
+ /* set timestamps */
+ if (!cli_setattrE(cli_share_dst, fnum_dst, f_ctime, f_atime, f_mtime)) {
+ DEBUG(0,("failed to set file-attrs (timestamps): %s\n",
+ cli_errstr(cli_share_dst)));
+ nt_status = cli_nt_error(cli_share_dst);
+ goto out;
+ }
+ }
+
+ if (copy_acls) {
+
+ /* set acls */
+ if (!cli_set_secdesc(cli_share_dst, fnum_dst, sd)) {
+ DEBUG(0,("could not set secdesc on %s: %s\n",
+ dst_name, cli_errstr(cli_share_dst)));
+ nt_status = cli_nt_error(cli_share_dst);
+ goto out;
+ }
+ }
+
+ if (copy_attrs) {
+
+ /* set attrs */
+ if (!cli_setatr(cli_share_dst, dst_name, attr, 0)) {
+ DEBUG(0,("failed to set file-attrs: %s\n",
+ cli_errstr(cli_share_dst)));
+ nt_status = cli_nt_error(cli_share_dst);
+ goto out;
+ }
+ }
+
+
+ /* closing files */
+
+ if (!cli_close(cli_share_src, fnum_src)) {
+ d_fprintf(stderr, "could not close %s on originating server: %s\n",
+ is_file?"file":"dir", cli_errstr(cli_share_src));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+
+ if (!cli_close(cli_share_dst, fnum_dst)) {
+ d_fprintf(stderr, "could not close %s on destination server: %s\n",
+ is_file?"file":"dir", cli_errstr(cli_share_dst));
+ nt_status = cli_nt_error(cli_share_dst);
+ 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;
+ int fnum_src = 0;
+ int 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)
+ fnum_src = cli_open(cli_share_src, src_name, O_RDONLY, DENY_NONE);
+ else
+ fnum_src = cli_nt_create(cli_share_src, src_name, READ_CONTROL_ACCESS);
+
+ if (fnum_src == -1) {
+ DEBUGADD(0,("cannot open %s %s on originating server %s\n",
+ is_file ? "file":"dir",
+ src_name, cli_errstr(cli_share_src)));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+
+
+ if (is_file) {
+
+ /* open file on the destination server */
+ DEBUGADD(3,("opening file %s on destination server\n", dst_name));
+ fnum_dst = cli_open(cli_share_dst, dst_name,
+ O_RDWR|O_CREAT|O_TRUNC, DENY_NONE);
+
+ if (fnum_dst == -1) {
+ DEBUGADD(1,("cannot create file %s on destination server: %s\n",
+ dst_name, cli_errstr(cli_share_dst)));
+ nt_status = cli_nt_error(cli_share_dst);
+ 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",
+ cli_share_src->desthost, cli_share_src->share, src_name,
+ cli_share_dst->desthost, cli_share_dst->share, dst_name,
+ copy_acls ? "with" : "without",
+ copy_attrs ? "with" : "without",
+ copy_timestamps ? "(preserving timestamps)" : "" );
+ }
+
+
+ while (is_file) {
+
+ /* copying file */
+ int n, ret;
+ n = cli_read(cli_share_src, fnum_src, data, nread,
+ read_size);
+
+ if (n <= 0)
+ break;
+
+ ret = cli_write(cli_share_dst, fnum_dst, 0, data,
+ nread, n);
+
+ if (n != ret) {
+ d_fprintf(stderr, "Error writing file: %s\n",
+ cli_errstr(cli_share_dst));
+ nt_status = cli_nt_error(cli_share_dst);
+ goto out;
+ }
+
+ nread += n;
+ }
+
+
+ if (!is_file && !cli_chkpath(cli_share_dst, dst_name)) {
+
+ /* creating dir */
+ DEBUGADD(3,("creating dir %s on the destination server\n",
+ dst_name));
+
+ if (!cli_mkdir(cli_share_dst, dst_name)) {
+ DEBUG(0,("cannot create directory %s: %s\n",
+ dst_name, cli_errstr(cli_share_dst)));
+ nt_status = NT_STATUS_NO_SUCH_FILE;
+ }
+
+ if (!cli_chkpath(cli_share_dst, dst_name)) {
+ d_fprintf(stderr, "cannot check for directory %s: %s\n",
+ dst_name, cli_errstr(cli_share_dst));
+ goto out;
+ }
+ }
+
+
+ /* closing files */
+ if (!cli_close(cli_share_src, fnum_src)) {
+ d_fprintf(stderr, "could not close file on originating server: %s\n",
+ cli_errstr(cli_share_src));
+ nt_status = cli_nt_error(cli_share_src);
+ goto out;
+ }
+
+ if (is_file && !cli_close(cli_share_dst, fnum_dst)) {
+ d_fprintf(stderr, "could not close file on destination server: %s\n",
+ cli_errstr(cli_share_dst));
+ nt_status = cli_nt_error(cli_share_dst);
+ 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,
+ char *file, const char *short_archi) {
+
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ const char *p;
+ char *src_name;
+ char *dst_name;
+ char *version;
+ char *filename;
+ char *tok;
+
+ /* 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, "\\");
+ }
+ }
+
+ /* build source file name */
+ if (asprintf(&src_name, "\\%s\\%s\\%s", short_archi, version, filename) < 0 )
+ return NT_STATUS_NO_MEMORY;
+
+
+ /* create destination file name */
+ if (asprintf(&dst_name, "\\%s\\%s", short_archi, filename) < 0 )
+ return NT_STATUS_NO_MEMORY;
+
+
+ /* finally copy the file */
+ nt_status = net_copy_file(c, mem_ctx, cli_share_src, cli_share_dst,
+ src_name, dst_name, false, false, false, true);
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto out;
+
+ nt_status = NT_STATUS_OK;
+
+out:
+ SAFE_FREE(src_name);
+ SAFE_FREE(dst_name);
+
+ return nt_status;
+}
+
+/**
+ * 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));
+
+ if (!cli_mkdir(cli_share, dir)) {
+ DEBUG(1,("cannot create directory %s: %s\n",
+ dir, cli_errstr(cli_share)));
+ nt_status = NT_STATUS_NO_SUCH_FILE;
+ }
+
+ if (!cli_chkpath(cli_share, dir)) {
+ d_fprintf(stderr, "cannot check %s: %s\n",
+ dir, cli_errstr(cli_share));
+ 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, DRIVER_INFO_3 *i1)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ int length = 0;
+ bool valid = true;
+
+ fstring name = "";
+ fstring driverpath = "";
+ fstring datafile = "";
+ fstring configfile = "";
+ fstring helpfile = "";
+ fstring dependentfiles = "";
+
+ if (i1 == NULL)
+ return nt_status;
+
+ rpcstr_pull(name, i1->name.buffer, sizeof(name), -1, STR_TERMINATE);
+ rpcstr_pull(driverpath, i1->driverpath.buffer, sizeof(driverpath), -1, STR_TERMINATE);
+ rpcstr_pull(datafile, i1->datafile.buffer, sizeof(datafile), -1, STR_TERMINATE);
+ rpcstr_pull(configfile, i1->configfile.buffer, sizeof(configfile), -1, STR_TERMINATE);
+ rpcstr_pull(helpfile, i1->helpfile.buffer, sizeof(helpfile), -1, STR_TERMINATE);
+
+
+ if (c->opt_verbose)
+ d_printf("copying driver: [%s], for architecture: [%s], version: [%d]\n",
+ name, short_archi, i1->version);
+
+ nt_status = net_copy_driverfile(c, mem_ctx, cli_share_src, cli_share_dst,
+ driverpath, 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,
+ datafile, 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,
+ configfile, 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,
+ helpfile, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ while (valid) {
+
+ rpcstr_pull(dependentfiles, i1->dependentfiles+length, sizeof(dependentfiles), -1, STR_TERMINATE);
+ length += strlen(dependentfiles)+1;
+
+ if (strlen(dependentfiles) > 0) {
+
+ nt_status = net_copy_driverfile(c, mem_ctx,
+ cli_share_src, cli_share_dst,
+ dependentfiles, short_archi);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+ } else {
+ valid = false;
+ }
+ }
+
+ 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 flags,
+ uint32 level,
+ uint32 *num_printers,
+ PRINTER_INFO_CTR *ctr)
+{
+ WERROR result;
+
+ /* enum printers */
+ result = rpccli_spoolss_enum_printers(pipe_hnd, mem_ctx, name, flags,
+ level, num_printers, ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("cannot enum printers: %s\n", dos_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 access_required,
+ const char *username,
+ POLICY_HND *hnd)
+{
+ WERROR result;
+ fstring servername, printername2;
+
+ slprintf(servername, sizeof(servername)-1, "\\\\%s",
+ pipe_hnd->desthost);
+
+ fstrcpy(printername2, servername);
+ fstrcat(printername2, "\\");
+ fstrcat(printername2, printername);
+
+ DEBUG(10,("connecting to: %s as %s for %s and access: %x\n",
+ servername, username, printername2, access_required));
+
+ /* open printer */
+ result = rpccli_spoolss_open_printer_ex(pipe_hnd, mem_ctx, printername2,
+ "", access_required,
+ servername, username, 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, servername, username);
+ return false;
+ }
+
+ if (!W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "cannot open printer %s on server %s: %s\n",
+ printername2, servername, dos_errstr(result));
+ return false;
+ }
+
+ DEBUG(2,("got printer handle for printer: %s, server: %s\n",
+ printername2, servername));
+
+ return true;
+}
+
+static bool net_spoolss_getprinter(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ WERROR result;
+
+ /* getprinter call */
+ result = rpccli_spoolss_getprinter(pipe_hnd, mem_ctx, hnd, level, ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("cannot get printer-info: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_setprinter(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ uint32 level,
+ PRINTER_INFO_CTR *ctr)
+{
+ WERROR result;
+
+ /* setprinter call */
+ result = rpccli_spoolss_setprinter(pipe_hnd, mem_ctx, hnd, level, ctr, 0);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("cannot set printer-info: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_setprinterdata(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ REGISTRY_VALUE *value)
+{
+ WERROR result;
+
+ /* setprinterdata call */
+ result = rpccli_spoolss_setprinterdata(pipe_hnd, mem_ctx, hnd, value);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf ("unable to set printerdata: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_enumprinterkey(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ const char *keyname,
+ uint16 **keylist)
+{
+ WERROR result;
+
+ /* enumprinterkey call */
+ result = rpccli_spoolss_enumprinterkey(pipe_hnd, mem_ctx, hnd, keyname, keylist, NULL);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("enumprinterkey failed: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumprinterdataex(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ uint32 offered,
+ POLICY_HND *hnd,
+ const char *keyname,
+ REGVAL_CTR *ctr)
+{
+ WERROR result;
+
+ /* enumprinterdataex call */
+ result = rpccli_spoolss_enumprinterdataex(pipe_hnd, mem_ctx, hnd, keyname, ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("enumprinterdataex failed: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_setprinterdataex(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ char *keyname,
+ REGISTRY_VALUE *value)
+{
+ WERROR result;
+
+ /* setprinterdataex call */
+ result = rpccli_spoolss_setprinterdataex(pipe_hnd, mem_ctx, hnd,
+ keyname, value);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("could not set printerdataex: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumforms(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd,
+ int level,
+ uint32 *num_forms,
+ FORM_1 **forms)
+{
+ WERROR result;
+
+ /* enumforms call */
+ result = rpccli_spoolss_enumforms(pipe_hnd, mem_ctx, hnd, level, num_forms, forms);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("could not enum forms: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_enumprinterdrivers (struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ uint32 level, const char *env,
+ uint32 *num_drivers,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ WERROR result;
+
+ /* enumprinterdrivers call */
+ result = rpccli_spoolss_enumprinterdrivers(
+ pipe_hnd, mem_ctx, level,
+ env, num_drivers, ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ printf("cannot enum drivers: %s\n", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+static bool net_spoolss_getprinterdriver(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hnd, uint32 level,
+ const char *env, int version,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ WERROR result;
+
+ /* getprinterdriver call */
+ result = rpccli_spoolss_getprinterdriver(
+ pipe_hnd, mem_ctx, hnd, level,
+ env, version, ctr);
+
+ if (!W_ERROR_IS_OK(result)) {
+ DEBUG(1,("cannot get driver (for architecture: %s): %s\n",
+ env, dos_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", dos_errstr(result));
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool net_spoolss_addprinterdriver(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx, uint32 level,
+ PRINTER_DRIVER_CTR *ctr)
+{
+ WERROR result;
+
+ /* addprinterdriver call */
+ result = rpccli_spoolss_addprinterdriver(pipe_hnd, mem_ctx, level, ctr);
+
+ /* 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", dos_errstr(result));
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * abstraction function to get uint32 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 *num_printers,
+ PRINTER_INFO_CTR *ctr)
+{
+
+ POLICY_HND hnd;
+
+ /* 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, ctr))
+ 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,
+ pipe_hnd->auth->user_name,
+ &hnd))
+ return false;
+
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, ctr)) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd);
+ return false;
+ }
+
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd);
+
+ *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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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 i, num_printers;
+ uint32 level = 2;
+ char *printername, *sharename;
+ PRINTER_INFO_CTR ctr;
+
+ printf("listing printers\n");
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &ctr))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+ /* do some initialization */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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 i;
+ uint32 level = 3;
+ PRINTER_DRIVER_CTR drv_ctr_enum;
+ int d;
+
+ ZERO_STRUCT(drv_ctr_enum);
+
+ printf("listing printer-drivers\n");
+
+ for (i=0; archi_table[i].long_archi!=NULL; i++) {
+
+ uint32 num_drivers;
+
+ /* enum remote drivers */
+ if (!net_spoolss_enumprinterdrivers(pipe_hnd, mem_ctx, level,
+ archi_table[i].long_archi,
+ &num_drivers, &drv_ctr_enum)) {
+ 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_driver_3(&(drv_ctr_enum.info3[d]));
+ }
+ }
+
+ 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, destoyed on compleation 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 action)
+{
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32 i, num_printers;
+ uint32 level = 7;
+ char *printername, *sharename;
+ PRINTER_INFO_CTR ctr, ctr_pub;
+ POLICY_HND hnd;
+ bool got_hnd = false;
+ WERROR result;
+ const char *action_str;
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &ctr))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+ /* do some initialization */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ if (!printername || !sharename) {
+ goto done;
+ }
+
+ /* open printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, pipe_hnd->auth->user_name, &hnd))
+ goto done;
+
+ got_hnd = true;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &ctr_pub))
+ goto done;
+
+ /* check action and set string */
+ switch (action) {
+ case SPOOL_DS_PUBLISH:
+ action_str = "published";
+ break;
+ case SPOOL_DS_UPDATE:
+ action_str = "updated";
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ action_str = "unpublished";
+ break;
+ default:
+ action_str = "unknown action";
+ printf("unkown action: %d\n", action);
+ break;
+ }
+
+ ctr_pub.printers_7->action = action;
+
+ result = rpccli_spoolss_setprinter(pipe_hnd, mem_ctx, &hnd, level, &ctr_pub, 0);
+ if (!W_ERROR_IS_OK(result) && (W_ERROR_V(result) != W_ERROR_V(WERR_IO_PENDING))) {
+ printf("cannot set printer-info: %s\n", dos_errstr(result));
+ goto done;
+ }
+
+ printf("successfully %s printer %s in Active Directory\n", action_str, sharename);
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (got_hnd)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd);
+
+ return nt_status;
+}
+
+NTSTATUS rpc_printer_publish_publish_internals(struct net_context *c,
+ const DOM_SID *domain_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, SPOOL_DS_PUBLISH);
+}
+
+NTSTATUS rpc_printer_publish_unpublish_internals(struct net_context *c,
+ const DOM_SID *domain_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, SPOOL_DS_UNPUBLISH);
+}
+
+NTSTATUS rpc_printer_publish_update_internals(struct net_context *c,
+ const DOM_SID *domain_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, SPOOL_DS_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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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 i, num_printers;
+ uint32 level = 7;
+ char *printername, *sharename;
+ char *guid;
+ PRINTER_INFO_CTR ctr, ctr_pub;
+ POLICY_HND hnd;
+ bool got_hnd = false;
+ int state;
+
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &ctr))
+ return nt_status;
+
+ for (i = 0; i < num_printers; i++) {
+ ZERO_STRUCT(ctr_pub);
+
+ /* do some initialization */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ if (!printername || !sharename) {
+ goto done;
+ }
+
+ /* open printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, cli->user_name, &hnd))
+ goto done;
+
+ got_hnd = true;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd, level, &ctr_pub))
+ goto done;
+
+ rpcstr_pull_talloc(mem_ctx,
+ &guid,
+ ctr_pub.printers_7->guid.buffer,
+ -1,
+ STR_TERMINATE);
+ if (!guid) {
+ goto done;
+ }
+ state = ctr_pub.printers_7->action;
+ switch (state) {
+ case SPOOL_DS_PUBLISH:
+ printf("printer [%s] is published", sharename);
+ if (c->opt_verbose)
+ printf(", guid: %s", guid);
+ printf("\n");
+ break;
+ case SPOOL_DS_UNPUBLISH:
+ printf("printer [%s] is unpublished\n", sharename);
+ break;
+ case SPOOL_DS_UPDATE:
+ printf("printer [%s] is currently updating\n", sharename);
+ break;
+ default:
+ printf("unkown state: %d\n", state);
+ break;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (got_hnd)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd);
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ /* 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 i = 0;
+ uint32 num_printers;
+ uint32 level = 2;
+ char *printername, *sharename;
+ bool got_hnd_src = false;
+ bool got_hnd_dst = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ POLICY_HND hnd_src, hnd_dst;
+ PRINTER_INFO_CTR ctr_src, ctr_dst, ctr_enum;
+ struct cli_state *cli_dst = NULL;
+
+ ZERO_STRUCT(ctr_src);
+
+ DEBUG(3,("copying printer ACLs\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &syntax_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+
+ /* enum source printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &ctr_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 */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr_enum.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr_enum.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ 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, cli->user_name, &hnd_src))
+ goto done;
+
+ got_hnd_src = true;
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, cli_dst->user_name, &hnd_dst))
+ goto done;
+
+ got_hnd_dst = true;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &ctr_dst))
+ goto done;
+
+ /* check for existing src printer */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, 3, &ctr_src))
+ goto done;
+
+ /* Copy Security Descriptor */
+
+ /* copy secdesc (info level 2) */
+ ctr_dst.printers_2->devmode = NULL;
+ ctr_dst.printers_2->secdesc = dup_sec_desc(mem_ctx, ctr_src.printers_3->secdesc);
+
+ if (c->opt_verbose)
+ display_sec_desc(ctr_dst.printers_2->secdesc);
+
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &ctr_dst))
+ goto done;
+
+ DEBUGADD(1,("\tSetPrinter of SECDESC succeeded\n"));
+
+
+ /* close printer handles here */
+ if (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ got_hnd_src = false;
+ }
+
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ }
+
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ }
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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;
+ WERROR result;
+ uint32 i, f;
+ uint32 num_printers;
+ uint32 level = 1;
+ char *printername, *sharename;
+ bool got_hnd_src = false;
+ bool got_hnd_dst = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ POLICY_HND hnd_src, hnd_dst;
+ PRINTER_INFO_CTR ctr_enum, ctr_dst;
+ uint32 num_forms;
+ FORM_1 *forms;
+ struct cli_state *cli_dst = NULL;
+
+ ZERO_STRUCT(ctr_enum);
+
+ DEBUG(3,("copying forms\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &syntax_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* enum src printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, 2, argc, argv, &num_printers, &ctr_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 */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr_enum.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr_enum.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ 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, cli->user_name, &hnd_src))
+ goto done;
+
+ got_hnd_src = true;
+
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, cli->user_name, &hnd_dst))
+ goto done;
+
+ got_hnd_dst = true;
+
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &ctr_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++) {
+
+ FORM form;
+ fstring form_name;
+
+ /* only migrate FORM_PRINTER types, according to jerry
+ FORM_BUILTIN-types are hard-coded in samba */
+ if (forms[f].flag != FORM_PRINTER)
+ continue;
+
+ if (forms[f].name.buffer)
+ rpcstr_pull(form_name, forms[f].name.buffer,
+ sizeof(form_name), -1, STR_TERMINATE);
+
+ if (c->opt_verbose)
+ d_printf("\tmigrating form # %d [%s] of type [%d]\n",
+ f, form_name, forms[f].flag);
+
+ /* is there a more elegant way to do that ? */
+ form.flags = FORM_PRINTER;
+ form.size_x = forms[f].width;
+ form.size_y = forms[f].length;
+ form.left = forms[f].left;
+ form.top = forms[f].top;
+ form.right = forms[f].right;
+ form.bottom = forms[f].bottom;
+
+ init_unistr2(&form.name, form_name, UNI_STR_TERMINATE);
+
+ /* FIXME: there might be something wrong with samba's
+ builtin-forms */
+ result = rpccli_spoolss_addform(pipe_hnd_dst, mem_ctx,
+ &hnd_dst, 1, &form);
+ if (!W_ERROR_IS_OK(result)) {
+ d_printf("\tAddForm form %d: [%s] refused.\n",
+ f, form_name);
+ continue;
+ }
+
+ DEBUGADD(1,("\tAddForm of [%s] succeeded\n", form_name));
+ }
+
+
+ /* close printer handles here */
+ if (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ got_hnd_src = false;
+ }
+
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (got_hnd_src)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+
+ if (got_hnd_dst)
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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 i, p;
+ uint32 num_printers;
+ uint32 level = 3;
+ char *printername, *sharename;
+ bool got_hnd_src = false;
+ bool got_hnd_dst = false;
+ bool got_src_driver_share = false;
+ bool got_dst_driver_share = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ POLICY_HND hnd_src, hnd_dst;
+ PRINTER_DRIVER_CTR drv_ctr_src, drv_ctr_dst;
+ PRINTER_INFO_CTR info_ctr_enum, info_ctr_dst;
+ struct cli_state *cli_dst = NULL;
+ struct cli_state *cli_share_src = NULL;
+ struct cli_state *cli_share_dst = NULL;
+ fstring drivername = "";
+
+ ZERO_STRUCT(drv_ctr_src);
+ ZERO_STRUCT(drv_ctr_dst);
+ ZERO_STRUCT(info_ctr_enum);
+ ZERO_STRUCT(info_ctr_dst);
+
+ DEBUG(3,("copying printer-drivers\n"));
+
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &syntax_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* open print$-share on the src server */
+ nt_status = connect_to_service(c, &cli_share_src, &cli->dest_ss,
+ cli->desthost, "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, &cli_dst->dest_ss,
+ cli_dst->desthost, "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_ctr_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 */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ info_ctr_enum.printers_2[p].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ info_ctr_enum.printers_2[p].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ 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, cli->user_name, &hnd_dst))
+ goto done;
+
+ got_hnd_dst = true;
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_ctr_dst))
+ goto done;
+
+
+ /* open src printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd, mem_ctx, sharename,
+ MAXIMUM_ALLOWED_ACCESS,
+ pipe_hnd->auth->user_name,
+ &hnd_src))
+ goto done;
+
+ got_hnd_src = true;
+
+
+ /* 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_ctr_src))
+ continue;
+
+ rpcstr_pull(drivername, drv_ctr_src.info3->name.buffer,
+ sizeof(drivername), -1, STR_TERMINATE);
+
+ if (c->opt_verbose)
+ display_print_driver_3(drv_ctr_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_ctr_src.info3);
+ if (!NT_STATUS_IS_OK(nt_status))
+ goto done;
+
+
+ /* adddriver dst */
+ if (!net_spoolss_addprinterdriver(pipe_hnd_dst, mem_ctx, level, &drv_ctr_src)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUGADD(1,("Sucessfully added driver [%s] for printer [%s]\n",
+ drivername, printername));
+
+ }
+
+ if (strlen(drivername) == 0) {
+ DEBUGADD(1,("Did not get driver for printer %s\n",
+ printername));
+ goto done;
+ }
+
+ /* setdriver dst */
+ init_unistr(&info_ctr_dst.printers_2->drivername, drivername);
+
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 2, &info_ctr_dst)) {
+ nt_status = NT_STATUS_UNSUCCESSFUL;
+ goto done;
+ }
+
+ DEBUGADD(1,("Sucessfully set driver %s for printer %s\n",
+ drivername, printername));
+
+ /* close dst */
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+
+ /* close src */
+ if (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ got_hnd_src = false;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+
+ if (got_hnd_src)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+
+ if (got_hnd_dst)
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_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;
+ uint32 i = 0, num_printers;
+ uint32 level = 2;
+ PRINTER_INFO_CTR ctr_src, ctr_dst, ctr_enum;
+ struct cli_state *cli_dst = NULL;
+ POLICY_HND hnd_dst, hnd_src;
+ char *printername, *sharename;
+ bool got_hnd_src = false;
+ bool got_hnd_dst = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+
+ DEBUG(3,("copying printers\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &syntax_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* enum printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &ctr_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 */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr_enum.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr_enum.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ 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, cli->user_name, &hnd_dst)) {
+
+ DEBUG(1,("could not open printer: %s\n", sharename));
+ } else {
+ got_hnd_dst = true;
+ }
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, level, &ctr_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 (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+ 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, cli->user_name, &hnd_src))
+ goto done;
+
+ got_hnd_src = true;
+
+ /* getprinter on the src server */
+ if (!net_spoolss_getprinter(pipe_hnd, mem_ctx, &hnd_src, level, &ctr_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);
+ result = rpccli_spoolss_addprinterex (pipe_hnd_dst, mem_ctx, level, &ctr_src);
+
+ 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 (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ got_hnd_src = false;
+ }
+
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ if (got_hnd_src)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+
+ if (got_hnd_dst)
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+
+ 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 aquired from the remote server
+ * @param cli A cli_state connected to the server.
+ * @param mem_ctx Talloc context, destoyed on compleation 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+
+ /* FIXME: Here the nightmare begins */
+
+ WERROR result;
+ NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
+ uint32 i = 0, p = 0, j = 0;
+ uint32 num_printers, val_needed, data_needed;
+ uint32 level = 2;
+ char *printername, *sharename;
+ bool got_hnd_src = false;
+ bool got_hnd_dst = false;
+ struct rpc_pipe_client *pipe_hnd_dst = NULL;
+ POLICY_HND hnd_src, hnd_dst;
+ PRINTER_INFO_CTR ctr_enum, ctr_dst, ctr_dst_publish;
+ REGVAL_CTR *reg_ctr;
+ struct cli_state *cli_dst = NULL;
+ char *devicename = NULL, *unc_name = NULL, *url = NULL;
+ const char *longname;
+
+ uint16 *keylist = NULL, *curkey;
+
+ ZERO_STRUCT(ctr_enum);
+
+ DEBUG(3,("copying printer settings\n"));
+
+ /* connect destination PI_SPOOLSS */
+ nt_status = connect_dst_pipe(c, &cli_dst, &pipe_hnd_dst,
+ &syntax_spoolss);
+ if (!NT_STATUS_IS_OK(nt_status))
+ return nt_status;
+
+ /* enum src printers */
+ if (!get_printer_info(pipe_hnd, mem_ctx, level, argc, argv, &num_printers, &ctr_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++) {
+ /* do some initialization */
+ rpcstr_pull_talloc(mem_ctx,
+ &printername,
+ ctr_enum.printers_2[i].printername.buffer,
+ -1,
+ STR_TERMINATE);
+ rpcstr_pull_talloc(mem_ctx,
+ &sharename,
+ ctr_enum.printers_2[i].sharename.buffer,
+ -1,
+ STR_TERMINATE);
+ 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, cli->user_name, &hnd_src))
+ goto done;
+
+ got_hnd_src = true;
+
+
+ /* open dst printer handle */
+ if (!net_spoolss_open_printer_ex(pipe_hnd_dst, mem_ctx, sharename,
+ PRINTER_ALL_ACCESS, cli_dst->user_name, &hnd_dst))
+ goto done;
+
+ got_hnd_dst = true;
+
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ level, &ctr_dst))
+ goto done;
+
+
+ /* STEP 1: COPY DEVICE-MODE and other
+ PRINTER_INFO_2-attributes
+ */
+
+ ctr_dst.printers_2 = &ctr_enum.printers_2[i];
+
+ /* why is the port always disconnected when the printer
+ is correctly installed (incl. driver ???) */
+ init_unistr( &ctr_dst.printers_2->portname, SAMBA_PRINTER_PORT_NAME);
+
+ /* check if printer is published */
+ if (ctr_enum.printers_2[i].attributes & PRINTER_ATTRIBUTE_PUBLISHED) {
+
+ /* check for existing dst printer */
+ if (!net_spoolss_getprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &ctr_dst_publish))
+ goto done;
+
+ ctr_dst_publish.printers_7->action = SPOOL_DS_PUBLISH;
+
+ /* ignore false from setprinter due to WERR_IO_PENDING */
+ net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst, 7, &ctr_dst_publish);
+
+ DEBUG(3,("republished printer\n"));
+ }
+
+ if (ctr_enum.printers_2[i].devmode != NULL) {
+
+ /* copy devmode (info level 2) */
+ ctr_dst.printers_2->devmode = (DEVICEMODE *)
+ TALLOC_MEMDUP(mem_ctx,
+ ctr_enum.printers_2[i].devmode,
+ sizeof(DEVICEMODE));
+
+ /* do not copy security descriptor (we have another
+ * command for that) */
+ ctr_dst.printers_2->secdesc = NULL;
+
+#if 0
+ if (asprintf(&devicename, "\\\\%s\\%s", longname,
+ printername) < 0) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ init_unistr(&ctr_dst.printers_2->devmode->devicename,
+ devicename);
+#endif
+ if (!net_spoolss_setprinter(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ level, &ctr_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
+ */
+
+ /* enumerate data on src handle */
+ result = rpccli_spoolss_enumprinterdata(pipe_hnd, mem_ctx, &hnd_src, p, 0, 0,
+ &val_needed, &data_needed, NULL);
+
+ /* loop for all printerdata of "PrinterDriverData" */
+ while (W_ERROR_IS_OK(result)) {
+
+ REGISTRY_VALUE value;
+
+ result = rpccli_spoolss_enumprinterdata(
+ pipe_hnd, mem_ctx, &hnd_src, p++, val_needed,
+ data_needed, 0, 0, &value);
+
+ /* loop for all reg_keys */
+ if (W_ERROR_IS_OK(result)) {
+
+ /* display_value */
+ if (c->opt_verbose)
+ display_reg_value(SPOOL_PRINTERDATA_KEY, value);
+
+ /* set_value */
+ if (!net_spoolss_setprinterdata(pipe_hnd_dst, mem_ctx,
+ &hnd_dst, &value))
+ goto done;
+
+ DEBUGADD(1,("\tSetPrinterData of [%s] succeeded\n",
+ value.valuename));
+ }
+ }
+
+ /* 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;
+
+ curkey = keylist;
+ while (*curkey != 0) {
+ char *subkey;
+ rpcstr_pull_talloc(mem_ctx,
+ &subkey,
+ curkey,
+ -1,
+ STR_TERMINATE);
+ if (!subkey) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ curkey += strlen(subkey) + 1;
+
+ if ( !(reg_ctr = TALLOC_ZERO_P( mem_ctx, REGVAL_CTR )) )
+ return NT_STATUS_NO_MEMORY;
+
+ /* enumerate all src subkeys */
+ if (!net_spoolss_enumprinterdataex(pipe_hnd, mem_ctx, 0,
+ &hnd_src, subkey,
+ reg_ctr))
+ goto done;
+
+ for (j=0; j < reg_ctr->num_values; j++) {
+
+ REGISTRY_VALUE value;
+ UNISTR2 data;
+
+ /* although samba replies with sane data in most cases we
+ should try to avoid writing wrong registry data */
+
+ if (strequal(reg_ctr->values[j]->valuename, SPOOL_REG_PORTNAME) ||
+ strequal(reg_ctr->values[j]->valuename, SPOOL_REG_UNCNAME) ||
+ strequal(reg_ctr->values[j]->valuename, SPOOL_REG_URL) ||
+ strequal(reg_ctr->values[j]->valuename, SPOOL_REG_SHORTSERVERNAME) ||
+ strequal(reg_ctr->values[j]->valuename, SPOOL_REG_SERVERNAME)) {
+
+ if (strequal(reg_ctr->values[j]->valuename, SPOOL_REG_PORTNAME)) {
+
+ /* although windows uses a multi-sz, we use a sz */
+ init_unistr2(&data, SAMBA_PRINTER_PORT_NAME, UNI_STR_TERMINATE);
+ fstrcpy(value.valuename, SPOOL_REG_PORTNAME);
+ }
+
+ if (strequal(reg_ctr->values[j]->valuename, SPOOL_REG_UNCNAME)) {
+
+ if (asprintf(&unc_name, "\\\\%s\\%s", longname, sharename) < 0) {
+ nt_status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ init_unistr2(&data, unc_name, UNI_STR_TERMINATE);
+ fstrcpy(value.valuename, SPOOL_REG_UNCNAME);
+ }
+
+ if (strequal(reg_ctr->values[j]->valuename, 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;
+ }
+ init_unistr2(&data, url, UNI_STR_TERMINATE);
+ fstrcpy(value.valuename, SPOOL_REG_URL);
+#endif
+ }
+
+ if (strequal(reg_ctr->values[j]->valuename, SPOOL_REG_SERVERNAME)) {
+
+ init_unistr2(&data, longname, UNI_STR_TERMINATE);
+ fstrcpy(value.valuename, SPOOL_REG_SERVERNAME);
+ }
+
+ if (strequal(reg_ctr->values[j]->valuename, SPOOL_REG_SHORTSERVERNAME)) {
+
+ init_unistr2(&data, global_myname(), UNI_STR_TERMINATE);
+ fstrcpy(value.valuename, SPOOL_REG_SHORTSERVERNAME);
+ }
+
+ value.type = REG_SZ;
+ value.size = data.uni_str_len * 2;
+ if (value.size) {
+ value.data_p = (uint8 *)TALLOC_MEMDUP(mem_ctx, data.buffer, value.size);
+ } else {
+ value.data_p = NULL;
+ }
+
+ if (c->opt_verbose)
+ display_reg_value(subkey, 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))
+ goto done;
+
+ } else {
+
+ if (c->opt_verbose)
+ display_reg_value(subkey, *(reg_ctr->values[j]));
+
+ /* here we have to set all subkeys on the dst server */
+ if (!net_spoolss_setprinterdataex(pipe_hnd_dst, mem_ctx, &hnd_dst,
+ subkey, reg_ctr->values[j]))
+ goto done;
+
+ }
+
+ DEBUGADD(1,("\tSetPrinterDataEx of key [%s\\%s] succeeded\n",
+ subkey, reg_ctr->values[j]->valuename));
+
+ }
+
+ TALLOC_FREE( reg_ctr );
+ }
+
+ safe_free(keylist);
+
+ /* close printer handles here */
+ if (got_hnd_src) {
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+ got_hnd_src = false;
+ }
+
+ if (got_hnd_dst) {
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+ got_hnd_dst = false;
+ }
+
+ }
+
+ nt_status = NT_STATUS_OK;
+
+done:
+ SAFE_FREE(devicename);
+ SAFE_FREE(url);
+ SAFE_FREE(unc_name);
+
+ if (got_hnd_src)
+ rpccli_spoolss_close_printer(pipe_hnd, mem_ctx, &hnd_src);
+
+ if (got_hnd_dst)
+ rpccli_spoolss_close_printer(pipe_hnd_dst, mem_ctx, &hnd_dst);
+
+ 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 0000000000..124af00b57
--- /dev/null
+++ b/source3/utils/net_rpc_registry.c
@@ -0,0 +1,1311 @@
+/*
+ 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 "utils/net.h"
+#include "utils/net_registry_util.h"
+#include "regfio.h"
+#include "reg_objects.h"
+
+static bool reg_hive_key(TALLOC_CTX *ctx, const char *fullname,
+ uint32 *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 access_mask,
+ struct policy_handle *hive_hnd,
+ struct policy_handle *key_hnd)
+{
+ uint32 hive;
+ NTSTATUS status;
+ struct winreg_String key;
+
+ ZERO_STRUCT(key);
+
+ if (!reg_hive_key(mem_ctx, name, &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_winreg_Connect(pipe_hnd, mem_ctx, hive, access_mask,
+ hive_hnd);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+
+ status = rpccli_winreg_OpenKey(pipe_hnd, mem_ctx, hive_hnd, key, 0,
+ access_mask, key_hnd, NULL);
+ if (!(NT_STATUS_IS_OK(status))) {
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, hive_hnd, NULL);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS registry_enumkeys(TALLOC_CTX *ctx,
+ struct rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ uint32 *pnum_keys, char ***pnames,
+ char ***pclasses, NTTIME ***pmodtimes)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ uint32 num_subkeys, max_subkeylen, max_classlen;
+ uint32 num_values, max_valnamelen, max_valbufsize;
+ uint32 i;
+ NTTIME last_changed_time;
+ uint32 secdescsize;
+ struct winreg_String classname;
+ char **names, **classes;
+ NTTIME **modtimes;
+
+ if (!(mem_ctx = talloc_new(ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+ status = rpccli_winreg_QueryInfoKey(
+ pipe_hnd, mem_ctx, key_hnd, &classname, &num_subkeys,
+ &max_subkeylen, &max_classlen, &num_values, &max_valnamelen,
+ &max_valbufsize, &secdescsize, &last_changed_time, NULL );
+
+ if (!NT_STATUS_IS_OK(status)) {
+ 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;
+ WERROR werr;
+
+ 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 = rpccli_winreg_EnumKey(pipe_hnd, mem_ctx, key_hnd,
+ i, &name_buf, &class_buf,
+ &modtime, &werr);
+
+ if (W_ERROR_EQUAL(werr,
+ WERR_NO_MORE_ITEMS) ) {
+ status = NT_STATUS_OK;
+ break;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ 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 *pnum_values, char ***pvalnames,
+ struct registry_value ***pvalues)
+{
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ uint32 num_subkeys, max_subkeylen, max_classlen;
+ uint32 num_values, max_valnamelen, max_valbufsize;
+ uint32 i;
+ NTTIME last_changed_time;
+ uint32 secdescsize;
+ struct winreg_String classname;
+ struct registry_value **values;
+ char **names;
+
+ if (!(mem_ctx = talloc_new(ctx))) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ZERO_STRUCT(classname);
+ status = rpccli_winreg_QueryInfoKey(
+ pipe_hnd, mem_ctx, key_hnd, &classname, &num_subkeys,
+ &max_subkeylen, &max_classlen, &num_values, &max_valnamelen,
+ &max_valbufsize, &secdescsize, &last_changed_time, NULL );
+
+ if (!NT_STATUS_IS_OK(status)) {
+ 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 *data = NULL;
+ uint32 data_size;
+ uint32 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 *)TALLOC(mem_ctx, data_size);
+ value_length = 0;
+
+ status = rpccli_winreg_EnumValue(pipe_hnd, mem_ctx, key_hnd,
+ i, &name_buf, &type,
+ data, &data_size,
+ &value_length, &err);
+
+ if ( W_ERROR_EQUAL(err,
+ WERR_NO_MORE_ITEMS) ) {
+ status = NT_STATUS_OK;
+ break;
+ }
+
+ if (!(NT_STATUS_IS_OK(status))) {
+ 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;
+ }
+
+ err = registry_pull_value(values, &values[i], type, data,
+ data_size, value_length);
+ if (!W_ERROR_IS_OK(err)) {
+ status = werror_to_ntstatus(err);
+ 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 rpc_pipe_client *pipe_hnd,
+ struct policy_handle *key_hnd,
+ uint32_t sec_info,
+ struct KeySecurityData *sd)
+{
+ return rpccli_winreg_GetKeySecurity(pipe_hnd, mem_ctx, key_hnd,
+ sec_info, sd, NULL);
+}
+
+
+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;
+ DATA_BLOB blob;
+ NTSTATUS result;
+ WERROR err;
+
+ err = registry_push_value(mem_ctx, value, &blob);
+ if (!W_ERROR_IS_OK(err)) {
+ return werror_to_ntstatus(err);
+ }
+
+ ZERO_STRUCT(name_string);
+
+ name_string.name = name;
+ result = rpccli_winreg_SetValue(pipe_hnd, blob.data, key_hnd,
+ name_string, value->type,
+ blob.data, blob.length, NULL);
+ TALLOC_FREE(blob.data);
+ return result;
+}
+
+static NTSTATUS rpc_registry_setvalue_internal(struct net_context *c,
+ const DOM_SID *domain_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;
+ struct registry_value value;
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0],
+ SEC_RIGHTS_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")) {
+ value.type = REG_DWORD;
+ value.v.dword = strtoul(argv[3], NULL, 10);
+ }
+ else if (strequal(argv[2], "sz")) {
+ value.type = REG_SZ;
+ value.v.sz.len = strlen(argv[3])+1;
+ value.v.sz.str = CONST_DISCARD(char *, argv[3]);
+ }
+ 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:
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &key_hnd, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &hive_hnd, NULL);
+
+ 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, "usage: net rpc registry setvalue <key> "
+ "<valuename> <type> [<val>]+\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_setvalue_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_deletevalue_internal(struct net_context *c,
+ const DOM_SID *domain_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;
+ struct winreg_String valuename;
+
+ ZERO_STRUCT(valuename);
+
+ status = registry_openkey(mem_ctx, pipe_hnd, argv[0],
+ SEC_RIGHTS_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 = rpccli_winreg_DeleteValue(pipe_hnd, mem_ctx, &key_hnd,
+ valuename, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "registry_deletevalue failed: %s\n",
+ nt_errstr(status));
+ }
+
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &key_hnd, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &hive_hnd, NULL);
+
+ 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, "usage: net rpc registry deletevalue <key> "
+ "<valuename>\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_deletevalue_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_getvalue_internal(struct net_context *c,
+ const 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;
+ uint8_t *data = NULL;
+ uint32_t data_size = 0;
+ uint32_t value_length = 0;
+ TALLOC_CTX *tmp_ctx = talloc_stackframe();
+
+ ZERO_STRUCT(valuename);
+
+ status = registry_openkey(tmp_ctx, pipe_hnd, argv[0],
+ SEC_RIGHTS_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];
+
+ /*
+ * call QueryValue once with data == NULL to get the
+ * needed memory size to be allocated, then allocate
+ * data buffer and call again.
+ */
+ status = rpccli_winreg_QueryValue(pipe_hnd, tmp_ctx, &key_hnd,
+ &valuename,
+ &type,
+ data,
+ &data_size,
+ &value_length,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "registry_queryvalue failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ data = (uint8 *)TALLOC(tmp_ctx, data_size);
+ value_length = 0;
+
+ status = rpccli_winreg_QueryValue(pipe_hnd, tmp_ctx, &key_hnd,
+ &valuename,
+ &type,
+ data,
+ &data_size,
+ &value_length,
+ NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "registry_queryvalue failed: %s\n",
+ nt_errstr(status));
+ goto done;
+ }
+
+ werr = registry_pull_value(tmp_ctx, &value, type, data,
+ data_size, value_length);
+ if (!W_ERROR_IS_OK(werr)) {
+ status = werror_to_ntstatus(werr);
+ goto done;
+ }
+
+ print_registry_value(value, raw);
+
+done:
+ rpccli_winreg_CloseKey(pipe_hnd, tmp_ctx, &key_hnd, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, tmp_ctx, &hive_hnd, NULL);
+
+ TALLOC_FREE(tmp_ctx);
+
+ return status;
+}
+
+static NTSTATUS rpc_registry_getvalue_full(struct net_context *c,
+ const DOM_SID *domain_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, "usage: net rpc registry getvalue <key> "
+ "<valuename>\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_getvalue_full, argc, argv);
+}
+
+static NTSTATUS rpc_registry_getvalue_raw(struct net_context *c,
+ const DOM_SID *domain_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, "usage: net rpc registry getvalue <key> "
+ "<valuename>\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_getvalue_raw, argc, argv);
+}
+
+static NTSTATUS rpc_registry_createkey_internal(struct net_context *c,
+ const DOM_SID *domain_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 hive;
+ struct policy_handle hive_hnd, key_hnd;
+ struct winreg_String key, keyclass;
+ enum winreg_CreateAction action;
+ NTSTATUS status;
+
+ ZERO_STRUCT(key);
+ ZERO_STRUCT(keyclass);
+
+ if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_winreg_Connect(pipe_hnd, mem_ctx, hive,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &hive_hnd);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+
+ action = REG_ACTION_NONE;
+ keyclass.name = "";
+
+ status = rpccli_winreg_CreateKey(pipe_hnd, mem_ctx, &hive_hnd, key,
+ keyclass, 0, REG_KEY_READ, NULL,
+ &key_hnd, &action, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "createkey returned %s\n",
+ nt_errstr(status));
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &hive_hnd, NULL);
+ return status;
+ }
+
+ 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;
+ }
+
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &key_hnd, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &hive_hnd, NULL);
+
+ 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, "usage: net rpc registry createkey <key>\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_createkey_internal, argc, argv );
+}
+
+static NTSTATUS rpc_registry_deletekey_internal(struct net_context *c,
+ const DOM_SID *domain_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 hive;
+ struct policy_handle hive_hnd;
+ struct winreg_String key;
+ NTSTATUS status;
+
+ ZERO_STRUCT(key);
+
+ if (!reg_hive_key(mem_ctx, argv[0], &hive, &key.name)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = rpccli_winreg_Connect(pipe_hnd, mem_ctx, hive,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &hive_hnd);
+ if (!(NT_STATUS_IS_OK(status))) {
+ return status;
+ }
+
+ status = rpccli_winreg_DeleteKey(pipe_hnd, mem_ctx, &hive_hnd, key, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &hive_hnd, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "deletekey returned %s\n",
+ nt_errstr(status));
+ }
+
+ 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, "usage: net rpc registry deletekey <key>\n");
+ return -1;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_winreg.syntax_id, 0,
+ rpc_registry_deletekey_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_registry_enumerate_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND pol_hive, pol_key;
+ NTSTATUS status;
+ uint32 num_subkeys = 0;
+ uint32 num_values = 0;
+ char **names = NULL, **classes = NULL;
+ NTTIME **modtimes = NULL;
+ uint32 i;
+ struct registry_value **values = NULL;
+
+ if (argc != 1 || c->display_usage) {
+ d_printf("Usage: net rpc registry enumerate <path>\n");
+ d_printf("Example: net rpc registry enumerate 'HKLM\\Software\\Samba'\n");
+ 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]);
+ }
+
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_key, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_hive, NULL);
+
+ 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.syntax_id, 0,
+ rpc_registry_enumerate_internal, argc, argv );
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_registry_save_internal(struct net_context *c,
+ const DOM_SID *domain_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_GENERAL_FAILURE;
+ POLICY_HND pol_hive, pol_key;
+ NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
+ struct winreg_String filename;
+
+ if (argc != 2 || c->display_usage) {
+ d_printf("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 = rpccli_winreg_SaveKey( pipe_hnd, mem_ctx, &pol_key, &filename, NULL, NULL);
+ if ( !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Unable to save [%s] to %s:%s\n", argv[0], cli->desthost, argv[1]);
+ }
+
+ /* cleanup */
+
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_key, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_hive, NULL);
+
+ 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.syntax_id, 0,
+ rpc_registry_save_internal, argc, argv );
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static void dump_values( REGF_NK_REC *nk )
+{
+ int i, j;
+ char *data_str = NULL;
+ uint32 data_size, data;
+
+ 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) ", reg_type_lookup( nk->values[i].type ) );
+
+ data_size = nk->values[i].data_size & ~VK_DATA_IN_OFFSET;
+ switch ( nk->values[i].type ) {
+ case REG_SZ:
+ rpcstr_pull_talloc(talloc_tos(),
+ &data_str,
+ nk->values[i].data,
+ -1,
+ STR_TERMINATE);
+ if (!data_str) {
+ break;
+ }
+ d_printf( "%s", data_str );
+ break;
+ case REG_MULTI_SZ:
+ case REG_EXPAND_SZ:
+ for ( j=0; j<data_size; j++ ) {
+ d_printf( "%c", nk->values[i].data[j] );
+ }
+ break;
+ case REG_DWORD:
+ data = IVAL( nk->values[i].data, 0 );
+ d_printf("0x%x", data );
+ break;
+ case REG_BINARY:
+ for ( j=0; j<data_size; j++ ) {
+ d_printf( "%x", nk->values[i].data[j] );
+ }
+ break;
+ default:
+ d_printf("unknown");
+ break;
+ }
+
+ d_printf( "\n" );
+ }
+
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool dump_registry_tree( REGF_FILE *file, REGF_NK_REC *nk, const char *parent )
+{
+ REGF_NK_REC *key;
+
+ /* depth first dump of the registry tree */
+
+ while ( (key = regfio_fetch_subkey( file, nk )) ) {
+ char *regpath;
+ if (asprintf(&regpath, "%s\\%s", parent, key->keyname) < 0) {
+ break;
+ }
+ d_printf("[%s]\n", regpath );
+ dump_values( key );
+ d_printf("\n");
+ dump_registry_tree( file, key, regpath );
+ SAFE_FREE(regpath);
+ }
+
+ return true;
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool write_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk,
+ REGF_NK_REC *parent, REGF_FILE *outfile,
+ const char *parentpath )
+{
+ REGF_NK_REC *key, *subkey;
+ REGVAL_CTR *values = NULL;
+ REGSUBKEY_CTR *subkeys = NULL;
+ int i;
+ char *path = NULL;
+
+ if ( !( subkeys = TALLOC_ZERO_P( infile->mem_ctx, REGSUBKEY_CTR )) ) {
+ DEBUG(0,("write_registry_tree: talloc() failed!\n"));
+ return false;
+ }
+
+ if ( !(values = TALLOC_ZERO_P( subkeys, REGVAL_CTR )) ) {
+ DEBUG(0,("write_registry_tree: talloc() failed!\n"));
+ TALLOC_FREE(subkeys);
+ return false;
+ }
+
+ /* copy values into the REGVAL_CTR */
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type,
+ (const char *)nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) );
+ }
+
+ /* copy subkeys into the 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("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("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_IREAD|S_IWRITE) )) ) {
+ 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 DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv)
+{
+ POLICY_HND pol_hive, pol_key;
+ NTSTATUS status;
+ 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 = REG_KEY_READ |
+ SEC_RIGHT_MAXIMUM_ALLOWED |
+ SEC_RIGHT_SYSTEM_SECURITY;
+
+ if (argc <1 || argc > 2 || c->display_usage) {
+ d_printf("Usage: net rpc registry getsd <path> <secinfo>\n");
+ d_printf("Example: net rpc registry getsd 'HKLM\\Software\\Samba'\n");
+ 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_P(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, pipe_hnd, &pol_key, sec_info, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "getting sd failed: %s\n",
+ nt_errstr(status));
+ 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:
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_key, NULL);
+ rpccli_winreg_CloseKey(pipe_hnd, mem_ctx, &pol_hive, NULL);
+
+ 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.syntax_id, 0,
+ rpc_registry_getsd_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,
+ "Enumerate registry keys and values",
+ "net rpc registry enumerate\n"
+ " Enumerate registry keys and values"
+ },
+ {
+ "createkey",
+ rpc_registry_createkey,
+ NET_TRANSPORT_RPC,
+ "Create a new registry key",
+ "net rpc registry createkey\n"
+ " Create a new registry key"
+ },
+ {
+ "deletekey",
+ rpc_registry_deletekey,
+ NET_TRANSPORT_RPC,
+ "Delete a registry key",
+ "net rpc registry deletekey\n"
+ " Delete a registry key"
+ },
+ {
+ "getvalue",
+ rpc_registry_getvalue,
+ NET_TRANSPORT_RPC,
+ "Print a registry value",
+ "net rpc registry getvalue\n"
+ " Print a registry value"
+ },
+ {
+ "getvalueraw",
+ rpc_registry_getvalueraw,
+ NET_TRANSPORT_RPC,
+ "Print a registry value",
+ "net rpc registry getvalueraw\n"
+ " Print a registry value (raw version)"
+ },
+ {
+ "setvalue",
+ rpc_registry_setvalue,
+ NET_TRANSPORT_RPC,
+ "Set a new registry value",
+ "net rpc registry setvalue\n"
+ " Set a new registry value"
+ },
+ {
+ "deletevalue",
+ rpc_registry_deletevalue,
+ NET_TRANSPORT_RPC,
+ "Delete a registry value",
+ "net rpc registry deletevalue\n"
+ " Delete a registry value"
+ },
+ {
+ "save",
+ rpc_registry_save,
+ NET_TRANSPORT_RPC,
+ "Save a registry file",
+ "net rpc registry save\n"
+ " Save a registry file"
+ },
+ {
+ "dump",
+ rpc_registry_dump,
+ NET_TRANSPORT_RPC,
+ "Dump a registry file",
+ "net rpc registry dump\n"
+ " Dump a registry file"
+ },
+ {
+ "copy",
+ rpc_registry_copy,
+ NET_TRANSPORT_RPC,
+ "Copy a registry file",
+ "net rpc registry copy\n"
+ " Copy a registry file"
+ },
+ {
+ "getsd",
+ rpc_registry_getsd,
+ NET_TRANSPORT_RPC,
+ "Get security descriptor",
+ "net rpc registry getsd\n"
+ " Get security descriptior"
+ },
+ {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 0000000000..2bc5efe3cf
--- /dev/null
+++ b/source3/utils/net_rpc_rights.c
@@ -0,0 +1,708 @@
+/*
+ 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"
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS sid_to_name(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid,
+ fstring name)
+{
+ POLICY_HND pol;
+ enum lsa_SidType *sid_types = NULL;
+ NTSTATUS result;
+ char **domains = NULL, **names = NULL;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(result) )
+ return result;
+
+ result = rpccli_lsa_lookup_sids(pipe_hnd, mem_ctx, &pol, 1, sid, &domains, &names, &sid_types);
+
+ if ( NT_STATUS_IS_OK(result) ) {
+ if ( *domains[0] )
+ fstr_sprintf( name, "%s\\%s", domains[0], names[0] );
+ else
+ fstrcpy( name, names[0] );
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &pol);
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS name_to_sid(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ DOM_SID *sid, const char *name)
+{
+ POLICY_HND pol;
+ enum lsa_SidType *sid_types;
+ NTSTATUS result;
+ DOM_SID *sids;
+
+ /* maybe its a raw SID */
+ if ( strncmp(name, "S-", 2) == 0 && string_to_sid(sid, name) ) {
+ return NT_STATUS_OK;
+ }
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(result) )
+ return result;
+
+ result = rpccli_lsa_lookup_names(pipe_hnd, mem_ctx, &pol, 1, &name,
+ NULL, 1, &sids, &sid_types);
+
+ if ( NT_STATUS_IS_OK(result) )
+ sid_copy( sid, &sids[0] );
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &pol);
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS enum_privileges(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ POLICY_HND *pol )
+{
+ NTSTATUS result;
+ uint32 enum_context = 0;
+ uint32 pref_max_length=0x1000;
+ int i;
+ uint16 lang_id=0;
+ uint16 lang_id_sys=0;
+ uint16 lang_id_desc;
+ struct lsa_StringLarge *description = NULL;
+ struct lsa_PrivArray priv_array;
+
+ result = rpccli_lsa_EnumPrivs(pipe_hnd, ctx,
+ pol,
+ &enum_context,
+ &priv_array,
+ pref_max_length);
+
+ 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);
+
+ result = rpccli_lsa_LookupPrivDisplayName(pipe_hnd, ctx,
+ pol,
+ &lsa_name,
+ lang_id,
+ lang_id_sys,
+ &description,
+ &lang_id_desc);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_printf("??????\n");
+ continue;
+ }
+
+ d_printf("%s\n", description->string);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS check_privilege_for_user(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *ctx,
+ POLICY_HND *pol,
+ DOM_SID *sid,
+ const char *right)
+{
+ NTSTATUS result;
+ struct lsa_RightSet rights;
+ int i;
+
+ result = rpccli_lsa_EnumAccountRights(pipe_hnd, ctx,
+ pol,
+ sid,
+ &rights);
+
+ 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(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,
+ POLICY_HND *pol,
+ DOM_SID *sid )
+{
+ NTSTATUS result;
+ struct lsa_RightSet rights;
+ int i;
+
+ result = rpccli_lsa_EnumAccountRights(pipe_hnd, ctx,
+ pol,
+ sid,
+ &rights);
+
+ 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,
+ POLICY_HND *pol,
+ const char *privilege)
+{
+ NTSTATUS result;
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ struct lsa_SidArray sid_array;
+ int i;
+ fstring name;
+
+ result = rpccli_lsa_EnumAccounts(pipe_hnd, ctx,
+ pol,
+ &enum_context,
+ &sid_array,
+ pref_max_length);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ d_printf("%s:\n", privilege);
+
+ for ( i=0; i<sid_array.num_sids; i++ ) {
+
+ result = check_privilege_for_user(pipe_hnd, ctx, pol,
+ sid_array.sids[i].sid,
+ privilege);
+
+ if ( ! NT_STATUS_IS_OK(result)) {
+ if ( ! NT_STATUS_EQUAL(result, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ return result;
+ }
+ continue;
+ }
+
+ /* try to convert the SID to a name. Fall back to
+ printing the raw SID if necessary */
+ result = sid_to_name( pipe_hnd, ctx, sid_array.sids[i].sid, name );
+ if ( !NT_STATUS_IS_OK (result) )
+ 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,
+ POLICY_HND *pol)
+{
+ NTSTATUS result;
+ uint32 enum_context=0;
+ uint32 pref_max_length=0x1000;
+ struct lsa_SidArray sid_array;
+ int i;
+ fstring name;
+
+ result = rpccli_lsa_EnumAccounts(pipe_hnd, ctx,
+ pol,
+ &enum_context,
+ &sid_array,
+ pref_max_length);
+
+ 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 */
+
+ result = sid_to_name(pipe_hnd, ctx, sid_array.sids[i].sid, name);
+ if ( !NT_STATUS_IS_OK (result) )
+ sid_to_fstring(name, sid_array.sids[i].sid);
+
+ d_printf("%s\n", name);
+
+ result = enum_privileges_for_user(pipe_hnd, ctx, pol,
+ sid_array.sids[i].sid);
+ if ( !NT_STATUS_IS_OK(result) )
+ return result;
+
+ d_printf("\n");
+ }
+
+ return NT_STATUS_OK;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_list_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND pol;
+ NTSTATUS result;
+ DOM_SID sid;
+ fstring privname;
+ struct lsa_String lsa_name;
+ struct lsa_StringLarge *description = NULL;
+ uint16 lang_id = 0;
+ uint16 lang_id_sys = 0;
+ uint16 lang_id_desc;
+
+ result = rpccli_lsa_open_policy(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED, &pol);
+
+ if ( !NT_STATUS_IS_OK(result) )
+ return result;
+
+ /* backwards compatibility; just list available privileges if no arguement */
+
+ if (argc == 0) {
+ result = enum_privileges(pipe_hnd, mem_ctx, &pol );
+ goto done;
+ }
+
+ if (strequal(argv[0], "privileges")) {
+ int i = 1;
+
+ if (argv[1] == NULL) {
+ result = 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 */
+ result = rpccli_lsa_LookupPrivDisplayName(pipe_hnd, mem_ctx,
+ &pol,
+ &lsa_name,
+ lang_id,
+ lang_id_sys,
+ &description,
+ &lang_id_desc);
+
+ 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;
+ }
+
+ result = enum_accounts_for_privilege(pipe_hnd, mem_ctx, &pol, privname);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Error enumerating accounts for privilege %s [%s].\n",
+ privname, nt_errstr(result));
+ 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) {
+ result = enum_privileges_for_accounts(pipe_hnd, mem_ctx, &pol);
+ goto done;
+ }
+
+ while (argv[i] != NULL) {
+ result = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[i]);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ result = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ i++;
+ }
+ goto done;
+ }
+
+ /* backward comaptibility: if no keyword provided, treat the key
+ as an account name */
+ if (argc > 1) {
+ d_printf("Usage: net rpc rights list [[accounts|privileges] [name|SID]]\n");
+ result = NT_STATUS_OK;
+ goto done;
+ }
+
+ result = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+ result = enum_privileges_for_user(pipe_hnd, mem_ctx, &pol, &sid );
+
+done:
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &pol);
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_grant_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct lsa_RightSet rights;
+ int i;
+
+ DOM_SID sid;
+
+ if (argc < 2 ) {
+ d_printf("Usage: net rpc rights grant <name|SID> <rights...>\n");
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = rpccli_lsa_open_policy2(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ 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]);
+ }
+
+ result = rpccli_lsa_AddAccountRights(pipe_hnd, mem_ctx,
+ &dom_pol,
+ &sid,
+ &rights);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ d_printf("Successfully granted rights.\n");
+
+ done:
+ if ( !NT_STATUS_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to grant privileges for %s (%s)\n",
+ argv[0], nt_errstr(result));
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_rights_revoke_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND dom_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ struct lsa_RightSet rights;
+ DOM_SID sid;
+ int i;
+
+ if (argc < 2 ) {
+ d_printf("Usage: net rpc rights revoke <name|SID> <rights...>\n");
+ return NT_STATUS_OK;
+ }
+
+ result = name_to_sid(pipe_hnd, mem_ctx, &sid, argv[0]);
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result = rpccli_lsa_open_policy2(pipe_hnd, mem_ctx, true,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &dom_pol);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ 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]);
+ }
+
+ result = rpccli_lsa_RemoveAccountRights(pipe_hnd, mem_ctx,
+ &dom_pol,
+ &sid,
+ false,
+ &rights);
+
+ if (!NT_STATUS_IS_OK(result))
+ goto done;
+
+ d_printf("Successfully revoked rights.\n");
+
+done:
+ if ( !NT_STATUS_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to revoke privileges for %s (%s)\n",
+ argv[0], nt_errstr(result));
+ }
+
+ rpccli_lsa_Close(pipe_hnd, mem_ctx, &dom_pol);
+
+ return result;
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static int rpc_rights_list(struct net_context *c, int argc, const char **argv )
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "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.syntax_id, 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("Usage:\n"
+ "net rpc rights grant <name|SID> <right>\n"
+ " Assign privilege[s]\n");
+ d_printf("For example:\n");
+ d_printf(" net rpc rights grant 'VALE\\biddle' "
+ "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n");
+ d_printf(" 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.syntax_id, 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("Usage:\n"
+ "net rpc rights revoke <name|SID> <right>\n"
+ " Revoke privilege[s]\n");
+ d_printf("For example:\n");
+ d_printf(" net rpc rights revoke 'VALE\\biddle' "
+ "SePrintOperatorPrivilege SeDiskOperatorPrivilege\n");
+ d_printf(" 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.syntax_id, 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,
+ "View available/assigned privileges",
+ "net rpc rights list\n"
+ " View available/assigned privileges"
+ },
+ {
+ "grant",
+ rpc_rights_grant,
+ NET_TRANSPORT_RPC,
+ "Assign privilege[s]",
+ "net rpc rights grant\n"
+ " Assign privilege[s]"
+ },
+ {
+ "revoke",
+ rpc_rights_revoke,
+ NET_TRANSPORT_RPC,
+ "Revoke privilege[s]",
+ "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.syntax_id, rpc_sh_rights_list,
+ "View available or assigned privileges" },
+
+ { "grant", NULL, &ndr_table_lsarpc.syntax_id, rpc_sh_rights_grant,
+ "Assign privilege[s]" },
+
+ { "revoke", NULL, &ndr_table_lsarpc.syntax_id, rpc_sh_rights_revoke,
+ "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 0000000000..c0922efe6b
--- /dev/null
+++ b/source3/utils/net_rpc_samsync.c
@@ -0,0 +1,362 @@
+/*
+ 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"
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_samdump_internals(struct net_context *c,
+ const DOM_SID *domain_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 samsync_context *ctx = NULL;
+ NTSTATUS status;
+
+ status = libnet_samsync_init_context(mem_ctx,
+ domain_sid,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ctx->mode = NET_SAMSYNC_MODE_DUMP;
+ ctx->cli = pipe_hnd;
+ ctx->delta_fn = display_sam_entries;
+ ctx->domain_name = domain_name;
+
+ libnet_samsync(SAM_DATABASE_DOMAIN, ctx);
+
+ libnet_samsync(SAM_DATABASE_BUILTIN, ctx);
+
+ libnet_samsync(SAM_DATABASE_PRIVS, ctx);
+
+ TALLOC_FREE(ctx);
+
+ return NT_STATUS_OK;
+}
+
+/**
+ * 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;
+}
+
+
+/* dump sam database via samsync rpc calls */
+NTSTATUS rpc_vampire_internals(struct net_context *c,
+ const DOM_SID *domain_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;
+ struct samsync_context *ctx = NULL;
+
+ if (!sid_equal(domain_sid, get_global_sam_sid())) {
+ 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(),
+ sid_string_dbg(get_global_sam_sid()),
+ domain_name,
+ sid_string_dbg(domain_sid),
+ domain_name);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ result = libnet_samsync_init_context(mem_ctx,
+ domain_sid,
+ &ctx);
+ if (!NT_STATUS_IS_OK(result)) {
+ return result;
+ }
+
+ ctx->mode = NET_SAMSYNC_MODE_FETCH_PASSDB;
+ ctx->cli = pipe_hnd;
+ ctx->delta_fn = fetch_sam_entries;
+ ctx->domain_name = domain_name;
+
+ /* fetch domain */
+ result = libnet_samsync(SAM_DATABASE_DOMAIN, ctx);
+
+ if (!NT_STATUS_IS_OK(result) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto fail;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ /* fetch builtin */
+ ctx->domain_sid = sid_dup_talloc(mem_ctx, &global_sid_Builtin);
+ ctx->domain_sid_str = sid_string_talloc(mem_ctx, ctx->domain_sid);
+ result = libnet_samsync(SAM_DATABASE_BUILTIN, ctx);
+
+ if (!NT_STATUS_IS_OK(result) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto fail;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ fail:
+ TALLOC_FREE(ctx);
+ return result;
+}
+
+NTSTATUS rpc_vampire_ldif_internals(struct net_context *c,
+ const DOM_SID *domain_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 samsync_context *ctx = NULL;
+
+ status = libnet_samsync_init_context(mem_ctx,
+ domain_sid,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (argc >= 1) {
+ ctx->output_filename = argv[0];
+ }
+
+ ctx->mode = NET_SAMSYNC_MODE_FETCH_LDIF;
+ ctx->cli = pipe_hnd;
+ ctx->delta_fn = fetch_sam_entries_ldif;
+ ctx->domain_name = domain_name;
+
+ /* fetch domain */
+ status = libnet_samsync(SAM_DATABASE_DOMAIN, ctx);
+
+ if (!NT_STATUS_IS_OK(status) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto fail;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ /* fetch builtin */
+ ctx->domain_sid = sid_dup_talloc(mem_ctx, &global_sid_Builtin);
+ ctx->domain_sid_str = sid_string_talloc(mem_ctx, ctx->domain_sid);
+ status = libnet_samsync(SAM_DATABASE_BUILTIN, ctx);
+
+ if (!NT_STATUS_IS_OK(status) && ctx->error_message) {
+ d_fprintf(stderr, "%s\n", ctx->error_message);
+ goto fail;
+ }
+
+ if (ctx->result_message) {
+ d_fprintf(stdout, "%s\n", ctx->result_message);
+ }
+
+ fail:
+ TALLOC_FREE(ctx);
+ return status;
+}
+
+int rpc_vampire_ldif(struct net_context *c, int argc, const char **argv)
+{
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc vampire ldif\n"
+ " Dump remote SAM database to LDIF file or stdout\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id, 0,
+ rpc_vampire_ldif_internals, argc, argv);
+}
+
+
+NTSTATUS rpc_vampire_keytab_internals(struct net_context *c,
+ const DOM_SID *domain_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 samsync_context *ctx = NULL;
+
+ status = libnet_samsync_init_context(mem_ctx,
+ domain_sid,
+ &ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (argc >= 1) {
+ ctx->output_filename = argv[0];
+ }
+
+ ctx->mode = NET_SAMSYNC_MODE_FETCH_KEYTAB;
+ ctx->cli = pipe_hnd;
+ ctx->delta_fn = fetch_sam_entries_keytab;
+ ctx->domain_name = domain_name;
+ ctx->username = c->opt_user_name;
+ ctx->password = c->opt_password;
+
+ /* fetch domain */
+ status = libnet_samsync(SAM_DATABASE_DOMAIN, 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;
+}
+
+static NTSTATUS rpc_vampire_keytab_ds_internals(struct net_context *c,
+ const DOM_SID *domain_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) {
+ 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;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc vampire keytab\n"
+ " Dump remote SAM database to Kerberos keytab file\n");
+ return 0;
+ }
+
+ ret = run_rpc_command(c, NULL, &ndr_table_drsuapi.syntax_id,
+ NET_FLAGS_SEAL,
+ rpc_vampire_keytab_ds_internals, argc, argv);
+ if (ret == 0) {
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_netlogon.syntax_id, 0,
+ rpc_vampire_keytab_internals,
+ argc, argv);
+}
diff --git a/source3/utils/net_rpc_service.c b/source3/utils/net_rpc_service.c
new file mode 100644
index 0000000000..d1349a903d
--- /dev/null
+++ b/source3/utils/net_rpc_service.c
@@ -0,0 +1,725 @@
+/*
+ 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"
+
+
+struct svc_state_msg {
+ uint32 flag;
+ const char *message;
+};
+
+static struct svc_state_msg state_msg_table[] = {
+ { SVCCTL_STOPPED, "stopped" },
+ { SVCCTL_START_PENDING, "start pending" },
+ { SVCCTL_STOP_PENDING, "stop pending" },
+ { SVCCTL_RUNNING, "running" },
+ { SVCCTL_CONTINUE_PENDING, "resume pending" },
+ { SVCCTL_PAUSE_PENDING, "pause pending" },
+ { SVCCTL_PAUSED, "paused" },
+ { 0, NULL }
+};
+
+
+/********************************************************************
+********************************************************************/
+const char *svc_status_string( uint32 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 query_service_state(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hSCM,
+ const char *service,
+ uint32 *state )
+{
+ POLICY_HND hService;
+ SERVICE_STATUS service_status;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+
+ /* now cycle until the status is actually 'watch_state' */
+
+ status = rpccli_svcctl_OpenServiceW(pipe_hnd, mem_ctx,
+ hSCM,
+ service,
+ SC_RIGHT_SVC_QUERY_STATUS,
+ &hService,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to open service. [%s]\n", dos_errstr(result));
+ return result;
+ }
+
+ status = rpccli_svcctl_QueryServiceStatus(pipe_hnd, mem_ctx,
+ &hService,
+ &service_status,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ *state = service_status.state;
+ }
+
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hService, NULL);
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR watch_service_state(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hSCM,
+ const char *service,
+ uint32 watch_state,
+ uint32 *final_state )
+{
+ uint32 i;
+ uint32 state = 0;
+ WERROR result = WERR_GENERAL_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++;
+ sys_usleep( 100 );
+ }
+ d_printf("\n");
+
+ *final_state = state;
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static WERROR control_service(struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ POLICY_HND *hSCM,
+ const char *service,
+ uint32 control,
+ uint32 watch_state )
+{
+ POLICY_HND hService;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ SERVICE_STATUS service_status;
+ uint32 state = 0;
+
+ /* Open the Service */
+
+ status = rpccli_svcctl_OpenServiceW(pipe_hnd, mem_ctx,
+ hSCM,
+ service,
+ (SC_RIGHT_SVC_STOP|SC_RIGHT_SVC_PAUSE_CONTINUE),
+ &hService,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to open service. [%s]\n", dos_errstr(result));
+ goto done;
+ }
+
+ /* get the status */
+
+ status = rpccli_svcctl_ControlService(pipe_hnd, mem_ctx,
+ &hService,
+ control,
+ &service_status,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Control service request failed. [%s]\n", dos_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:
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hService, NULL);
+
+ return result;
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_list_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM;
+ ENUM_SERVICES_STATUS *services;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ fstring servicename;
+ fstring displayname;
+ uint32 num_services = 0;
+ int i;
+
+ if (argc != 0 ) {
+ d_printf("Usage: net rpc service list\n");
+ return NT_STATUS_OK;
+ }
+
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ result = rpccli_svcctl_enumerate_services(pipe_hnd, mem_ctx, &hSCM, SVCCTL_TYPE_WIN32,
+ SVCCTL_STATE_ALL, &num_services, &services );
+
+ if ( !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to enumerate services. [%s]\n", dos_errstr(result));
+ goto done;
+ }
+
+ if ( num_services == 0 )
+ d_printf("No services returned\n");
+
+ for ( i=0; i<num_services; i++ ) {
+ rpcstr_pull( servicename, services[i].servicename.buffer, sizeof(servicename), -1, STR_TERMINATE );
+ rpcstr_pull( displayname, services[i].displayname.buffer, sizeof(displayname), -1, STR_TERMINATE );
+
+ d_printf("%-20s \"%s\"\n", servicename, displayname);
+ }
+
+done:
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_status_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM, hService;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ SERVICE_STATUS service_status;
+ SERVICE_CONFIG config;
+ fstring ascii_string;
+
+ if (argc != 1 ) {
+ d_printf("Usage: net rpc service status <service>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ /* Open the Service */
+
+ status = rpccli_svcctl_OpenServiceW(pipe_hnd, mem_ctx,
+ &hSCM,
+ argv[0],
+ (SC_RIGHT_SVC_QUERY_STATUS|SC_RIGHT_SVC_QUERY_CONFIG),
+ &hService,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to open service. [%s]\n", dos_errstr(result));
+ goto done;
+ }
+
+ /* get the status */
+
+ status = rpccli_svcctl_QueryServiceStatus(pipe_hnd, mem_ctx,
+ &hService,
+ &service_status,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Query status request failed. [%s]\n", dos_errstr(result));
+ goto done;
+ }
+
+ d_printf("%s service is %s.\n", argv[0], svc_status_string(service_status.state));
+
+ /* get the config */
+
+ result = rpccli_svcctl_query_config(pipe_hnd, mem_ctx, &hService, &config );
+ if ( !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Query config request failed. [%s]\n", dos_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 ) {
+ rpcstr_pull( ascii_string, config.executablepath->buffer, sizeof(ascii_string), -1, STR_TERMINATE );
+ d_printf("\tExecutable Path = %s\n", ascii_string);
+ }
+
+ if ( config.loadordergroup ) {
+ rpcstr_pull( ascii_string, config.loadordergroup->buffer, sizeof(ascii_string), -1, STR_TERMINATE );
+ d_printf("\tLoad Order Group = %s\n", ascii_string);
+ }
+
+ if ( config.dependencies ) {
+ rpcstr_pull( ascii_string, config.dependencies->buffer, sizeof(ascii_string), -1, STR_TERMINATE );
+ d_printf("\tDependencies = %s\n", ascii_string);
+ }
+
+ if ( config.startname ) {
+ rpcstr_pull( ascii_string, config.startname->buffer, sizeof(ascii_string), -1, STR_TERMINATE );
+ d_printf("\tStart Name = %s\n", ascii_string);
+ }
+
+ if ( config.displayname ) {
+ rpcstr_pull( ascii_string, config.displayname->buffer, sizeof(ascii_string), -1, STR_TERMINATE );
+ d_printf("\tDisplay Name = %s\n", ascii_string);
+ }
+
+done:
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hService, NULL);
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_stop_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ fstring servicename;
+
+ if (argc != 1 ) {
+ d_printf("Usage: net rpc service status <service>\n");
+ return NT_STATUS_OK;
+ }
+
+ fstrcpy( servicename, argv[0] );
+
+ /* Open the Service Control Manager */
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, servicename,
+ SVCCTL_CONTROL_STOP, SVCCTL_STOPPED );
+
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_pause_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ fstring servicename;
+
+ if (argc != 1 ) {
+ d_printf("Usage: net rpc service status <service>\n");
+ return NT_STATUS_OK;
+ }
+
+ fstrcpy( servicename, argv[0] );
+
+ /* Open the Service Control Manager */
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, servicename,
+ SVCCTL_CONTROL_PAUSE, SVCCTL_PAUSED );
+
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_resume_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ fstring servicename;
+
+ if (argc != 1 ) {
+ d_printf("Usage: net rpc service status <service>\n");
+ return NT_STATUS_OK;
+ }
+
+ fstrcpy( servicename, argv[0] );
+
+ /* Open the Service Control Manager */
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ result = control_service(pipe_hnd, mem_ctx, &hSCM, servicename,
+ SVCCTL_CONTROL_CONTINUE, SVCCTL_RUNNING );
+
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ return werror_to_ntstatus(result);
+}
+
+/********************************************************************
+********************************************************************/
+
+static NTSTATUS rpc_service_start_internal(struct net_context *c,
+ const DOM_SID *domain_sid,
+ const char *domain_name,
+ struct cli_state *cli,
+ struct rpc_pipe_client *pipe_hnd,
+ TALLOC_CTX *mem_ctx,
+ int argc,
+ const char **argv )
+{
+ POLICY_HND hSCM, hService;
+ WERROR result = WERR_GENERAL_FAILURE;
+ NTSTATUS status;
+ uint32 state = 0;
+
+ if (argc != 1 ) {
+ d_printf("Usage: net rpc service status <service>\n");
+ return NT_STATUS_OK;
+ }
+
+ /* Open the Service Control Manager */
+ status = rpccli_svcctl_OpenSCManagerW(pipe_hnd, mem_ctx,
+ pipe_hnd->srv_name_slash,
+ NULL,
+ SC_RIGHT_MGR_ENUMERATE_SERVICE,
+ &hSCM,
+ &result);
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result)) {
+ d_fprintf(stderr, "Failed to open Service Control Manager. [%s]\n", dos_errstr(result));
+ return werror_to_ntstatus(result);
+ }
+
+ /* Open the Service */
+
+ status = rpccli_svcctl_OpenServiceW(pipe_hnd, mem_ctx,
+ &hSCM,
+ argv[0],
+ SC_RIGHT_SVC_START,
+ &hService,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Failed to open service. [%s]\n", dos_errstr(result));
+ goto done;
+ }
+
+ /* get the status */
+
+ status = rpccli_svcctl_StartServiceW(pipe_hnd, mem_ctx,
+ &hService,
+ 0,
+ NULL,
+ &result);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(result) ) {
+ d_fprintf(stderr, "Query status request failed. [%s]\n", dos_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], dos_errstr(result) );
+
+done:
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hService, NULL);
+ rpccli_svcctl_CloseServiceHandle(pipe_hnd, mem_ctx, &hSCM, NULL);
+
+ 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("Usage:\n"
+ "net rpc service list\n"
+ " View configured Win32 services\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 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("Usage:\n"
+ "net rpc service start <service>\n"
+ " Start a Win32 service\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 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("Usage:\n"
+ "net rpc service stop <service>\n"
+ " Stop a Win32 service\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 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("Usage:\n"
+ "net rpc service resume <service>\n"
+ " Resume a Win32 service\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 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("Usage:\n"
+ "net rpc service pause <service>\n"
+ " Pause a Win32 service\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 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("Usage:\n"
+ "net rpc service status <service>\n"
+ " Show the current status of a service\n");
+ return 0;
+ }
+
+ return run_rpc_command(c, NULL, &ndr_table_svcctl.syntax_id, 0,
+ rpc_service_status_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,
+ "View configured Win32 services",
+ "net rpc service list\n"
+ " View configured Win32 services"
+ },
+ {
+ "start",
+ rpc_service_start,
+ NET_TRANSPORT_RPC,
+ "Start a service",
+ "net rpc service start\n"
+ " Start a service"
+ },
+ {
+ "stop",
+ rpc_service_stop,
+ NET_TRANSPORT_RPC,
+ "Stop a service",
+ "net rpc service stop\n"
+ " Stop a service"
+ },
+ {
+ "pause",
+ rpc_service_pause,
+ NET_TRANSPORT_RPC,
+ "Pause a service",
+ "net rpc service pause\n"
+ " Pause a service"
+ },
+ {
+ "resume",
+ rpc_service_resume,
+ NET_TRANSPORT_RPC,
+ "Resume a paused service",
+ "net rpc service resume\n"
+ " Resume a service"
+ },
+ {
+ "status",
+ rpc_service_status,
+ NET_TRANSPORT_RPC,
+ "View current status of a service",
+ "net rpc service status\n"
+ " View current status of 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 0000000000..977e1e2a0a
--- /dev/null
+++ b/source3/utils/net_rpc_sh_acct.c
@@ -0,0 +1,453 @@
+/*
+ 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"
+
+/*
+ * 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))
+{
+ POLICY_HND connect_pol, domain_pol;
+ NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
+ union samr_DomainInfo *info1 = NULL;
+ union samr_DomainInfo *info3 = NULL;
+ union samr_DomainInfo *info12 = NULL;
+ int store;
+
+ ZERO_STRUCT(connect_pol);
+ ZERO_STRUCT(domain_pol);
+
+ /* Get sam policy handle */
+
+ result = rpccli_samr_Connect2(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ MAXIMUM_ALLOWED_ACCESS,
+ &connect_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ /* Get domain policy handle */
+
+ result = rpccli_samr_OpenDomain(pipe_hnd, mem_ctx,
+ &connect_pol,
+ MAXIMUM_ALLOWED_ACCESS,
+ ctx->domain_sid,
+ &domain_pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ goto done;
+ }
+
+ result = rpccli_samr_QueryDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ &info1);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "query_domain_info level 1 failed: %s\n",
+ nt_errstr(result));
+ goto done;
+ }
+
+ result = rpccli_samr_QueryDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 3,
+ &info3);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "query_domain_info level 3 failed: %s\n",
+ nt_errstr(result));
+ goto done;
+ }
+
+ result = rpccli_samr_QueryDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 12,
+ &info12);
+
+ if (!NT_STATUS_IS_OK(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:
+ result = rpccli_samr_SetDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 1,
+ info1);
+ break;
+ case 3:
+ result = rpccli_samr_SetDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 3,
+ info3);
+ break;
+ case 12:
+ result = rpccli_samr_SetDomainInfo(pipe_hnd, mem_ctx,
+ &domain_pol,
+ 12,
+ info12);
+ break;
+ default:
+ d_fprintf(stderr, "Got unexpected info level %d\n", store);
+ result = NT_STATUS_INTERNAL_ERROR;
+ goto done;
+ }
+
+ done:
+ if (is_valid_policy_hnd(&domain_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &domain_pol);
+ }
+ if (is_valid_policy_hnd(&connect_pol)) {
+ rpccli_samr_Close(pipe_hnd, mem_ctx, &connect_pol);
+ }
+
+ return result;
+}
+
+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, "usage: %s\n", 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, "usage: %s <count>\n", 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.syntax_id, rpc_sh_acct_pol_show,
+ "Show current account policy settings" },
+ { "badpw", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_badpw,
+ "Set bad password count before lockout" },
+ { "lockduration", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_lockduration,
+ "Set account lockout duration" },
+ { "resetduration", NULL, &ndr_table_samr.syntax_id,
+ rpc_sh_acct_set_resetduration,
+ "Set bad password count reset duration" },
+ { "minpwage", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_minpwage,
+ "Set minimum password age" },
+ { "maxpwage", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_maxpwage,
+ "Set maximum password age" },
+ { "minpwlen", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_minpwlen,
+ "Set minimum password length" },
+ { "pwhistlen", NULL, &ndr_table_samr.syntax_id, rpc_sh_acct_set_pwhistlen,
+ "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 0000000000..3aaed1ed18
--- /dev/null
+++ b/source3/utils/net_rpc_shell.c
@@ -0,0 +1,298 @@
+/*
+ * 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"
+
+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;
+ 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->interface,
+ &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")) {
+ 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_P(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.syntax_id, rpc_sh_info,
+ "Print information about the domain connected to" },
+
+ { "rights", net_rpc_rights_cmds, 0, NULL,
+ "List/Grant/Revoke user rights" },
+
+ { "share", net_rpc_share_cmds, 0, NULL,
+ "List/Add/Remove etc shares" },
+
+ { "user", net_rpc_user_cmds, 0, NULL,
+ "List/Add/Remove user info" },
+
+ { "account", net_rpc_acct_cmds, 0, NULL,
+ "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;
+
+ if (argc != 0 || c->display_usage) {
+ d_printf("Usage:\n"
+ "net rpc shell\n");
+ return -1;
+ }
+
+ if (libnetapi_init(&c->netapi_ctx) != 0) {
+ return -1;
+ }
+ libnetapi_set_username(c->netapi_ctx, c->opt_user_name);
+ libnetapi_set_password(c->netapi_ctx, c->opt_password);
+ if (c->opt_kerberos) {
+ libnetapi_set_use_kerberos(c->netapi_ctx);
+ }
+
+ ctx = TALLOC_P(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,
+ sid_string_tos(ctx->domain_sid));
+
+ 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_sam.c b/source3/utils/net_sam.c
new file mode 100644
index 0000000000..ce132131f7
--- /dev/null
+++ b/source3/utils/net_sam.c
@@ -0,0 +1,1938 @@
+/*
+ * 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 "utils/net.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;
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "usage: 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 flag)
+{
+ struct samu *sam_acct = NULL;
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+ uint16 acct_flags;
+
+ if ((argc != 2) || c->display_usage ||
+ (!strequal(argv[1], "yes") &&
+ !strequal(argv[1], "no"))) {
+ d_fprintf(stderr, "usage: 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;
+ 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, "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;
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "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;
+ }
+
+ if (!pdb_getgrsid(&map, sid)) {
+ d_fprintf(stderr, "Could not load group %s\n", argv[0]);
+ return -1;
+ }
+
+ fstrcpy(map.comment, argv[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]);
+
+ 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,
+ "Change a user's home directory",
+ "net sam set homedir\n"
+ " Change a user's home directory"
+ },
+ {
+ "profilepath",
+ net_sam_set_profilepath,
+ NET_TRANSPORT_LOCAL,
+ "Change a user's profile path",
+ "net sam set profilepath\n"
+ " Change a user's profile path"
+ },
+ {
+ "comment",
+ net_sam_set_comment,
+ NET_TRANSPORT_LOCAL,
+ "Change a users or groups description",
+ "net sam set comment\n"
+ " Change a users or groups description"
+ },
+ {
+ "fullname",
+ net_sam_set_fullname,
+ NET_TRANSPORT_LOCAL,
+ "Change a user's full name",
+ "net sam set fullname\n"
+ " Change a user's full name"
+ },
+ {
+ "logonscript",
+ net_sam_set_logonscript,
+ NET_TRANSPORT_LOCAL,
+ "Change a user's logon script",
+ "net sam set logonscript\n"
+ " Change a user's logon script"
+ },
+ {
+ "homedrive",
+ net_sam_set_homedrive,
+ NET_TRANSPORT_LOCAL,
+ "Change a user's home drive",
+ "net sam set homedrive\n"
+ " Change a user's home drive"
+ },
+ {
+ "workstations",
+ net_sam_set_workstations,
+ NET_TRANSPORT_LOCAL,
+ "Change a user's allowed workstations",
+ "net sam set workstations\n"
+ " Change a user's allowed workstations"
+ },
+ {
+ "disabled",
+ net_sam_set_disabled,
+ NET_TRANSPORT_LOCAL,
+ "Disable/Enable a user",
+ "net sam set disable\n"
+ " Disable/Enable a user"
+ },
+ {
+ "pwnotreq",
+ net_sam_set_pwnotreq,
+ NET_TRANSPORT_LOCAL,
+ "Disable/Enable the password not required flag",
+ "net sam set pwnotreq\n"
+ " Disable/Enable the password not required flag"
+ },
+ {
+ "autolock",
+ net_sam_set_autolock,
+ NET_TRANSPORT_LOCAL,
+ "Disable/Enable a user's lockout flag",
+ "net sam set autolock\n"
+ " Disable/Enable a user's lockout flag"
+ },
+ {
+ "pwnoexp",
+ net_sam_set_pwnoexp,
+ NET_TRANSPORT_LOCAL,
+ "Disable/Enable whether a user's pw does not expire",
+ "net sam set pwnoexp\n"
+ " Disable/Enable whether a user's pw does not expire"
+ },
+ {
+ "pwdmustchangenow",
+ net_sam_set_pwdmustchangenow,
+ NET_TRANSPORT_LOCAL,
+ "Force users password must change at next logon",
+ "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 value = 0;
+ uint32 old_value = 0;
+ int field;
+ char *endptr;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "usage: net sam policy set "
+ "\"<account policy>\" <value> \n");
+ return -1;
+ }
+
+ account_policy = argv[0];
+ field = account_policy_name_to_fieldnum(account_policy);
+
+ if (strequal(argv[1], "forever") || strequal(argv[1], "never")
+ || strequal(argv[1], "off")) {
+ value = -1;
+ }
+ else {
+ value = strtoul(argv[1], &endptr, 10);
+
+ if ((endptr == argv[1]) || (endptr[0] != '\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(&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]);
+ }
+
+ SAFE_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 old_value;
+ int field;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "usage: net sam policy show"
+ " \"<account policy>\" \n");
+ return -1;
+ }
+
+ account_policy = argv[0];
+ field = account_policy_name_to_fieldnum(account_policy);
+
+ if (field == 0) {
+ const char **names;
+ int count;
+ int i;
+ account_policy_names_list(&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]);
+ }
+ }
+ SAFE_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("Usage:\n"
+ "net sam policy list\n"
+ " List account policies\n");
+ return 0;
+ }
+
+ account_policy_names_list(&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]);
+ }
+ }
+ SAFE_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,
+ "List account policies",
+ "net sam policy list\n"
+ " List account policies"
+ },
+ {
+ "show",
+ net_sam_policy_show,
+ NET_TRANSPORT_LOCAL,
+ "Show account policies",
+ "net sam policy show\n"
+ " Show account policies"
+ },
+ {
+ "set",
+ net_sam_policy_set,
+ NET_TRANSPORT_LOCAL,
+ "Change account policies",
+ "net sam policy set\n"
+ " Change account policies"
+ },
+ {NULL, NULL, 0, NULL, NULL}
+ };
+
+ return net_run_function(c, argc, argv, "net sam policy", func);
+}
+
+extern PRIVS privs[];
+
+static int net_sam_rights_list(struct net_context *c, int argc,
+ const char **argv)
+{
+ SE_PRIV mask;
+
+ if (argc > 1 || c->display_usage) {
+ d_fprintf(stderr, "usage: net sam rights list [privilege name]\n");
+ return -1;
+ }
+
+ if (argc == 0) {
+ int i;
+ int num = count_all_privileges();
+
+ for (i=0; i<num; i++) {
+ d_printf("%s\n", privs[i].name);
+ }
+ return 0;
+ }
+
+ if (se_priv_from_name(argv[0], &mask)) {
+ DOM_SID *sids;
+ int i, num_sids;
+ NTSTATUS status;
+
+ status = privilege_enum_sids(&mask, 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;
+
+ if (lookup_sid(talloc_tos(), &sids[i], &dom, &name,
+ &type)) {
+ d_printf("%s\\%s\n", dom, name);
+ }
+ else {
+ d_printf("%s\n", sid_string_tos(&sids[i]));
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+static int net_sam_rights_grant(struct net_context *c, int argc,
+ const char **argv)
+{
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ SE_PRIV mask;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "usage: net sam rights grant <name> "
+ "<right>\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 (!se_priv_from_name(argv[1], &mask)) {
+ d_fprintf(stderr, "%s unknown\n", argv[1]);
+ return -1;
+ }
+
+ if (!grant_privilege(&sid, &mask)) {
+ d_fprintf(stderr, "Could not grant privilege\n");
+ return -1;
+ }
+
+ d_printf("Granted %s to %s\\%s\n", argv[1], dom, name);
+ return 0;
+}
+
+static int net_sam_rights_revoke(struct net_context *c, int argc, const char **argv)
+{
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ SE_PRIV mask;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "usage: net sam rights revoke <name> "
+ "<right>\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 (!se_priv_from_name(argv[1], &mask)) {
+ d_fprintf(stderr, "%s unknown\n", argv[1]);
+ return -1;
+ }
+
+ if (!revoke_privilege(&sid, &mask)) {
+ d_fprintf(stderr, "Could not revoke privilege\n");
+ return -1;
+ }
+
+ d_printf("Revoked %s from %s\\%s\n", argv[1], 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,
+ "List possible user rights",
+ "net sam rights list\n"
+ " List possible user rights"
+ },
+ {
+ "grant",
+ net_sam_rights_grant,
+ NET_TRANSPORT_LOCAL,
+ "Grant a right",
+ "net sam rights grant\n"
+ " Grant a right"
+ },
+ {
+ "revoke",
+ net_sam_rights_revoke,
+ NET_TRANSPORT_LOCAL,
+ "Revoke a right",
+ "net sam rights revoke\n"
+ " Revoke a right"
+ },
+ {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 *pmap)
+{
+ NTSTATUS status;
+ GROUP_MAP map;
+ const char *grpname, *dom, *name;
+ uint32 rid;
+
+ if (pdb_getgrgid(&map, grp->gr_gid)) {
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ map.gid = grp->gr_gid;
+ grpname = grp->gr_name;
+
+ if (lookup_name(talloc_tos(), grpname, LOOKUP_NAME_LOCAL,
+ &dom, &name, NULL, NULL)) {
+
+ const char *tmp = talloc_asprintf(
+ talloc_tos(), "Unix Group %s", grp->gr_name);
+
+ DEBUG(5, ("%s exists as %s\\%s, retrying as \"%s\"\n",
+ grpname, dom, name, tmp));
+ grpname = tmp;
+ }
+
+ if (lookup_name(talloc_tos(), grpname, LOOKUP_NAME_LOCAL,
+ NULL, NULL, NULL, NULL)) {
+ DEBUG(3, ("\"%s\" exists, can't map it\n", grp->gr_name));
+ return NT_STATUS_GROUP_EXISTS;
+ }
+
+ fstrcpy(map.nt_name, grpname);
+
+ if (pdb_rid_algorithm()) {
+ rid = algorithmic_pdb_gid_to_group_rid( grp->gr_gid );
+ } else {
+ if (!pdb_new_rid(&rid)) {
+ DEBUG(3, ("Could not get a new RID for %s\n",
+ grp->gr_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ sid_compose(&map.sid, get_global_sam_sid(), rid);
+ map.sid_name_use = SID_NAME_DOM_GRP;
+ fstrcpy(map.comment, talloc_asprintf(talloc_tos(), "Unix Group %s",
+ grp->gr_name));
+
+ status = pdb_add_group_mapping_entry(&map);
+ if (NT_STATUS_IS_OK(status)) {
+ *pmap = map;
+ }
+ return status;
+}
+
+static int net_sam_mapunixgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ GROUP_MAP map;
+ struct group *grp;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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;
+ }
+
+ 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],
+ sid_string_tos(&map.sid));
+
+ return 0;
+}
+
+/*
+ * Remove a group mapping
+ */
+
+static NTSTATUS unmap_unix_group(const struct group *grp, GROUP_MAP *pmap)
+{
+ NTSTATUS status;
+ GROUP_MAP map;
+ const char *grpname;
+ DOM_SID dom_sid;
+
+ map.gid = grp->gr_gid;
+ grpname = grp->gr_name;
+
+ if (!lookup_name(talloc_tos(), grpname, 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;
+ }
+
+ fstrcpy(map.nt_name, grpname);
+
+ if (!pdb_gid_to_sid(map.gid, &dom_sid)) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ status = pdb_delete_group_mapping_entry(dom_sid);
+
+ return status;
+}
+
+static int net_sam_unmapunixgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ GROUP_MAP map;
+ struct group *grp;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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, &map);
+
+ 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 local group
+ */
+
+static int net_sam_createlocalgroup(struct net_context *c, int argc, const char **argv)
+{
+ NTSTATUS status;
+ uint32 rid;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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)
+{
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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 rid;
+ enum lsa_SidType type;
+ fstring groupname;
+ DOM_SID sid;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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_alias( 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;
+ DOM_SID group, member;
+ enum lsa_SidType grouptype, membertype;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "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_DOM_GRP)) {
+ d_fprintf(stderr, "%s is a local group, only users "
+ "and domain groups can be added.\n"
+ "%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 {
+ 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;
+ DOM_SID group, member;
+ enum lsa_SidType grouptype;
+ NTSTATUS status;
+
+ if (argc != 2 || c->display_usage) {
+ d_fprintf(stderr, "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 {
+ 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 {
+ d_printf("Deleted %s from %s\\%s\n",
+ sid_string_tos(&member), 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;
+ DOM_SID group;
+ enum lsa_SidType grouptype;
+ NTSTATUS status;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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)) {
+ DOM_SID *members = NULL;
+ size_t i, num_members = 0;
+
+ status = pdb_enum_aliasmem(&group, &members, &num_members);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ d_fprintf(stderr, "Listing group members failed with "
+ "%s\n", nt_errstr(status));
+ 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 {
+ d_printf(" %s\n", sid_string_tos(&members[i]));
+ }
+ }
+
+ TALLOC_FREE(members);
+ } 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;
+ }
+
+ 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, "usage: 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);
+ }
+ }
+
+ pdb_search_destroy(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(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(), "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(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(&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(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,
+ "List SAM users",
+ "net sam list users\n"
+ " List SAM users"
+ },
+ {
+ "groups",
+ net_sam_list_groups,
+ NET_TRANSPORT_LOCAL,
+ "List SAM groups",
+ "net sam list groups\n"
+ " List SAM groups"
+ },
+ {
+ "localgroups",
+ net_sam_list_localgroups,
+ NET_TRANSPORT_LOCAL,
+ "List SAM local groups",
+ "net sam list localgroups\n"
+ " List SAM local groups"
+ },
+ {
+ "builtin",
+ net_sam_list_builtin,
+ NET_TRANSPORT_LOCAL,
+ "List builtin groups",
+ "net sam list builtin\n"
+ " List builtin groups"
+ },
+ {
+ "workstations",
+ net_sam_list_workstations,
+ NET_TRANSPORT_LOCAL,
+ "List domain member workstations",
+ "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)
+{
+ DOM_SID sid;
+ enum lsa_SidType type;
+ const char *dom, *name;
+
+ if (argc != 1 || c->display_usage) {
+ d_fprintf(stderr, "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), sid_string_tos(&sid));
+
+ 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 *ls;
+ GROUP_MAP gmap;
+ DOM_SID gsid;
+ gid_t domusers_gid = -1;
+ gid_t domadmins_gid = -1;
+ struct samu *samuser;
+ struct passwd *pwd;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net sam provision\n"
+ " Init an LDAP tree with default users/groups\n");
+ 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, "ldapsam") != 0) {
+ 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 (!winbind_ping()) {
+ d_fprintf(stderr, "winbind seems not to run. Provisioning "
+ "LDAP only works when winbind runs.\n");
+ goto failed;
+ }
+
+ if (!NT_STATUS_IS_OK(smbldap_init(tc, NULL, ldap_uri, &ls))) {
+ 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_GROUP_RID_USERS);
+
+ if (!pdb_getgrsid(&gmap, gsid)) {
+ 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 (!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());
+ gidstr = talloc_asprintf(tc, "%d", 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);
+ 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",
+ sid_string_talloc(tc, &gsid));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(ls, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, "Failed to add Domain Users group to ldap directory\n");
+ }
+ } 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_GROUP_RID_ADMINS);
+
+ if (!pdb_getgrsid(&gmap, gsid)) {
+ 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 (!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());
+ gidstr = talloc_asprintf(tc, "%d", 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);
+ 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",
+ sid_string_talloc(tc, &gsid));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(ls, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, "Failed to add Domain Admins group to ldap directory\n");
+ }
+ } 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;
+ DOM_SID sid;
+ char *dn;
+ char *name;
+ char *uidstr;
+ char *gidstr;
+ char *shell;
+ char *dir;
+ 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 (!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());
+ uidstr = talloc_asprintf(tc, "%d", uid);
+ gidstr = talloc_asprintf(tc, "%d", domadmins_gid);
+ dir = talloc_sub_specified(tc, lp_template_homedir(),
+ "Administrator",
+ get_global_sam_name(),
+ uid, domadmins_gid);
+ shell = talloc_sub_specified(tc, lp_template_shell(),
+ "Administrator",
+ 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_USER_RID_ADMIN);
+
+ 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);
+ 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",
+ sid_string_talloc(tc, &sid));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags",
+ pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED,
+ NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(ls, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, "Failed to add Administrator user to ldap directory\n");
+ }
+ } 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_guestaccount())) {
+ LDAPMod **mods = NULL;
+ DOM_SID sid;
+ char *dn;
+ char *uidstr;
+ char *gidstr;
+ int rc;
+
+ d_printf("Adding the Guest user.\n");
+
+ pwd = getpwnam_alloc(tc, lp_guestaccount());
+
+ 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_guestaccount());
+ 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;
+ }
+ }
+
+ sid_compose(&sid, get_global_sam_sid(), DOMAIN_USER_RID_GUEST);
+
+ dn = talloc_asprintf(tc, "uid=%s,%s", pwd->pw_name, lp_ldap_user_suffix ());
+ uidstr = talloc_asprintf(tc, "%d", pwd->pw_uid);
+ gidstr = talloc_asprintf(tc, "%d", 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);
+ 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",
+ sid_string_talloc(tc, &sid));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaAcctFlags",
+ pdb_encode_acct_ctrl(ACB_NORMAL|ACB_DISABLED,
+ NEW_PW_FORMAT_SPACE_PADDED_LEN));
+
+ talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(ls, dn, mods);
+
+ if (rc != LDAP_SUCCESS) {
+ d_fprintf(stderr, "Failed to add Guest user to ldap directory\n");
+ }
+ } else {
+ d_printf("found!\n");
+ }
+
+ d_printf("Checking Guest's group.\n");
+
+ pwd = getpwnam_alloc(NULL, lp_guestaccount());
+ 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)) {
+ 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());
+ gidstr = talloc_asprintf(tc, "%d", 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_GROUP_RID_GUESTS);
+
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_POSIXGROUP);
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "objectClass", LDAP_OBJ_GROUPMAP);
+ 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",
+ sid_string_talloc(tc, &gsid));
+ smbldap_set_mod(&mods, LDAP_MOD_ADD, "sambaGroupType", gtype);
+
+ talloc_autofree_ldapmod(tc, mods);
+
+ rc = smbldap_add(ls, 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(tc);
+ return 0;
+
+failed:
+ talloc_free(tc);
+ 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,
+ "Create a new BUILTIN group",
+ "net sam createbuiltingroup\n"
+ " Create a new BUILTIN group"
+ },
+ {
+ "createlocalgroup",
+ net_sam_createlocalgroup,
+ NET_TRANSPORT_LOCAL,
+ "Create a new local group",
+ "net sam createlocalgroup\n"
+ " Create a new local group"
+ },
+ {
+ "deletelocalgroup",
+ net_sam_deletelocalgroup,
+ NET_TRANSPORT_LOCAL,
+ "Delete an existing local group",
+ "net sam deletelocalgroup\n"
+ " Delete an existing local group"
+ },
+ {
+ "mapunixgroup",
+ net_sam_mapunixgroup,
+ NET_TRANSPORT_LOCAL,
+ "Map a unix group to a domain group",
+ "net sam mapunixgroup\n"
+ " Map a unix group to a domain group"
+ },
+ {
+ "unmapunixgroup",
+ net_sam_unmapunixgroup,
+ NET_TRANSPORT_LOCAL,
+ "Remove a group mapping of an unix group to a domain "
+ "group",
+ "net sam unmapunixgroup\n"
+ " Remove a group mapping of an unix group to a "
+ "domain group"
+ },
+ {
+ "addmem",
+ net_sam_addmem,
+ NET_TRANSPORT_LOCAL,
+ "Add a member to a group",
+ "net sam addmem\n"
+ " Add a member to a group"
+ },
+ {
+ "delmem",
+ net_sam_delmem,
+ NET_TRANSPORT_LOCAL,
+ "Delete a member from a group",
+ "net sam delmem\n"
+ " Delete a member from a group"
+ },
+ {
+ "listmem",
+ net_sam_listmem,
+ NET_TRANSPORT_LOCAL,
+ "List group members",
+ "net sam listmem\n"
+ " List group members"
+ },
+ {
+ "list",
+ net_sam_list,
+ NET_TRANSPORT_LOCAL,
+ "List users, groups and local groups",
+ "net sam list\n"
+ " List users, groups and local groups"
+ },
+ {
+ "show",
+ net_sam_show,
+ NET_TRANSPORT_LOCAL,
+ "Show details of a SAM entry",
+ "net sam show\n"
+ " Show details of a SAM entry"
+ },
+ {
+ "set",
+ net_sam_set,
+ NET_TRANSPORT_LOCAL,
+ "Set details of a SAM account",
+ "net sam set\n"
+ " Set details of a SAM account"
+ },
+ {
+ "policy",
+ net_sam_policy,
+ NET_TRANSPORT_LOCAL,
+ "Set account policies",
+ "net sam policy\n"
+ " Set account policies"
+ },
+ {
+ "rights",
+ net_sam_rights,
+ NET_TRANSPORT_LOCAL,
+ "Manipulate user privileges",
+ "net sam rights\n"
+ " Manipulate user privileges"
+ },
+#ifdef HAVE_LDAP
+ {
+ "provision",
+ net_sam_provision,
+ NET_TRANSPORT_LOCAL,
+ "Provision a clean user database",
+ "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_share.c b/source3/utils/net_share.c
new file mode 100644
index 0000000000..68fcd3b3bd
--- /dev/null
+++ b/source3/utils/net_share.c
@@ -0,0 +1,77 @@
+/*
+ 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 [<filename>] "
+ "[misc. options] [targets]"
+ "\n\tshows a list of all shares together with all users allowed to"
+ "\n\taccess them. This needs the output of 'net usersidlist' on"
+ "\n\tstdin 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 < 1)
+ return net_share_usage(c, argc, argv);
+
+ if (StrCaseCmp(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 0000000000..4e355e48b3
--- /dev/null
+++ b/source3/utils/net_status.c
@@ -0,0 +1,269 @@
+/*
+ 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 "utils/net.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(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ bool *parseable = (bool *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid)) {
+ return 0;
+ }
+
+ if (*parseable) {
+ d_printf("%s\\%s\\%s\\%s\\%s\n",
+ procid_str_static(&sessionid.pid), uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ } else {
+ d_printf("%7s %-12s %-12s %-12s (%s)\n",
+ procid_str_static(&sessionid.pid), uidtoname(sessionid.uid),
+ gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+ }
+
+ return 0;
+}
+
+static int net_status_sessions(struct net_context *c, int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ bool parseable;
+
+ if (c->display_usage) {
+ d_printf("Usage:\n"
+ "net status sessions [parseable]\n"
+ " Display open user sessions.\n"
+ " If parseable is specified, output is machine-"
+ "readable.\n");
+ 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");
+ d_printf("-------------------------------------------"
+ "------------------------\n");
+ }
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_fprintf(stderr, "%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, show_session, &parseable);
+ tdb_close(tdb);
+
+ return 0;
+}
+
+static int show_share(struct db_record *rec,
+ const struct connections_key *key,
+ const struct connections_data *crec,
+ void *state)
+{
+ if (crec->cnum == -1)
+ return 0;
+
+ if (!process_exists(crec->pid)) {
+ return 0;
+ }
+
+ d_printf("%-10.10s %s %-12s %s",
+ crec->servicename, procid_str_static(&crec->pid),
+ crec->machine,
+ time_to_asc(crec->start));
+
+ return 0;
+}
+
+struct sessionids {
+ int num_entries;
+ struct sessionid *entries;
+};
+
+static int collect_pid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ struct sessionid sessionid;
+
+ if (dbuf.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, dbuf.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.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] = sessionid;
+
+ return 0;
+}
+
+static int show_share_parseable(struct db_record *rec,
+ const struct connections_key *key,
+ const struct connections_data *crec,
+ void *state)
+{
+ struct sessionids *ids = (struct sessionids *)state;
+ int i;
+ bool guest = true;
+
+ if (crec->cnum == -1)
+ 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 (procid_equal(&id, &crec->pid)) {
+ guest = false;
+ break;
+ }
+ }
+
+ d_printf("%s\\%s\\%s\\%s\\%s\\%s\\%s",
+ crec->servicename,procid_str_static(&crec->pid),
+ guest ? "" : uidtoname(ids->entries[i].uid),
+ guest ? "" : gidtoname(ids->entries[i].gid),
+ crec->machine,
+ guest ? "" : ids->entries[i].hostname,
+ time_to_asc(crec->start));
+
+ return 0;
+}
+
+static int net_status_shares_parseable(struct net_context *c, int argc, const char **argv)
+{
+ struct sessionids ids;
+ TDB_CONTEXT *tdb;
+
+ ids.num_entries = 0;
+ ids.entries = NULL;
+
+ tdb = tdb_open_log(lock_path("sessionid.tdb"), 0,
+ TDB_DEFAULT, O_RDONLY, 0);
+
+ if (tdb == NULL) {
+ d_fprintf(stderr, "%s not initialised\n", lock_path("sessionid.tdb"));
+ return -1;
+ }
+
+ tdb_traverse(tdb, collect_pid, &ids);
+ tdb_close(tdb);
+
+ connections_forall(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("Usage:\n"
+ "net status shares [parseable]\n"
+ " Display open user shares.\n"
+ " If parseable is specified, output is machine-"
+ "readable.\n");
+ return 0;
+ }
+
+ if (argc == 0) {
+
+ d_printf("\nService pid machine "
+ "Connected at\n");
+ d_printf("-------------------------------------"
+ "------------------\n");
+
+ connections_forall(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,
+ "Show list of open sessions",
+ "net status sessions [parseable]\n"
+ " If parseable is specified, output is presented "
+ "in a machine-parseable fashion."
+ },
+ {
+ "shares",
+ net_status_shares,
+ NET_TRANSPORT_LOCAL,
+ "Show list of open shares",
+ "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_time.c b/source3/utils/net_time.c
new file mode 100644
index 0000000000..f569538fac
--- /dev/null
+++ b/source3/utils/net_time.c
@@ -0,0 +1,238 @@
+/*
+ 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"
+
+/*
+ return the time on a server. This does not require any authentication
+*/
+static time_t cli_servertime(const char *host, struct sockaddr_storage *pss, int *zone)
+{
+ struct nmb_name calling, called;
+ time_t ret = 0;
+ struct cli_state *cli = NULL;
+ NTSTATUS status;
+
+ cli = cli_initialise();
+ if (!cli) {
+ goto done;
+ }
+
+ status = cli_connect(cli, host, pss);
+ if (!NT_STATUS_IS_OK(status)) {
+ fprintf(stderr,"Can't contact server %s. Error %s\n", host, nt_errstr(status));
+ goto done;
+ }
+
+ make_nmb_name(&calling, global_myname(), 0x0);
+ if (host) {
+ make_nmb_name(&called, host, 0x20);
+ } else {
+ make_nmb_name(&called, "*SMBSERVER", 0x20);
+ }
+
+ if (!cli_session_request(cli, &calling, &called)) {
+ fprintf(stderr,"Session request failed\n");
+ goto done;
+ }
+ if (!cli_negprot(cli)) {
+ fprintf(stderr,"Protocol negotiation failed\n");
+ goto done;
+ }
+
+ ret = cli->servertime;
+ if (zone) *zone = cli->serverzone;
+
+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)
+{
+ static fstring s;
+ struct tm *tm;
+
+ tm = localtime(&t);
+ if (!tm) {
+ return "unknown";
+ }
+
+ fstr_sprintf(s, "%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);
+ return s;
+}
+
+int net_time_usage(struct net_context *c, int argc, const char **argv)
+{
+ d_printf(
+"net time\n\tdisplays time on a server\n\n"
+"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n"
+"net time set\n\truns /bin/date with the time from the server\n\n"
+"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n"
+"\n");
+ net_common_flags_usage(c, argc, argv);
+ return -1;
+}
+
+/* try to set the system clock using /bin/date */
+static int net_time_set(struct net_context *c, int argc, const char **argv)
+{
+ time_t t = nettime(c, NULL);
+ char *cmd;
+ int result;
+
+ if (t == 0) return -1;
+
+ /* yes, I know this is cheesy. Use "net time system" if you want to
+ roll your own. I'm putting this in as it works on a large number
+ of systems and the user has a choice in whether its used or not */
+ asprintf(&cmd, "/bin/date %s", systime(t));
+ result = system(cmd);
+ if (result)
+ d_fprintf(stderr, "%s failed. Error was (%s)\n",
+ cmd, strerror(errno));
+ free(cmd);
+
+ 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) {
+ d_printf("Usage:\n"
+ "net time system\n"
+ " Output remote time server time in a format ready "
+ "for /bin/date\n");
+ 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) {
+ d_printf("Usage:\n"
+ "net time zone\n"
+ " Display the remote time server's offset to UTC\n");
+ 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,
+ "Display time ready for /bin/date",
+ "net time system\n"
+ " Display time ready for /bin/date"
+ },
+ {
+ "set",
+ net_time_set,
+ NET_TRANSPORT_LOCAL,
+ "Set the system time from time server",
+ "net time set\n"
+ " Set the system time from time server"
+ },
+ {
+ "zone",
+ net_time_zone,
+ NET_TRANSPORT_LOCAL,
+ "Display timezone offset from UTC",
+ "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("Usage:\n");
+ d_printf("net time\n"
+ " Display the remote time server's time\n");
+ net_display_usage_from_functable(func);
+ return 0;
+ }
+
+ if (!c->opt_host && !c->opt_have_ip &&
+ !find_master_ip(c->opt_target_workgroup, &c->opt_dest_ip)) {
+ d_fprintf(stderr, "Could not locate a time server. Try "
+ "specifying a target host.\n");
+ net_time_usage(c, argc,argv);
+ return -1;
+ }
+
+ /* 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 0000000000..b98b6a13a7
--- /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(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 0000000000..8f263c636c
--- /dev/null
+++ b/source3/utils/net_usershare.c
@@ -0,0 +1,1073 @@
+/*
+ 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 "utils/net.h"
+
+struct {
+ const char *us_errstr;
+ enum usershare_err us_err;
+} us_errs [] = {
+ {"",USERSHARE_OK},
+ {"Malformed usershare file", USERSHARE_MALFORMED_FILE},
+ {"Bad version number", USERSHARE_BAD_VERSION},
+ {"Malformed path entry", USERSHARE_MALFORMED_PATH},
+ {"Malformed comment entryfile", USERSHARE_MALFORMED_COMMENT_DEF},
+ {"Malformed acl definition", USERSHARE_MALFORMED_ACL_DEF},
+ {"Acl parse error", USERSHARE_ACL_ERR},
+ {"Path not absolute", USERSHARE_PATH_NOT_ABSOLUTE},
+ {"Path is denied", USERSHARE_PATH_IS_DENIED},
+ {"Path not allowed", USERSHARE_PATH_NOT_ALLOWED},
+ {"Path is not a directory", USERSHARE_PATH_NOT_DIRECTORY},
+ {"System error", USERSHARE_POSIX_ERR},
+ {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 [-l|--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 -l or --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 [-l|--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 -l or --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 [-l|--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 -l or --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 [-l|--long] [wildcard sharename] to print info about a user defined share.\n"
+ "net usershare list [-l|--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)
+{
+ char *basepath = talloc_strdup(ctx, lp_usershare_path());
+
+ 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)
+{
+ char *us_path;
+ char *sharename;
+
+ if (argc != 1 || c->display_usage) {
+ return net_usershare_delete_usage(c, argc, argv);
+ }
+
+ if ((sharename = strdup_lower(argv[0])) == NULL) {
+ d_fprintf(stderr, "strdup 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ us_path = talloc_asprintf(talloc_tos(),
+ "%s/%s",
+ lp_usershare_path(),
+ sharename);
+ if (!us_path) {
+ SAFE_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));
+ SAFE_FREE(sharename);
+ return -1;
+ }
+ SAFE_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)
+{
+ SMB_STRUCT_DIR *dp;
+ SMB_STRUCT_DIRENT *de;
+ uid_t myuid = geteuid();
+ struct file_list *fl = NULL;
+ char *basepath = get_basepath(ctx);
+
+ if (!basepath) {
+ return -1;
+ }
+ dp = sys_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 = sys_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) {
+ sys_closedir(dp);
+ return -1;
+ }
+
+ if (sys_lstat(path, &sbuf) != 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_mode)) {
+ d_fprintf(stderr, "get_share_list: file %s is not a regular file. Ignoring.\n",
+ path );
+ continue;
+ }
+
+ if (only_ours && sbuf.st_uid != myuid) {
+ continue;
+ }
+
+ if (!unix_wild_match(wcard, n)) {
+ continue;
+ }
+
+ /* (Finally) - add to list. */
+ fl = TALLOC_P(ctx, struct file_list);
+ if (!fl) {
+ sys_closedir(dp);
+ return -1;
+ }
+ fl->pathname = talloc_strdup(ctx, n);
+ if (!fl->pathname) {
+ sys_closedir(dp);
+ return -1;
+ }
+
+ DLIST_ADD(flist, fl);
+ }
+
+ sys_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;
+ SEC_DESC *psd = NULL;
+ char *basepath;
+ char *sharepath = NULL;
+ char *comment = 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 = sys_open(basepath, O_RDONLY|O_NOFOLLOW, 0);
+#else
+ fd = sys_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) != 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_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);
+ 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,
+ &psd,
+ &guest_ok);
+
+ file_lines_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 {
+ fstring sidstr;
+ sid_to_fstring(sidstr,
+ &psd->dacl->aces[num_aces].trustee);
+ acl_str = talloc_asprintf_append(acl_str,
+ "%s",
+ 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;
+ }
+ }
+ }
+
+ if (pi->op == US_INFO_OP) {
+ d_printf("[%s]\n", fl->pathname );
+ 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", fl->pathname);
+ }
+
+ 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);
+ }
+
+ strlower_m(wcard);
+
+ 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)
+{
+ SMB_STRUCT_DIR *dp;
+ SMB_STRUCT_DIRENT *de;
+ int num_usershares = 0;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *basepath = get_basepath(ctx);
+
+ if (!basepath) {
+ return -1;
+ }
+
+ dp = sys_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 = sys_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) {
+ sys_closedir(dp);
+ return -1;
+ }
+
+ if (sys_lstat(path, &sbuf) != 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_mode)) {
+ d_fprintf(stderr, "count_num_usershares: file %s is not a regular file. Ignoring.\n",
+ path );
+ continue;
+ }
+ num_usershares++;
+ }
+
+ sys_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;
+ 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;
+
+ us_comment = "";
+ arg_acl = "S-1-1-0:R";
+
+ if (c->display_usage)
+ return net_usershare_add_usage(c, argc, argv);
+
+ switch (argc) {
+ case 0:
+ case 1:
+ default:
+ return net_usershare_add_usage(c, argc, argv);
+ case 2:
+ sharename = strdup_lower(argv[0]);
+ us_path = argv[1];
+ break;
+ case 3:
+ sharename = strdup_lower(argv[0]);
+ us_path = argv[1];
+ us_comment = argv[2];
+ break;
+ case 4:
+ sharename = strdup_lower(argv[0]);
+ us_path = argv[1];
+ us_comment = argv[2];
+ arg_acl = argv[3];
+ break;
+ case 5:
+ sharename = strdup_lower(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);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Construct the full path for the usershare file. */
+ full_path = get_basepath(ctx);
+ if (!full_path) {
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+ full_path_tmp = talloc_asprintf(ctx,
+ "%s/:tmpXXXXXX",
+ full_path);
+ if (!full_path_tmp) {
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ full_path = talloc_asprintf_append(full_path,
+ "/%s",
+ sharename);
+ if (!full_path) {
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Check the directory to be shared exists. */
+ if (sys_stat(us_path, &sbuf) != 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ if (!S_ISDIR(sbuf.st_mode)) {
+ d_fprintf(stderr, "net usershare add: path %s is not a directory.\n",
+ us_path );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ 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_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);
+ SAFE_FREE(sharename);
+ 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++) {
+ DOM_SID sid;
+ 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);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Get the name */
+ if ((name = talloc_strndup(ctx, pacl, pcolon - pacl)) == NULL) {
+ d_fprintf(stderr, "talloc_strndup failed\n");
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+ }
+ us_acl = talloc_asprintf_append(
+ us_acl, "%s:%c,", sid_string_tos(&sid), pcolon[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);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Create a temporary filename for this share. */
+ tmpfd = smb_mkstemp(full_path_tmp);
+
+ if (tmpfd == -1) {
+ d_fprintf(stderr, "net usershare add: cannot create tmp file %s\n",
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Ensure we opened the file we thought we did. */
+ if (sys_lstat(full_path_tmp, &lsbuf) != 0) {
+ d_fprintf(stderr, "net usershare add: cannot lstat tmp file %s\n",
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Check this is the same as the file we opened. */
+ if (sys_fstat(tmpfd, &sbuf) != 0) {
+ d_fprintf(stderr, "net usershare add: cannot fstat tmp file %s\n",
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ if (!S_ISREG(sbuf.st_mode) || sbuf.st_dev != lsbuf.st_dev || sbuf.st_ino != lsbuf.st_ino) {
+ d_fprintf(stderr, "net usershare add: tmp file %s is not a regular file ?\n",
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ if (fchmod(tmpfd, 0644) == -1) {
+ d_fprintf(stderr, "net usershare add: failed to fchmod tmp file %s to 0644n",
+ full_path_tmp );
+ TALLOC_FREE(ctx);
+ SAFE_FREE(sharename);
+ return -1;
+ }
+
+ /* Create the in-memory image of the file. */
+ file_img = talloc_strdup(ctx, "#VERSION 2\npath=");
+ file_img = talloc_asprintf_append(file_img, "%s\ncomment=%s\nusershare_acl=%s\nguest_ok=%c\n",
+ us_path, us_comment, us_acl, guest_ok ? 'y' : 'n');
+
+ 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);
+ SAFE_FREE(sharename);
+ 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);
+ SAFE_FREE(sharename);
+ 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);
+ }
+
+ SAFE_FREE(sharename);
+ 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);
+ }
+
+ strlower_m(wcard);
+
+ 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;
+
+ 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)
+{
+ SMB_STRUCT_DIR *dp;
+
+ struct functable func[] = {
+ {
+ "add",
+ net_usershare_add,
+ NET_TRANSPORT_LOCAL,
+ "Add/modify user defined share",
+ "net usershare add\n"
+ " Add/modify user defined share"
+ },
+ {
+ "delete",
+ net_usershare_delete,
+ NET_TRANSPORT_LOCAL,
+ "Delete user defined share",
+ "net usershare delete\n"
+ " Delete user defined share"
+ },
+ {
+ "info",
+ net_usershare_info,
+ NET_TRANSPORT_LOCAL,
+ "Display information about a user defined share",
+ "net usershare info\n"
+ " Display information about a user defined share"
+ },
+ {
+ "list",
+ net_usershare_list,
+ NET_TRANSPORT_LOCAL,
+ "List user defined shares",
+ "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 = sys_opendir(lp_usershare_path());
+ if (!dp) {
+ int err = errno;
+ d_fprintf(stderr, "net usershare: cannot open usershare directory %s. Error %s\n",
+ lp_usershare_path(), 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;
+ }
+ sys_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 0000000000..88850d29df
--- /dev/null
+++ b/source3/utils/net_util.c
@@ -0,0 +1,597 @@
+/*
+ * Unix SMB/CIFS implementation.
+ * Helper routines for net
+ * 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"
+
+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, DOM_SID *ret_sid,
+ enum lsa_SidType *ret_type)
+{
+ struct rpc_pipe_client *lsa_pipe;
+ POLICY_HND pol;
+ NTSTATUS result = NT_STATUS_OK;
+ const char **dom_names;
+ DOM_SID *sids;
+ enum lsa_SidType *types;
+
+ ZERO_STRUCT(pol);
+
+ result = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &lsa_pipe);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "Could not initialise lsa pipe\n");
+ return result;
+ }
+
+ result = rpccli_lsa_open_policy(lsa_pipe, mem_ctx, false,
+ SEC_RIGHTS_MAXIMUM_ALLOWED,
+ &pol);
+ if (!NT_STATUS_IS_OK(result)) {
+ d_fprintf(stderr, "open_policy failed: %s\n",
+ nt_errstr(result));
+ return result;
+ }
+
+ result = rpccli_lsa_lookup_names(lsa_pipe, mem_ctx, &pol, 1,
+ &name, &dom_names, 1, &sids, &types);
+
+ if (!NT_STATUS_IS_OK(result)) {
+ /* 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)) {
+ rpccli_lsa_Close(lsa_pipe, mem_ctx, &pol);
+ }
+ TALLOC_FREE(lsa_pipe);
+
+ return result;
+}
+
+/****************************************************************************
+ Connect to \\server\service.
+****************************************************************************/
+
+NTSTATUS connect_to_service(struct net_context *c,
+ struct cli_state **cli_ctx,
+ struct sockaddr_storage *server_ss,
+ const char *server_name,
+ const char *service_name,
+ const char *service_type)
+{
+ NTSTATUS nt_status;
+ int flags = 0;
+
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+
+ if (c->opt_kerberos) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS;
+ }
+
+ if (c->opt_kerberos && c->opt_password) {
+ flags |= CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+ }
+
+ nt_status = cli_full_connection(cli_ctx, NULL, server_name,
+ server_ss, c->opt_port,
+ service_name, service_type,
+ c->opt_user_name, c->opt_workgroup,
+ c->opt_password, flags, Undefined, NULL);
+ 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;
+ }
+
+ if (c->smb_encrypt) {
+ nt_status = cli_force_encryption(*cli_ctx,
+ c->opt_user_name,
+ c->opt_password,
+ c->opt_workgroup);
+
+ if (NT_STATUS_EQUAL(nt_status,NT_STATUS_NOT_SUPPORTED)) {
+ d_printf("Encryption required and "
+ "server that doesn't support "
+ "UNIX extensions - failing connect\n");
+ } else if (NT_STATUS_EQUAL(nt_status,NT_STATUS_UNKNOWN_REVISION)) {
+ d_printf("Encryption required and "
+ "can't get UNIX CIFS extensions "
+ "version from server.\n");
+ } else if (NT_STATUS_EQUAL(nt_status,NT_STATUS_UNSUPPORTED_COMPRESSION)) {
+ d_printf("Encryption required and "
+ "share %s doesn't support "
+ "encryption.\n", service_name);
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ d_printf("Encryption required and "
+ "setup failed with error %s.\n",
+ nt_errstr(nt_status));
+ }
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(*cli_ctx);
+ *cli_ctx = NULL;
+ }
+ }
+
+ return nt_status;
+}
+
+/****************************************************************************
+ Connect to \\server\ipc$.
+****************************************************************************/
+
+NTSTATUS connect_to_ipc(struct net_context *c,
+ struct cli_state **cli_ctx,
+ 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,
+ struct sockaddr_storage *server_ss,
+ const char *server_name)
+{
+ NTSTATUS nt_status;
+
+ nt_status = cli_full_connection(cli_ctx, c->opt_requester_name,
+ server_name, server_ss, c->opt_port,
+ "IPC$", "IPC",
+ "", "",
+ "", 0, Undefined, NULL);
+
+ 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;
+ }
+}
+
+/****************************************************************************
+ Return malloced user@realm for krb5 login.
+****************************************************************************/
+
+static char *get_user_and_realm(const char *username)
+{
+ char *user_and_realm = NULL;
+
+ if (!username) {
+ return NULL;
+ }
+ if (strchr_m(username, '@')) {
+ user_and_realm = SMB_STRDUP(username);
+ } else {
+ if (asprintf(&user_and_realm, "%s@%s", username, lp_realm()) == -1) {
+ user_and_realm = NULL;
+ }
+ }
+ return user_and_realm;
+}
+
+/****************************************************************************
+ Connect to \\server\ipc$ using KRB5.
+****************************************************************************/
+
+NTSTATUS connect_to_ipc_krb5(struct net_context *c,
+ struct cli_state **cli_ctx,
+ struct sockaddr_storage *server_ss,
+ const char *server_name)
+{
+ NTSTATUS nt_status;
+ char *user_and_realm = NULL;
+
+ /* FIXME: Should get existing kerberos ticket if possible. */
+ c->opt_password = net_prompt_pass(c, c->opt_user_name);
+ if (!c->opt_password) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ user_and_realm = get_user_and_realm(c->opt_user_name);
+ if (!user_and_realm) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ nt_status = cli_full_connection(cli_ctx, NULL, server_name,
+ server_ss, c->opt_port,
+ "IPC$", "IPC",
+ user_and_realm, c->opt_workgroup,
+ c->opt_password,
+ CLI_FULL_CONNECTION_USE_KERBEROS,
+ Undefined, NULL);
+
+ SAFE_FREE(user_and_realm);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(1,("Cannot connect to server using kerberos. Error was %s\n", nt_errstr(nt_status)));
+ return nt_status;
+ }
+
+ if (c->smb_encrypt) {
+ nt_status = cli_cm_force_encryption(*cli_ctx,
+ user_and_realm,
+ c->opt_password,
+ c->opt_workgroup,
+ "IPC$");
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(*cli_ctx);
+ *cli_ctx = NULL;
+ }
+ }
+
+ 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_syntax_id *interface)
+{
+ 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, interface,
+ &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", global_myname(), lp_realm()) == -1) {
+ return -1;
+ }
+ c->opt_user_name = user_name;
+ return 0;
+}
+
+/****************************************************************************
+ Use the machine account name and password for this session.
+****************************************************************************/
+
+int net_use_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$", global_myname()) == -1) {
+ return -1;
+ }
+ c->opt_user_name = user_name;
+ 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)) {
+ 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)) */
+ if (!resolve_name(d, &msbrow_ss, 0x1B)) {
+ 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)) {
+ /* 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,
+ 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, cli->desthost );
+
+ SAFE_FREE(server_name);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ d_fprintf(stderr, "Connection failed: %s\n",
+ nt_errstr(nt_status));
+ cli = NULL;
+ }
+
+done:
+ if (pcli != NULL) {
+ *pcli = cli;
+ }
+ return nt_status;
+}
+
+/****************************************************************************
+****************************************************************************/
+
+const char *net_prompt_pass(struct net_context *c, const char *user)
+{
+ char *prompt = NULL;
+ const char *pass = NULL;
+
+ if (c->opt_password) {
+ return c->opt_password;
+ }
+
+ if (c->opt_machine_pass) {
+ return NULL;
+ }
+
+ if (c->opt_kerberos && !c->opt_user_specified) {
+ return NULL;
+ }
+
+ asprintf(&prompt, "Enter %s's password:", user);
+ if (!prompt) {
+ return NULL;
+ }
+
+ pass = getpass(prompt);
+ SAFE_FREE(prompt);
+
+ return pass;
+}
+
+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(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);
+ }
+}
diff --git a/source3/utils/netlookup.c b/source3/utils/netlookup.c
new file mode 100644
index 0000000000..14f2dddebc
--- /dev/null
+++ b/source3/utils/netlookup.c
@@ -0,0 +1,227 @@
+/*
+ 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"
+
+/********************************************************
+ 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;
+ POLICY_HND 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;
+
+ *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_P(ctx, struct con_struct);
+ if (!cs) {
+ *perr = NT_STATUS_NO_MEMORY;
+ return NULL;
+ }
+
+ ZERO_STRUCTP(cs);
+ talloc_set_destructor(cs, cs_destructor);
+
+ /* Connect to localhost with given username/password. */
+ /* JRA. Pretty sure we can just do this anonymously.... */
+#if 0
+ if (!opt_password && !opt_machine_pass) {
+ char *pass = getpass("Password:");
+ if (pass) {
+ opt_password = SMB_STRDUP(pass);
+ }
+ }
+#endif
+
+ nt_status = cli_full_connection(&cs->cli, global_myname(), global_myname(),
+ &loopback_ss, 0,
+ "IPC$", "IPC",
+#if 0
+ c->opt_user_name,
+ c->opt_workgroup,
+ c->opt_password,
+#else
+ "",
+ c->opt_workgroup,
+ "",
+#endif
+ 0,
+ Undefined,
+ NULL);
+
+ 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.syntax_id,
+ &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_RIGHTS_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,
+ 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, DOM_SID *pret_sid)
+{
+ NTSTATUS nt_status;
+ struct con_struct *csp = NULL;
+ 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 0000000000..c04b628b1b
--- /dev/null
+++ b/source3/utils/nmblookup.c
@@ -0,0 +1,369 @@
+/*
+ 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"
+
+extern bool AllowDebugChange;
+
+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_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", sock_addr));
+ return false;
+ }
+ ServerFD = open_socket_in( SOCK_DGRAM,
+ (RootPort ? 137 : 0),
+ (RootPort ? 0 : 3),
+ &ss, true );
+
+ if (ServerFD == -1) {
+ 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 void do_node_status(int fd,
+ const char *name,
+ int type,
+ struct sockaddr_storage *pss)
+{
+ struct nmb_name nname;
+ int count, i, j;
+ NODE_STATUS_STRUCT *status;
+ struct node_status_extra extra;
+ fstring cleanname;
+ char addr[INET6_ADDRSTRLEN];
+
+ 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(fd, &nname, pss, &count, &extra);
+ if (status) {
+ for (i=0;i<count;i++) {
+ pull_ascii_fstring(cleanname, status[i].name);
+ for (j=0;cleanname[j];j++) {
+ if (!isprint((int)cleanname[j])) {
+ cleanname[j] = '.';
+ }
+ }
+ d_printf("\t%-15s <%02x> - %s\n",
+ cleanname,status[i].type,
+ node_status_flags(status[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");
+ SAFE_FREE(status);
+ } else {
+ d_printf("No reply from %s\n\n",addr);
+ }
+}
+
+
+/****************************************************************************
+ Send out one query.
+****************************************************************************/
+
+static bool query_one(const char *lookup, unsigned int lookup_type)
+{
+ int j, count, flags = 0;
+ struct sockaddr_storage *ip_list=NULL;
+
+ if (got_bcast) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &bcast_addr);
+ d_printf("querying %s on %s\n", lookup, addr);
+ ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast,
+ use_bcast?true:recursion_desired,
+ &bcast_addr, &count, &flags, NULL);
+ } else {
+ const struct in_addr *bcast;
+ for (j=iface_count() - 1;
+ !ip_list && j >= 0;
+ j--) {
+ char addr[INET6_ADDRSTRLEN];
+ struct sockaddr_storage bcast_ss;
+
+ bcast = iface_n_bcast_v4(j);
+ if (!bcast) {
+ continue;
+ }
+ in_addr_to_sockaddr_storage(&bcast_ss, *bcast);
+ print_sockaddr(addr, sizeof(addr), &bcast_ss);
+ d_printf("querying %s on %s\n",
+ lookup, addr);
+ ip_list = name_query(ServerFD,lookup,lookup_type,
+ use_bcast,
+ use_bcast?True:recursion_desired,
+ &bcast_ss,&count, &flags, NULL);
+ }
+ }
+
+ if (!ip_list) {
+ 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) {
+ do_node_status(ServerFD, lookup,
+ lookup_type, &ip_list[j]);
+ }
+ }
+
+ safe_free(ip_list);
+
+ return (ip_list != NULL);
+}
+
+
+/****************************************************************************
+ main program
+****************************************************************************/
+int main(int argc,char *argv[])
+{
+ int opt;
+ unsigned int lookup_type = 0x0;
+ fstring lookup;
+ static bool find_master=False;
+ static bool lookup_by_ip = False;
+ poptContext pc;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'B', POPT_ARG_STRING, NULL, 'B', "Specify address to use for broadcasts", "BROADCAST-ADDRESS" },
+ { "flags", 'f', POPT_ARG_NONE, NULL, 'f', "List the NMB flags returned" },
+ { "unicast", 'U', POPT_ARG_STRING, NULL, 'U', "Specify address to use for unicast" },
+ { "master-browser", 'M', POPT_ARG_NONE, NULL, 'M', "Search for a master browser" },
+ { "recursion", 'R', POPT_ARG_NONE, NULL, 'R', "Set recursion desired in package" },
+ { "status", 'S', POPT_ARG_NONE, NULL, 'S', "Lookup node status as well" },
+ { "translate", 'T', POPT_ARG_NONE, NULL, 'T', "Translate IP addresses into names" },
+ { "root-port", 'r', POPT_ARG_NONE, NULL, 'r', "Use root port 137 (Win95 only replies to this)" },
+ { "lookup-by-ip", 'A', POPT_ARG_NONE, NULL, 'A', "Do a node status on <name> as an IP Address" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ { 0, 0, 0, 0 }
+ };
+
+ *lookup = 0;
+
+ load_case_tables();
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("nmblookup", argc, (const char **)argv,
+ long_options, POPT_CONTEXT_KEEP_FIRST);
+
+ poptSetOtherOptionHelp(pc, "<NODE> ...");
+
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'f':
+ give_flags = true;
+ break;
+ case 'M':
+ find_master = true;
+ break;
+ case 'R':
+ 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;
+ }
+ }
+
+ poptGetArg(pc); /* Remove argv[0] */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ if (!lp_load(get_dyn_CONFIGFILE(),True,False,False,True)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ get_dyn_CONFIGFILE());
+ }
+
+ load_interfaces();
+ if (!open_sockets()) {
+ return(1);
+ }
+
+ while(poptPeekArg(pc)) {
+ char *p;
+ struct in_addr ip;
+
+ fstrcpy(lookup,poptGetArg(pc));
+
+ if(lookup_by_ip) {
+ struct sockaddr_storage ss;
+ (void)interpret_addr2(&ip, lookup);
+ in_addr_to_sockaddr_storage(&ss, ip);
+ fstrcpy(lookup,"*");
+ do_node_status(ServerFD, lookup, lookup_type, &ss);
+ 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);
+ }
+
+ if (!query_one(lookup, lookup_type)) {
+ d_printf( "name_query failed to find name %s", lookup );
+ if( 0 != lookup_type ) {
+ d_printf( "#%02x", lookup_type );
+ }
+ d_printf( "\n" );
+ }
+ }
+
+ poptFreeContext(pc);
+ TALLOC_FREE(frame);
+ return(0);
+}
diff --git a/source3/utils/ntlm_auth.c b/source3/utils/ntlm_auth.c
new file mode 100644
index 0000000000..4586086d73
--- /dev/null
+++ b/source3/utils/ntlm_auth.c
@@ -0,0 +1,2491 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General 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"
+
+#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,
+ 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
+};
+
+enum ntlm_auth_svr_state {
+ SERVER_INITIAL = 0,
+ SERVER_CHALLENGE,
+ SERVER_FINISHED,
+ SERVER_ERROR
+};
+
+struct ntlm_auth_state {
+ TALLOC_CTX *mem_ctx;
+ enum stdio_helper_mode helper_mode;
+ enum ntlm_auth_cli_state cli_state;
+ enum ntlm_auth_svr_state svr_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;
+};
+
+typedef void (*stdio_helper_function)(struct ntlm_auth_state *state, char *buf,
+ int length);
+
+static void manage_squid_basic_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_squid_ntlmssp_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_client_ntlmssp_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_gss_spnego_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_gss_spnego_client_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_ntlm_server_1_request (struct ntlm_auth_state *state,
+ char *buf, int length);
+
+static void manage_ntlm_change_password_1_request(struct ntlm_auth_state *state,
+ char *buf, int length);
+
+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, "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 const char *require_membership_of;
+static const char *require_membership_of_sid;
+
+static char winbind_separator(void)
+{
+ struct winbindd_response response;
+ static bool got_sep;
+ static char sep;
+
+ if (got_sep)
+ return sep;
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request_response(WINBINDD_INFO, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ d_printf("could not obtain winbind separator!\n");
+ return *lp_winbind_separator();
+ }
+
+ sep = response.data.info.winbind_separator;
+ got_sep = True;
+
+ if (!sep) {
+ d_printf("winbind separator was NULL!\n");
+ return *lp_winbind_separator();
+ }
+
+ return sep;
+}
+
+const char *get_winbind_domain(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_domain;
+ if (*winbind_domain) {
+ return winbind_domain;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request_response(WINBINDD_DOMAIN_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind domain name!\n"));
+ return lp_workgroup();
+ }
+
+ fstrcpy(winbind_domain, response.data.domain_name);
+
+ return winbind_domain;
+
+}
+
+const char *get_winbind_netbios_name(void)
+{
+ struct winbindd_response response;
+
+ static fstring winbind_netbios_name;
+
+ if (*winbind_netbios_name) {
+ return winbind_netbios_name;
+ }
+
+ ZERO_STRUCT(response);
+
+ /* Send off request */
+
+ if (winbindd_request_response(WINBINDD_NETBIOS_NAME, NULL, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("could not obtain winbind netbios name!\n"));
+ return global_myname();
+ }
+
+ fstrcpy(winbind_netbios_name, response.data.netbios_name);
+
+ 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;
+ strupper_m(domain);
+
+ return True;
+}
+
+static bool get_require_membership_sid(void) {
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ if (!require_membership_of) {
+ return True;
+ }
+
+ if (require_membership_of_sid) {
+ return True;
+ }
+
+ /* Otherwise, ask winbindd for the name->sid request */
+
+ ZERO_STRUCT(request);
+ ZERO_STRUCT(response);
+
+ if (!parse_ntlm_auth_domain_user(require_membership_of,
+ request.data.name.dom_name,
+ request.data.name.name)) {
+ DEBUG(0, ("Could not parse %s into seperate domain/name parts!\n",
+ require_membership_of));
+ return False;
+ }
+
+ if (winbindd_request_response(WINBINDD_LOOKUPNAME, &request, &response) !=
+ NSS_STATUS_SUCCESS) {
+ DEBUG(0, ("Winbindd lookupname failed to resolve %s into a SID!\n",
+ require_membership_of));
+ return False;
+ }
+
+ require_membership_of_sid = SMB_STRDUP(response.data.sid.sid);
+
+ if (require_membership_of_sid)
+ return True;
+
+ return False;
+}
+/* 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;
+ NSS_STATUS result;
+
+ 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));
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH, &request, &response);
+
+ /* Display response */
+
+ if (stdout_diagnostics) {
+ if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) {
+ d_printf("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 ((result != NSS_STATUS_SUCCESS) && (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 (result == NSS_STATUS_SUCCESS);
+}
+
+/* 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 flags,
+ uint8 lm_key[8],
+ uint8 user_session_key[16],
+ char **error_string,
+ char **unix_name)
+{
+ NTSTATUS nt_status;
+ NSS_STATUS result;
+ struct winbindd_request request;
+ struct winbindd_response response;
+
+ 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 = MSV1_0_ALLOW_WORKSTATION_TRUST_ACCOUNT | MSV1_0_ALLOW_SERVER_TRUST_ACCOUNT;
+
+ 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) {
+ memcpy(request.data.auth_crap.lm_resp,
+ lm_response->data,
+ MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp)));
+ request.data.auth_crap.lm_resp_len = lm_response->length;
+ }
+
+ if (nt_response && nt_response->length) {
+ memcpy(request.data.auth_crap.nt_resp,
+ nt_response->data,
+ MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp)));
+ request.data.auth_crap.nt_resp_len = nt_response->length;
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ if ((result != NSS_STATUS_SUCCESS) && (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;
+ }
+
+ 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;
+ NSS_STATUS result;
+ 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;
+ }
+
+ result = winbindd_request_response(WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, &request, &response);
+
+ /* Display response */
+
+ if ((result != NSS_STATUS_SUCCESS) && (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;
+}
+
+static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
+{
+ static const char zeros[16] = { 0, };
+ NTSTATUS nt_status;
+ char *error_string;
+ uint8 lm_key[8];
+ uint8 user_sess_key[16];
+ char *unix_name;
+
+ nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain,
+ ntlmssp_state->workstation,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ WBFLAG_PAM_LMKEY | WBFLAG_PAM_USER_SESSION_KEY | WBFLAG_PAM_UNIX_NAME,
+ lm_key, user_sess_key,
+ &error_string, &unix_name);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ if (memcmp(lm_key, zeros, 8) != 0) {
+ *lm_session_key = data_blob(NULL, 16);
+ memcpy(lm_session_key->data, lm_key, 8);
+ memset(lm_session_key->data+8, '\0', 8);
+ }
+
+ if (memcmp(user_sess_key, zeros, 16) != 0) {
+ *user_session_key = data_blob(user_sess_key, 16);
+ }
+ ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name);
+ SAFE_FREE(unix_name);
+ } else {
+ DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3,
+ ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user,
+ ntlmssp_state->workstation,
+ error_string ? error_string : "unknown error (NULL)"));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *user_session_key, DATA_BLOB *lm_session_key)
+{
+ NTSTATUS nt_status;
+ uint8 lm_pw[16], nt_pw[16];
+
+ nt_lm_owf_gen (opt_password, nt_pw, lm_pw);
+
+ nt_status = ntlm_password_check(ntlmssp_state->mem_ctx,
+ &ntlmssp_state->chal,
+ &ntlmssp_state->lm_resp,
+ &ntlmssp_state->nt_resp,
+ NULL, NULL,
+ ntlmssp_state->user,
+ ntlmssp_state->user,
+ ntlmssp_state->domain,
+ lm_pw, nt_pw, user_session_key, lm_session_key);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx,
+ "%s%c%s", ntlmssp_state->domain,
+ *lp_winbind_separator(),
+ ntlmssp_state->user);
+ } else {
+ DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n",
+ ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation,
+ nt_errstr(nt_status)));
+ ntlmssp_state->auth_context = NULL;
+ }
+ return nt_status;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state)
+{
+ NTSTATUS status;
+ if ( (opt_username == NULL) || (opt_domain == NULL) ) {
+ status = NT_STATUS_UNSUCCESSFUL;
+ DEBUG(1, ("Need username and domain for NTLMSSP\n"));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = ntlmssp_client_start(client_ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP client: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_username(*client_ntlmssp_state, opt_username);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set username: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set domain: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+
+ if (opt_password) {
+ status = ntlmssp_set_password(*client_ntlmssp_state, opt_password);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not set password: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(client_ntlmssp_state);
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state)
+{
+ NTSTATUS status = ntlmssp_server_start(ntlmssp_state);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1, ("Could not start NTLMSSP server: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+
+ /* Have we been given a local password, or should we ask winbind? */
+ if (opt_password) {
+ (*ntlmssp_state)->check_password = local_pw_check;
+ (*ntlmssp_state)->get_domain = lp_workgroup;
+ (*ntlmssp_state)->get_global_myname = global_myname;
+ } else {
+ (*ntlmssp_state)->check_password = winbind_pw_check;
+ (*ntlmssp_state)->get_domain = get_winbind_domain;
+ (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name;
+ }
+ return NT_STATUS_OK;
+}
+
+/*******************************************************************
+ Used by firefox to drive NTLM auth to IIS servers.
+*******************************************************************/
+
+static NTSTATUS do_ccache_ntlm_auth(DATA_BLOB initial_msg, DATA_BLOB challenge_msg,
+ DATA_BLOB *reply)
+{
+ struct winbindd_request wb_request;
+ struct winbindd_response wb_response;
+ NSS_STATUS result;
+
+ /* get winbindd to do the ntlmssp step on our behalf */
+ ZERO_STRUCT(wb_request);
+ ZERO_STRUCT(wb_response);
+
+ fstr_sprintf(wb_request.data.ccache_ntlm_auth.user,
+ "%s%c%s", opt_domain, winbind_separator(), opt_username);
+ wb_request.data.ccache_ntlm_auth.uid = geteuid();
+ wb_request.data.ccache_ntlm_auth.initial_blob_len = initial_msg.length;
+ wb_request.data.ccache_ntlm_auth.challenge_blob_len = challenge_msg.length;
+ wb_request.extra_len = initial_msg.length + challenge_msg.length;
+
+ if (wb_request.extra_len > 0) {
+ wb_request.extra_data.data = SMB_MALLOC_ARRAY(char, wb_request.extra_len);
+ if (wb_request.extra_data.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memcpy(wb_request.extra_data.data, initial_msg.data, initial_msg.length);
+ memcpy(wb_request.extra_data.data + initial_msg.length,
+ challenge_msg.data, challenge_msg.length);
+ }
+
+ result = winbindd_request_response(WINBINDD_CCACHE_NTLMAUTH, &wb_request, &wb_response);
+ SAFE_FREE(wb_request.extra_data.data);
+
+ if (result != NSS_STATUS_SUCCESS) {
+ winbindd_free_response(&wb_response);
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ if (reply) {
+ *reply = data_blob(wb_response.extra_data.data,
+ wb_response.data.ccache_ntlm_auth.auth_blob_len);
+ if (wb_response.data.ccache_ntlm_auth.auth_blob_len > 0 &&
+ reply->data == NULL) {
+ winbindd_free_response(&wb_response);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ winbindd_free_response(&wb_response);
+ return NT_STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+static void manage_squid_ntlmssp_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ if(strncmp(buf, "SF ", 3) == 0){
+ DEBUG(10, ("Setting flags to negotioate\n"));
+ TALLOC_FREE(state->want_feature_list);
+ state->want_feature_list = talloc_strdup(state->mem_ctx,
+ buf+3);
+ x_fprintf(x_stdout, "OK\n");
+ return;
+ }
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob_null;
+ }
+
+ if ((strncmp(buf, "PW ", 3) == 0)) {
+ /* The calling application wants us to use a local password
+ * (rather than winbindd) */
+
+ opt_password = SMB_STRNDUP((const char *)request.data,
+ request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH Out of memory\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (state->ntlmssp_state)
+ ntlmssp_end(&state->ntlmssp_state);
+ state->svr_state = SERVER_INITIAL;
+ } else if (strncmp(buf, "KK", 2) == 0) {
+ /* No special preprocessing required */
+ } else if (strncmp(buf, "GF", 2) == 0) {
+ DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
+
+ if (state->svr_state == SERVER_FINISHED) {
+ x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
+ }
+ else {
+ x_fprintf(x_stdout, "BH\n");
+ }
+ data_blob_free(&request);
+ return;
+ } else if (strncmp(buf, "GK", 2) == 0) {
+ DEBUG(10, ("Requested NTLMSSP session key\n"));
+ if(state->have_session_key) {
+ char *key64 = base64_encode_data_blob(state->mem_ctx,
+ state->session_key);
+ x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
+ TALLOC_FREE(key64);
+ } else {
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ data_blob_free(&request);
+ return;
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
+ return;
+ }
+
+ if (!state->ntlmssp_state) {
+ nt_status = ntlm_auth_start_ntlmssp_server(
+ &state->ntlmssp_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ ntlmssp_want_feature_list(state->ntlmssp_state,
+ state->want_feature_list);
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, request.data, request.length);
+
+ nt_status = ntlmssp_update(state->ntlmssp_state, request, &reply);
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
+ reply);
+ x_fprintf(x_stdout, "TT %s\n", reply_base64);
+ TALLOC_FREE(reply_base64);
+ data_blob_free(&reply);
+ state->svr_state = SERVER_CHALLENGE;
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+
+ ntlmssp_end(&state->ntlmssp_state);
+ } else if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status));
+ DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status)));
+ } else {
+ x_fprintf(x_stdout, "AF %s\n",
+ (char *)state->ntlmssp_state->auth_context);
+ DEBUG(10, ("NTLMSSP OK!\n"));
+
+ if(state->have_session_key)
+ data_blob_free(&state->session_key);
+ state->session_key = data_blob(
+ state->ntlmssp_state->session_key.data,
+ state->ntlmssp_state->session_key.length);
+ state->neg_flags = state->ntlmssp_state->neg_flags;
+ state->have_session_key = true;
+ state->svr_state = SERVER_FINISHED;
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_client_ntlmssp_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ DATA_BLOB request, reply;
+ NTSTATUS nt_status;
+
+ if (!opt_username || !*opt_username) {
+ x_fprintf(x_stderr, "username must be specified!\n\n");
+ exit(1);
+ }
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
+ return;
+ }
+
+ if (strlen(buf) > 3) {
+ if(strncmp(buf, "SF ", 3) == 0) {
+ DEBUG(10, ("Looking for flags to negotiate\n"));
+ talloc_free(state->want_feature_list);
+ state->want_feature_list = talloc_strdup(state->mem_ctx,
+ buf+3);
+ x_fprintf(x_stdout, "OK\n");
+ return;
+ }
+ request = base64_decode_data_blob(buf + 3);
+ } else {
+ request = data_blob_null;
+ }
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = SMB_STRNDUP((const char *)request.data,
+ request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH Out of memory\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if (!state->ntlmssp_state && use_cached_creds) {
+ /* check whether cached credentials are usable. */
+ DATA_BLOB empty_blob = data_blob_null;
+
+ nt_status = do_ccache_ntlm_auth(empty_blob, empty_blob, NULL);
+ if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ /* failed to use cached creds */
+ use_cached_creds = False;
+ }
+ }
+
+ if (opt_password == NULL && !use_cached_creds) {
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry asking for the
+ negotiate. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (state->ntlmssp_state)
+ ntlmssp_end(&state->ntlmssp_state);
+ state->cli_state = CLIENT_INITIAL;
+ } else if (strncmp(buf, "TT", 2) == 0) {
+ /* No special preprocessing required */
+ } else if (strncmp(buf, "GF", 2) == 0) {
+ DEBUG(10, ("Requested negotiated NTLMSSP flags\n"));
+
+ if(state->cli_state == CLIENT_FINISHED) {
+ x_fprintf(x_stdout, "GF 0x%08x\n", state->neg_flags);
+ }
+ else {
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ data_blob_free(&request);
+ return;
+ } else if (strncmp(buf, "GK", 2) == 0 ) {
+ DEBUG(10, ("Requested session key\n"));
+
+ if(state->cli_state == CLIENT_FINISHED) {
+ char *key64 = base64_encode_data_blob(state->mem_ctx,
+ state->session_key);
+ x_fprintf(x_stdout, "GK %s\n", key64?key64:"<NULL>");
+ TALLOC_FREE(key64);
+ }
+ else {
+ x_fprintf(x_stdout, "BH\n");
+ }
+
+ data_blob_free(&request);
+ return;
+ } else {
+ DEBUG(1, ("NTLMSSP query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH NTLMSSP query invalid\n");
+ return;
+ }
+
+ if (!state->ntlmssp_state) {
+ nt_status = ntlm_auth_start_ntlmssp_client(
+ &state->ntlmssp_state);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ return;
+ }
+ ntlmssp_want_feature_list(state->ntlmssp_state,
+ state->want_feature_list);
+ state->initial_message = data_blob_null;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, request.data, request.length);
+
+ if (use_cached_creds && !opt_password &&
+ (state->cli_state == CLIENT_RESPONSE)) {
+ nt_status = do_ccache_ntlm_auth(state->initial_message, request,
+ &reply);
+ } else {
+ nt_status = ntlmssp_update(state->ntlmssp_state, request,
+ &reply);
+ }
+
+ if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ char *reply_base64 = base64_encode_data_blob(state->mem_ctx,
+ reply);
+ if (state->cli_state == CLIENT_INITIAL) {
+ x_fprintf(x_stdout, "YR %s\n", reply_base64);
+ state->initial_message = reply;
+ state->cli_state = CLIENT_RESPONSE;
+ } else {
+ x_fprintf(x_stdout, "KK %s\n", reply_base64);
+ data_blob_free(&reply);
+ }
+ TALLOC_FREE(reply_base64);
+ DEBUG(10, ("NTLMSSP challenge\n"));
+ } else if (NT_STATUS_IS_OK(nt_status)) {
+ char *reply_base64 = base64_encode_data_blob(talloc_tos(),
+ reply);
+ x_fprintf(x_stdout, "AF %s\n", reply_base64);
+ TALLOC_FREE(reply_base64);
+
+ if(state->have_session_key)
+ data_blob_free(&state->session_key);
+
+ state->session_key = data_blob(
+ state->ntlmssp_state->session_key.data,
+ state->ntlmssp_state->session_key.length);
+ state->neg_flags = state->ntlmssp_state->neg_flags;
+ state->have_session_key = true;
+
+ DEBUG(10, ("NTLMSSP OK!\n"));
+ state->cli_state = CLIENT_FINISHED;
+ if (state->ntlmssp_state)
+ ntlmssp_end(&state->ntlmssp_state);
+ } else {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status));
+ DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status)));
+ state->cli_state = CLIENT_ERROR;
+ if (state->ntlmssp_state)
+ ntlmssp_end(&state->ntlmssp_state);
+ }
+
+ data_blob_free(&request);
+}
+
+static void manage_squid_basic_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ char *user, *pass;
+ user=buf;
+
+ pass=(char *)memchr(buf,' ',length);
+ if (!pass) {
+ DEBUG(2, ("Password not found. Denying access\n"));
+ x_fprintf(x_stdout, "ERR\n");
+ return;
+ }
+ *pass='\0';
+ pass++;
+
+ if (state->helper_mode == SQUID_2_5_BASIC) {
+ rfc1738_unescape(user);
+ rfc1738_unescape(pass);
+ }
+
+ if (check_plaintext_auth(user, pass, False)) {
+ x_fprintf(x_stdout, "OK\n");
+ } else {
+ x_fprintf(x_stdout, "ERR\n");
+ }
+}
+
+static void offer_gss_spnego_mechs(void) {
+
+ DATA_BLOB token;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+ char *reply_base64;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *principal;
+ char *myname_lower;
+
+ ZERO_STRUCT(spnego);
+
+ myname_lower = talloc_strdup(ctx, global_myname());
+ if (!myname_lower) {
+ return;
+ }
+ strlower_m(myname_lower);
+
+ principal = talloc_asprintf(ctx, "%s$@%s", myname_lower, lp_realm());
+ if (!principal) {
+ return;
+ }
+
+ /* Server negTokenInit (mech offerings) */
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = SMB_XMALLOC_ARRAY(const char *, 2);
+#ifdef HAVE_KRB5
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD);
+ spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[2] = NULL;
+#else
+ spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP);
+ spnego.negTokenInit.mechTypes[1] = NULL;
+#endif
+
+
+ spnego.negTokenInit.mechListMIC = data_blob(principal,
+ strlen(principal));
+
+ len = write_spnego_data(&token, &spnego);
+ free_spnego_data(&spnego);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(talloc_tos(), token);
+ x_fprintf(x_stdout, "TT %s *\n", reply_base64);
+
+ TALLOC_FREE(reply_base64);
+ data_blob_free(&token);
+ DEBUG(10, ("sent SPNEGO negTokenInit\n"));
+ return;
+}
+
+static void manage_gss_spnego_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ static NTLMSSP_STATE *ntlmssp_state = NULL;
+ SPNEGO_DATA request, response;
+ DATA_BLOB token;
+ NTSTATUS status;
+ ssize_t len;
+ TALLOC_CTX *ctx = talloc_tos();
+
+ char *user = NULL;
+ char *domain = NULL;
+
+ const char *reply_code;
+ char *reply_base64;
+ char *reply_argument = NULL;
+
+ if (strlen(buf) < 2) {
+ DEBUG(1, ("SPENGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH SPENGO query invalid\n");
+ return;
+ }
+
+ if (strncmp(buf, "YR", 2) == 0) {
+ if (ntlmssp_state)
+ ntlmssp_end(&ntlmssp_state);
+ } else if (strncmp(buf, "KK", 2) == 0) {
+ ;
+ } else {
+ DEBUG(1, ("SPENGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH SPENGO query invalid\n");
+ return;
+ }
+
+ if ( (strlen(buf) == 2)) {
+
+ /* no client data, get the negTokenInit offering
+ mechanisms */
+
+ offer_gss_spnego_mechs();
+ return;
+ }
+
+ /* All subsequent requests have a blob. This might be negTokenInit or negTokenTarg */
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH GSS-SPNEGO query invalid\n");
+ return;
+ }
+
+ token = base64_decode_data_blob(buf + 3);
+ len = read_spnego_data(token, &request);
+ data_blob_free(&token);
+
+ if (len == -1) {
+ DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf));
+ x_fprintf(x_stdout, "BH GSS-SPNEGO query invalid\n");
+ return;
+ }
+
+ if (request.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* Second request from Client. This is where the
+ client offers its mechanism to use. */
+
+ if ( (request.negTokenInit.mechTypes == NULL) ||
+ (request.negTokenInit.mechTypes[0] == NULL) ) {
+ DEBUG(1, ("Client did not offer any mechanism"));
+ x_fprintf(x_stdout, "BH Client did not offer any "
+ "mechanism\n");
+ return;
+ }
+
+ status = NT_STATUS_UNSUCCESSFUL;
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) {
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide NTLMSSP data\n"));
+ x_fprintf(x_stdout, "BH Client did not provide "
+ "NTLMSSP data\n");
+ return;
+ }
+
+ if ( ntlmssp_state != NULL ) {
+ DEBUG(1, ("Client wants a new NTLMSSP challenge, but "
+ "already got one\n"));
+ x_fprintf(x_stdout, "BH Client wants a new "
+ "NTLMSSP challenge, but "
+ "already got one\n");
+ ntlmssp_end(&ntlmssp_state);
+ return;
+ }
+
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return;
+ }
+
+ DEBUG(10, ("got NTLMSSP packet:\n"));
+ dump_data(10, request.negTokenInit.mechToken.data,
+ request.negTokenInit.mechToken.length);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob_null;
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenInit.mechToken,
+ &response.negTokenTarg.responseToken);
+ }
+
+#ifdef HAVE_KRB5
+ if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) {
+
+ TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request");
+ char *principal;
+ DATA_BLOB ap_rep;
+ DATA_BLOB session_key;
+ struct PAC_DATA *pac_data = NULL;
+
+ if ( request.negTokenInit.mechToken.data == NULL ) {
+ DEBUG(1, ("Client did not provide Kerberos data\n"));
+ x_fprintf(x_stdout, "BH Client did not provide "
+ "Kerberos data\n");
+ return;
+ }
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = SMB_STRDUP(OID_KERBEROS5_OLD);
+ response.negTokenTarg.mechListMIC = data_blob_null;
+ response.negTokenTarg.responseToken = data_blob_null;
+
+ status = ads_verify_ticket(mem_ctx, lp_realm(), 0,
+ &request.negTokenInit.mechToken,
+ &principal, &pac_data, &ap_rep,
+ &session_key, True);
+
+ talloc_destroy(mem_ctx);
+
+ /* Now in "principal" we have the name we are
+ authenticated as. */
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ domain = strchr_m(principal, '@');
+
+ if (domain == NULL) {
+ DEBUG(1, ("Did not get a valid principal "
+ "from ads_verify_ticket\n"));
+ x_fprintf(x_stdout, "BH Did not get a "
+ "valid principal from "
+ "ads_verify_ticket\n");
+ return;
+ }
+
+ *domain++ = '\0';
+ domain = SMB_STRDUP(domain);
+ user = SMB_STRDUP(principal);
+
+ data_blob_free(&ap_rep);
+
+ SAFE_FREE(principal);
+ }
+ }
+#endif
+
+ } else {
+
+ if ( (request.negTokenTarg.supportedMech == NULL) ||
+ ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) {
+ /* Kerberos should never send a negTokenTarg, OID_NTLMSSP
+ is the only one we support that sends this stuff */
+ DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n",
+ request.negTokenTarg.supportedMech));
+ x_fprintf(x_stdout, "BH Got a negTokenTarg for "
+ "something non-NTLMSSP\n");
+ return;
+ }
+
+ if (request.negTokenTarg.responseToken.data == NULL) {
+ DEBUG(1, ("Got a negTokenTarg without a responseToken!\n"));
+ x_fprintf(x_stdout, "BH Got a negTokenTarg without a "
+ "responseToken!\n");
+ return;
+ }
+
+ status = ntlmssp_update(ntlmssp_state,
+ request.negTokenTarg.responseToken,
+ &response.negTokenTarg.responseToken);
+
+ response.type = SPNEGO_NEG_TOKEN_TARG;
+ response.negTokenTarg.supportedMech = SMB_STRDUP(OID_NTLMSSP);
+ response.negTokenTarg.mechListMIC = data_blob_null;
+
+ if (NT_STATUS_IS_OK(status)) {
+ user = SMB_STRDUP(ntlmssp_state->user);
+ domain = SMB_STRDUP(ntlmssp_state->domain);
+ ntlmssp_end(&ntlmssp_state);
+ }
+ }
+
+ free_spnego_data(&request);
+
+ if (NT_STATUS_IS_OK(status)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED;
+ reply_code = "AF";
+ reply_argument = talloc_asprintf(ctx, "%s\\%s", domain, user);
+ } else if (NT_STATUS_EQUAL(status,
+ NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ reply_code = "TT";
+ reply_argument = talloc_strdup(ctx, "*");
+ } else {
+ response.negTokenTarg.negResult = SPNEGO_REJECT;
+ reply_code = "NA";
+ reply_argument = talloc_strdup(ctx, nt_errstr(status));
+ }
+
+ if (!reply_argument) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
+ return;
+ }
+
+ SAFE_FREE(user);
+ SAFE_FREE(domain);
+
+ len = write_spnego_data(&token, &response);
+ free_spnego_data(&response);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ x_fprintf(x_stdout, "BH Could not write SPNEGO data blob\n");
+ return;
+ }
+
+ reply_base64 = base64_encode_data_blob(talloc_tos(), token);
+
+ x_fprintf(x_stdout, "%s %s %s\n",
+ reply_code, reply_base64, reply_argument);
+
+ TALLOC_FREE(reply_base64);
+ data_blob_free(&token);
+
+ return;
+}
+
+static NTLMSSP_STATE *client_ntlmssp_state = NULL;
+
+static bool manage_client_ntlmssp_init(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob_null;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+ const char *my_mechs[] = {OID_NTLMSSP, NULL};
+
+ DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n"));
+
+ if (client_ntlmssp_state != NULL) {
+ DEBUG(1, ("Request for initial SPNEGO request where "
+ "we already have a state\n"));
+ return False;
+ }
+
+ if (!client_ntlmssp_state) {
+ if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) {
+ x_fprintf(x_stdout, "BH %s\n", nt_errstr(status));
+ return False;
+ }
+ }
+
+
+ if (opt_password == NULL) {
+
+ /* Request a password from the calling process. After
+ sending it, the calling process should retry with
+ the negTokenInit. */
+
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_INIT;
+ spnego.negTokenInit.mechTypes = my_mechs;
+ spnego.negTokenInit.reqFlags = 0;
+ spnego.negTokenInit.mechListMIC = null_blob;
+
+ status = ntlmssp_update(client_ntlmssp_state, null_blob,
+ &spnego.negTokenInit.mechToken);
+
+ if ( !(NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) ||
+ NT_STATUS_IS_OK(status)) ) {
+ DEBUG(1, ("Expected OK or MORE_PROCESSING_REQUIRED, got: %s\n",
+ nt_errstr(status)));
+ ntlmssp_end(&client_ntlmssp_state);
+ return False;
+ }
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&spnego.negTokenInit.mechToken);
+
+ to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ TALLOC_FREE(to_server_base64);
+ return True;
+}
+
+static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego)
+{
+ NTSTATUS status;
+ DATA_BLOB null_blob = data_blob_null;
+ DATA_BLOB request;
+ DATA_BLOB to_server;
+ char *to_server_base64;
+
+ DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n"));
+
+ if (client_ntlmssp_state == NULL) {
+ DEBUG(1, ("Got NTLMSSP tArg without a client state\n"));
+ x_fprintf(x_stdout, "BH Got NTLMSSP tArg without a client state\n");
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
+ x_fprintf(x_stdout, "NA\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
+ x_fprintf(x_stdout, "AF\n");
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ status = ntlmssp_update(client_ntlmssp_state,
+ spnego.negTokenTarg.responseToken,
+ &request);
+
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
+ DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_client_update, got: %s\n",
+ nt_errstr(status)));
+ x_fprintf(x_stdout, "BH Expected MORE_PROCESSING_REQUIRED from "
+ "ntlmssp_client_update\n");
+ data_blob_free(&request);
+ ntlmssp_end(&client_ntlmssp_state);
+ return;
+ }
+
+ spnego.type = SPNEGO_NEG_TOKEN_TARG;
+ spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
+ spnego.negTokenTarg.supportedMech = (char *)OID_NTLMSSP;
+ spnego.negTokenTarg.responseToken = request;
+ spnego.negTokenTarg.mechListMIC = null_blob;
+
+ write_spnego_data(&to_server, &spnego);
+ data_blob_free(&request);
+
+ to_server_base64 = base64_encode_data_blob(talloc_tos(), to_server);
+ data_blob_free(&to_server);
+ x_fprintf(x_stdout, "KK %s\n", to_server_base64);
+ TALLOC_FREE(to_server_base64);
+ return;
+}
+
+#ifdef HAVE_KRB5
+
+static bool manage_client_krb5_init(SPNEGO_DATA spnego)
+{
+ char *principal;
+ DATA_BLOB tkt, to_server;
+ DATA_BLOB session_key_krb5 = data_blob_null;
+ SPNEGO_DATA reply;
+ char *reply_base64;
+ int retval;
+
+ const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL};
+ ssize_t len;
+
+ if ( (spnego.negTokenInit.mechListMIC.data == NULL) ||
+ (spnego.negTokenInit.mechListMIC.length == 0) ) {
+ DEBUG(1, ("Did not get a principal for krb5\n"));
+ return False;
+ }
+
+ principal = (char *)SMB_MALLOC(
+ spnego.negTokenInit.mechListMIC.length+1);
+
+ if (principal == NULL) {
+ DEBUG(1, ("Could not malloc principal\n"));
+ return False;
+ }
+
+ memcpy(principal, spnego.negTokenInit.mechListMIC.data,
+ spnego.negTokenInit.mechListMIC.length);
+ principal[spnego.negTokenInit.mechListMIC.length] = '\0';
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
+
+ if (retval) {
+ char *user = NULL;
+
+ /* Let's try to first get the TGT, for that we need a
+ password. */
+
+ if (opt_password == NULL) {
+ DEBUG(10, ("Requesting password\n"));
+ x_fprintf(x_stdout, "PW\n");
+ return True;
+ }
+
+ user = talloc_asprintf(talloc_tos(), "%s@%s", opt_username, opt_domain);
+ if (!user) {
+ return false;
+ }
+
+ if ((retval = kerberos_kinit_password(user, opt_password, 0, NULL))) {
+ DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval)));
+ return False;
+ }
+
+ retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5, 0, NULL, NULL);
+
+ if (retval) {
+ DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval)));
+ return False;
+ }
+ }
+
+ data_blob_free(&session_key_krb5);
+
+ ZERO_STRUCT(reply);
+
+ reply.type = SPNEGO_NEG_TOKEN_INIT;
+ reply.negTokenInit.mechTypes = my_mechs;
+ reply.negTokenInit.reqFlags = 0;
+ reply.negTokenInit.mechToken = tkt;
+ reply.negTokenInit.mechListMIC = data_blob_null;
+
+ len = write_spnego_data(&to_server, &reply);
+ data_blob_free(&tkt);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not write SPNEGO data blob\n"));
+ return False;
+ }
+
+ reply_base64 = base64_encode_data_blob(talloc_tos(), to_server);
+ x_fprintf(x_stdout, "KK %s *\n", reply_base64);
+
+ TALLOC_FREE(reply_base64);
+ data_blob_free(&to_server);
+ DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n"));
+ return True;
+}
+
+static void manage_client_krb5_targ(SPNEGO_DATA spnego)
+{
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_INCOMPLETE:
+ DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n"));
+ x_fprintf(x_stdout, "BH Got a Kerberos negTokenTarg with "
+ "ACCEPT_INCOMPLETE\n");
+ break;
+ case SPNEGO_ACCEPT_COMPLETED:
+ DEBUG(10, ("Accept completed\n"));
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ DEBUG(10, ("Rejected\n"));
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got an invalid negTokenTarg\n"));
+ x_fprintf(x_stdout, "AF\n");
+ }
+}
+
+#endif
+
+static void manage_gss_spnego_client_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ DATA_BLOB request;
+ SPNEGO_DATA spnego;
+ ssize_t len;
+
+ if (!opt_username || !*opt_username) {
+ x_fprintf(x_stderr, "username must be specified!\n\n");
+ exit(1);
+ }
+
+ if (strlen(buf) <= 3) {
+ DEBUG(1, ("SPNEGO query [%s] too short\n", buf));
+ x_fprintf(x_stdout, "BH SPNEGO query too short\n");
+ return;
+ }
+
+ request = base64_decode_data_blob(buf+3);
+
+ if (strncmp(buf, "PW ", 3) == 0) {
+
+ /* We asked for a password and obviously got it :-) */
+
+ opt_password = SMB_STRNDUP((const char *)request.data, request.length);
+
+ if (opt_password == NULL) {
+ DEBUG(1, ("Out of memory\n"));
+ x_fprintf(x_stdout, "BH Out of memory\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ x_fprintf(x_stdout, "OK\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ if ( (strncmp(buf, "TT ", 3) != 0) &&
+ (strncmp(buf, "AF ", 3) != 0) &&
+ (strncmp(buf, "NA ", 3) != 0) ) {
+ DEBUG(1, ("SPNEGO request [%s] invalid\n", buf));
+ x_fprintf(x_stdout, "BH SPNEGO request invalid\n");
+ data_blob_free(&request);
+ return;
+ }
+
+ /* So we got a server challenge to generate a SPNEGO
+ client-to-server request... */
+
+ len = read_spnego_data(request, &spnego);
+ data_blob_free(&request);
+
+ if (len == -1) {
+ DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf));
+ x_fprintf(x_stdout, "BH Could not read SPNEGO data\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_INIT) {
+
+ /* The server offers a list of mechanisms */
+
+ const char **mechType = (const char **)spnego.negTokenInit.mechTypes;
+
+ while (*mechType != NULL) {
+
+#ifdef HAVE_KRB5
+ if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) ||
+ (strcmp(*mechType, OID_KERBEROS5) == 0) ) {
+ if (manage_client_krb5_init(spnego))
+ goto out;
+ }
+#endif
+
+ if (strcmp(*mechType, OID_NTLMSSP) == 0) {
+ if (manage_client_ntlmssp_init(spnego))
+ goto out;
+ }
+
+ mechType++;
+ }
+
+ DEBUG(1, ("Server offered no compatible mechanism\n"));
+ x_fprintf(x_stdout, "BH Server offered no compatible mechanism\n");
+ return;
+ }
+
+ if (spnego.type == SPNEGO_NEG_TOKEN_TARG) {
+
+ if (spnego.negTokenTarg.supportedMech == NULL) {
+ /* On accept/reject Windows does not send the
+ mechanism anymore. Handle that here and
+ shut down the mechanisms. */
+
+ switch (spnego.negTokenTarg.negResult) {
+ case SPNEGO_ACCEPT_COMPLETED:
+ x_fprintf(x_stdout, "AF\n");
+ break;
+ case SPNEGO_REJECT:
+ x_fprintf(x_stdout, "NA\n");
+ break;
+ default:
+ DEBUG(1, ("Got a negTokenTarg with no mech and an "
+ "unknown negResult: %d\n",
+ spnego.negTokenTarg.negResult));
+ x_fprintf(x_stdout, "BH Got a negTokenTarg with"
+ " no mech and an unknown "
+ "negResult\n");
+ }
+
+ ntlmssp_end(&client_ntlmssp_state);
+ goto out;
+ }
+
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_NTLMSSP) == 0) {
+ manage_client_ntlmssp_targ(spnego);
+ goto out;
+ }
+
+#if HAVE_KRB5
+ if (strcmp(spnego.negTokenTarg.supportedMech,
+ OID_KERBEROS5_OLD) == 0) {
+ manage_client_krb5_targ(spnego);
+ goto out;
+ }
+#endif
+
+ }
+
+ DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf));
+ x_fprintf(x_stdout, "BH Got an SPNEGO token I could not handle\n");
+ return;
+
+ out:
+ free_spnego_data(&spnego);
+ return;
+}
+
+static void manage_ntlm_server_1_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ 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) {
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "Error: Out of memory in asprintf!\n.\n");
+ return;
+ }
+ }
+ if (check_plaintext_auth(full_username, plaintext_password, False)) {
+ x_fprintf(x_stdout, "Authenticated: Yes\n");
+ } else {
+ x_fprintf(x_stdout, "Authenticated: No\n");
+ }
+ } else if (!lm_response.data && !nt_response.data) {
+ x_fprintf(x_stdout, "Error: No password supplied!\n");
+ } else if (!challenge.data) {
+ x_fprintf(x_stdout, "Error: No lanman-challenge supplied!\n");
+ } else {
+ char *error_string = NULL;
+ uchar lm_key[8];
+ uchar user_session_key[16];
+ uint32 flags = 0;
+
+ 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 */
+ x_fprintf(x_stdout, "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 (!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;
+
+ if (!NT_STATUS_IS_OK(
+ contact_winbind_auth_crap(username,
+ domain,
+ global_myname(),
+ &challenge,
+ &lm_response,
+ &nt_response,
+ flags,
+ lm_key,
+ user_session_key,
+ &error_string,
+ NULL))) {
+
+ x_fprintf(x_stdout, "Authenticated: No\n");
+ x_fprintf(x_stdout, "Authentication-Error: %s\n.\n", error_string);
+ SAFE_FREE(error_string);
+ } else {
+ static char zeros[16];
+ char *hex_lm_key;
+ char *hex_user_session_key;
+
+ x_fprintf(x_stdout, "Authenticated: Yes\n");
+
+ if (ntlm_server_1_lm_session_key
+ && (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0)) {
+ hex_lm_key = hex_encode(NULL,
+ (const unsigned char *)lm_key,
+ sizeof(lm_key));
+ x_fprintf(x_stdout, "LANMAN-Session-Key: %s\n", hex_lm_key);
+ TALLOC_FREE(hex_lm_key);
+ }
+
+ if (ntlm_server_1_user_session_key
+ && (memcmp(zeros, user_session_key,
+ sizeof(user_session_key)) != 0)) {
+ hex_user_session_key = hex_encode(NULL,
+ (const unsigned char *)user_session_key,
+ sizeof(user_session_key));
+ x_fprintf(x_stdout, "User-Session-Key: %s\n", hex_user_session_key);
+ TALLOC_FREE(hex_user_session_key);
+ }
+ }
+ }
+ /* 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;
+ x_fprintf(x_stdout, ".\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"));
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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 {
+ x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_ntlm_change_password_1_request(struct ntlm_auth_state *state,
+ char *buf, int length)
+{
+ 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];
+
+ 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
+ */
+
+ 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);
+
+ SamOEMhash(new_lm_pswd.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_lm_hash,
+ old_lm_hash_enc.data);
+ } 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);
+
+ SamOEMhash(new_nt_pswd.data, old_nt_hash, 516);
+ E_old_pw_hash(new_nt_hash, old_nt_hash,
+ old_nt_hash_enc.data);
+ }
+
+ if (!full_username && !username) {
+ x_fprintf(x_stdout, "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) ) {
+ x_fprintf(x_stdout, "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 */
+ x_fprintf(x_stdout, "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))) {
+ x_fprintf(x_stdout, "Password-Change: No\n");
+ x_fprintf(x_stdout, "Password-Change-Error: "
+ "%s\n.\n", error_string);
+ } else {
+ x_fprintf(x_stdout, "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);
+ x_fprintf(x_stdout, ".\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"));
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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) {
+ x_fprintf(x_stdout, "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)
+ {
+ x_fprintf(x_stdout, "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 {
+ x_fprintf(x_stdout, "Error: Unknown request %s\n.\n", request);
+ }
+}
+
+static void manage_squid_request(struct ntlm_auth_state *state,
+ stdio_helper_function fn)
+{
+ 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"));
+ x_fprintf(x_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"));
+ x_fprintf(x_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"));
+ x_fprintf(x_stderr, "ERR\n");
+ talloc_free(buf);
+ return;
+ }
+
+ fn(state, buf, length);
+ talloc_free(buf);
+}
+
+
+static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) {
+ TALLOC_CTX *mem_ctx;
+ struct ntlm_auth_state *state;
+
+ /* initialize FDescs */
+ x_setbuf(x_stdout, NULL);
+ x_setbuf(x_stderr, NULL);
+
+ mem_ctx = talloc_init("ntlm_auth");
+ if (!mem_ctx) {
+ DEBUG(0, ("squid_stream: Failed to create talloc context\n"));
+ x_fprintf(x_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"));
+ x_fprintf(x_stderr, "ERR\n");
+ exit(1);
+ }
+
+ state->mem_ctx = mem_ctx;
+ state->helper_mode = stdio_mode;
+
+ while(1) {
+ manage_squid_request(state, fn);
+ }
+}
+
+
+/* Authenticate a user with a challenge/response */
+
+static bool check_auth_crap(void)
+{
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ char lm_key[8];
+ char user_session_key[16];
+ char *hex_lm_key;
+ char *hex_user_session_key;
+ char *error_string;
+ static uint8 zeros[16];
+
+ x_setbuf(x_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,
+ (unsigned char *)lm_key,
+ (unsigned char *)user_session_key,
+ &error_string, NULL);
+
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ x_fprintf(x_stdout, "%s (0x%x)\n",
+ error_string,
+ NT_STATUS_V(nt_status));
+ SAFE_FREE(error_string);
+ return False;
+ }
+
+ if (request_lm_key
+ && (memcmp(zeros, lm_key,
+ sizeof(lm_key)) != 0)) {
+ hex_lm_key = hex_encode(NULL, (const unsigned char *)lm_key,
+ sizeof(lm_key));
+ x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key);
+ TALLOC_FREE(hex_lm_key);
+ }
+ if (request_user_session_key
+ && (memcmp(zeros, user_session_key,
+ sizeof(user_session_key)) != 0)) {
+ hex_user_session_key = hex_encode(NULL, (const unsigned char *)user_session_key,
+ sizeof(user_session_key));
+ x_fprintf(x_stdout, "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
+};
+
+ int main(int argc, const char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ int opt;
+ static const char *helper_protocol;
+ static int diagnostics;
+
+ static const char *hex_challenge;
+ static const char *hex_lm_response;
+ static const char *hex_nt_response;
+
+ poptContext pc;
+
+ /* 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
+ { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"},
+ { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"},
+ { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"},
+ { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"},
+ { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"},
+ { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"},
+ { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"},
+ { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"},
+ { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retrieve LM session key"},
+ { "request-nt-key", 0, POPT_ARG_NONE, &request_user_session_key, OPT_USER_SESSION_KEY, "Retrieve User (NT) session key"},
+ { "use-cached-creds", 0, POPT_ARG_NONE, &use_cached_creds, OPT_USE_CACHED_CREDS, "Use cached credentials if no password is given"},
+ { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"},
+ { "require-membership-of", 0, POPT_ARG_STRING, &require_membership_of, OPT_REQUIRE_MEMBERSHIP, "Require that a user be a member of this group (either name or SID) for authentication to succeed" },
+ POPT_COMMON_CONFIGFILE
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+
+ /* Samba client initialisation */
+ load_case_tables();
+
+ dbf = x_stderr;
+
+ /* Parse options */
+
+ pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0);
+
+ /* Parse command line options */
+
+ if (argc == 1) {
+ poptPrintHelp(pc, stderr, 0);
+ return 1;
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ /* Get generic config options like --configfile */
+ }
+
+ poptFreeContext(pc);
+
+ if (!lp_load(get_dyn_CONFIGFILE(), True, False, False, True)) {
+ d_fprintf(stderr, "ntlm_auth: error opening config file %s. Error was %s\n",
+ get_dyn_CONFIGFILE(), strerror(errno));
+ exit(1);
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_CHALLENGE:
+ opt_challenge = strhex_to_data_blob(NULL, hex_challenge);
+ if (opt_challenge.length != 8) {
+ x_fprintf(x_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) {
+ x_fprintf(x_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) {
+ x_fprintf(x_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("S-", require_membership_of, 2) == 0) {
+ require_membership_of_sid = require_membership_of;
+ }
+ break;
+ }
+ }
+
+ 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)) {
+ x_fprintf(x_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 = "";
+ }
+
+ 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, stdio_helper_protocols[i].fn);
+ exit(0);
+ }
+ }
+ x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol);
+
+ for (i=0; i<NUM_HELPER_MODES; i++) {
+ x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name);
+ }
+
+ exit(1);
+ }
+
+ if (!opt_username || !*opt_username) {
+ x_fprintf(x_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) {
+ opt_password = getpass("password: ");
+ }
+
+ if (diagnostics) {
+ if (!diagnose_ntlm_auth()) {
+ 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)) {
+ return 1;
+ }
+ }
+
+ /* Exit code */
+
+ 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 0000000000..fb1dd62a19
--- /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 0000000000..dcdc8e9a40
--- /dev/null
+++ b/source3/utils/ntlm_auth_diagnostics.c
@@ -0,0 +1,607 @@
+/*
+ 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"
+
+#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 pass = True;
+ NTSTATUS nt_status;
+ uint32 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);
+
+ 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, NULL, 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,
+ lm_key,
+ user_session_key,
+ &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 (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(void)
+{
+
+ return test_lm_ntlm_broken(NO_NT);
+}
+
+/*
+ * Test the NTLM response only, no LM.
+ */
+
+static bool test_ntlm(void)
+{
+ return test_lm_ntlm_broken(NO_LM);
+}
+
+/*
+ * Test the NTLM response only, but in the LM field.
+ */
+
+static bool test_ntlm_in_lm(void)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+
+ 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,
+ lm_key,
+ user_session_key,
+ &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 (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(void)
+{
+ bool pass = True;
+ NTSTATUS nt_status;
+ uint32 flags = 0;
+ DATA_BLOB nt_response = data_blob(NULL, 24);
+ DATA_BLOB session_key = data_blob(NULL, 16);
+
+ uint8 lm_key[8];
+ uint8 lm_hash[16];
+ uint8 user_session_key[16];
+ uint8 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, NULL, 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,
+ lm_key,
+ user_session_key,
+ &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 (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 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(get_winbind_netbios_name(), get_winbind_domain());
+
+ 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(opt_username, opt_domain, opt_password, &chall,
+ &names_blob,
+ &lmv2_response, &ntlmv2_response,
+ &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,
+ NULL,
+ user_session_key,
+ &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(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NONE);
+}
+
+/*
+ * Test the LMv2 response only
+ */
+
+static bool test_lmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_NT);
+}
+
+/*
+ * Test the NTLMv2 response only
+ */
+
+static bool test_ntlmv2(void)
+{
+ return test_lmv2_ntlmv2_broken(NO_LM);
+}
+
+static bool test_lm_ntlm(void)
+{
+ return test_lm_ntlm_broken(BREAK_NONE);
+}
+
+static bool test_ntlm_lm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_LM);
+}
+
+static bool test_ntlm_ntlm_broken(void)
+{
+ return test_lm_ntlm_broken(BREAK_NT);
+}
+
+static bool test_ntlmv2_lmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_LM);
+}
+
+static bool test_ntlmv2_ntlmv2_broken(void)
+{
+ return test_lmv2_ntlmv2_broken(BREAK_NT);
+}
+
+static bool test_plaintext(enum ntlm_break break_which)
+{
+ NTSTATUS nt_status;
+ uint32 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;
+
+ 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_allocate(&nt_response_ucs2, opt_password,
+ &converted_size))
+ {
+ DEBUG(0, ("push_ucs2_allocate 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 = strdup_upper(opt_password)) == NULL) {
+ DEBUG(0, ("strdup_upper failed!\n"));
+ exit(1);
+ }
+
+ if (!convert_string_allocate(NULL, CH_UNIX,
+ CH_DOS, password,
+ strlen(password)+1,
+ &lm_response.data,
+ &lm_response.length, True)) {
+ DEBUG(0, ("convert_string_allocate failed!\n"));
+ exit(1);
+ }
+
+ SAFE_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:
+ SAFE_FREE(lm_response.data);
+ lm_response.length = 0;
+ break;
+ case NO_NT:
+ SAFE_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,
+ lm_key,
+ user_session_key,
+ &error_string, NULL);
+
+ SAFE_FREE(nt_response.data);
+ SAFE_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(void) {
+ return test_plaintext(BREAK_NONE);
+}
+
+static bool test_plaintext_lm_broken(void) {
+ return test_plaintext(BREAK_LM);
+}
+
+static bool test_plaintext_nt_broken(void) {
+ return test_plaintext(BREAK_NT);
+}
+
+static bool test_plaintext_nt_only(void) {
+ return test_plaintext(NO_LM);
+}
+
+static bool test_plaintext_lm_only(void) {
+ 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 feilds)
+
+ 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)(void);
+ const char *name;
+} test_table[] = {
+ {test_lm, "LM"},
+ {test_lm_ntlm, "LM and NTLM"},
+ {test_ntlm, "NTLM"},
+ {test_ntlm_in_lm, "NTLM in LM"},
+ {test_ntlm_in_both, "NTLM in both"},
+ {test_ntlmv2, "NTLMv2"},
+ {test_lmv2_ntlmv2, "NTLMv2 and LMv2"},
+ {test_lmv2, "LMv2"},
+ {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"},
+ {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"},
+ {test_ntlm_lm_broken, "NTLM and LM, LM broken"},
+ {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"},
+ {test_plaintext_none_broken, "Plaintext"},
+ {test_plaintext_lm_broken, "Plaintext LM broken"},
+ {test_plaintext_nt_broken, "Plaintext NT broken"},
+ {test_plaintext_nt_only, "Plaintext NT only"},
+ {test_plaintext_lm_only, "Plaintext LM only"},
+ {NULL, NULL}
+};
+
+bool diagnose_ntlm_auth(void)
+{
+ unsigned int i;
+ bool pass = True;
+
+ for (i=0; test_table[i].fn; i++) {
+ if (!test_table[i].fn()) {
+ DEBUG(1, ("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 0000000000..e48a190ed6
--- /dev/null
+++ b/source3/utils/ntlm_auth_proto.h
@@ -0,0 +1,48 @@
+/*
+ * 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 flags,
+ uint8 lm_key[8],
+ uint8 user_session_key[16],
+ char **error_string,
+ char **unix_name);
+
+/* The following definitions come from utils/ntlm_auth_diagnostics.c */
+
+bool diagnose_ntlm_auth(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 0000000000..104e00a65c
--- /dev/null
+++ b/source3/utils/passwd_proto.h
@@ -0,0 +1,32 @@
+/*
+ * 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 *stdin_new_passwd( void);
+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 0000000000..293f16364e
--- /dev/null
+++ b/source3/utils/passwd_util.c
@@ -0,0 +1,68 @@
+/*
+ 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"
+
+/*************************************************************
+ Utility function to prompt for passwords from stdin. Each
+ password entered must end with a newline.
+*************************************************************/
+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) {
+ 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 *p;
+ if (stdin_get) {
+ p = stdin_new_passwd();
+ } else {
+ p = getpass( prompt);
+ }
+ return smb_xstrdup( p);
+}
diff --git a/source3/utils/pdbedit.c b/source3/utils/pdbedit.c
new file mode 100644
index 0000000000..e1d6709073
--- /dev/null
+++ b/source3/utils/pdbedit.c
@@ -0,0 +1,1101 @@
+/*
+ Unix SMB/CIFS implementation.
+ passdb editing frontend
+
+ Copyright (C) Simo Sorce 2000
+ 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"
+
+#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 MASK_ALWAYS_GOOD 0x0000001F
+#define MASK_USER_GOOD 0x00405FE0
+
+/*********************************************************
+ 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(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"));
+ pdb_search_destroy(u_search);
+ return 1;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+ struct samu *user;
+ struct samu *account;
+ 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 );
+ pdb_search_destroy(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 );
+ }
+
+ pdb_search_destroy(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]));
+ }
+
+ SAFE_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 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 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: chaeck if entry is a user or a workstation */
+ if (!sam_pwent) return -1;
+
+ if (verbosity) {
+ char temp[44];
+ const uint8 *hours;
+
+ 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",
+ sid_string_tos(pdb_get_user_sid(sam_pwent)));
+ printf ("Primary Group SID: %s\n",
+ sid_string_tos(pdb_get_group_sid(sam_pwent)));
+ 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(tmp) : "0");
+
+ tmp = pdb_get_logoff_time(sam_pwent);
+ printf ("Logoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_kickoff_time(sam_pwent);
+ printf ("Kickoff time: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_last_set_time(sam_pwent);
+ printf ("Password last set: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_can_change_time(sam_pwent);
+ printf ("Password can change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_pass_must_change_time(sam_pwent);
+ printf ("Password must change: %s\n", tmp ? http_timestring(tmp) : "0");
+
+ tmp = pdb_get_bad_password_time(sam_pwent);
+ printf ("Last bad password : %s\n", tmp ? http_timestring(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);
+
+ } 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)convert_time_t_to_uint32(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 (struct pdb_methods *in, const char *username, bool verbosity, bool smbpwdstyle)
+{
+ struct samu *sam_pwent=NULL;
+ bool ret;
+
+ if ( (sam_pwent = samu_new( NULL )) == NULL ) {
+ return -1;
+ }
+
+ ret = NT_STATUS_IS_OK(in->getsampwnam (in, sam_pwent, username));
+
+ if (ret==False) {
+ 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 (struct pdb_methods *in, bool verbosity, bool smbpwdstyle)
+{
+ struct pdb_search *u_search;
+ struct samr_displayentry userentry;
+
+ u_search = pdb_search_init(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"));
+ pdb_search_destroy(u_search);
+ return 1;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+ struct samu *sam_pwent;
+ DOM_SID user_sid;
+ NTSTATUS status;
+
+ sam_pwent = samu_new(talloc_tos());
+ if (sam_pwent == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ break;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), userentry.rid);
+
+ status = in->getsampwsid(in, sam_pwent, &user_sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("getsampwsid failed: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(sam_pwent);
+ continue;
+ }
+
+ if (verbosity)
+ printf ("---------------\n");
+ print_sam_info (sam_pwent, verbosity, smbpwdstyle);
+ TALLOC_FREE(sam_pwent);
+ }
+ pdb_search_destroy(u_search);
+
+ return 0;
+}
+
+/*********************************************************
+ Fix a list of Users for uninitialised passwords
+**********************************************************/
+static int fix_users_list (struct pdb_methods *in)
+{
+ struct pdb_search *u_search;
+ struct samr_displayentry userentry;
+
+ u_search = pdb_search_init(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"));
+ pdb_search_destroy(u_search);
+ return 1;
+ }
+
+ while (u_search->next_entry(u_search, &userentry)) {
+ struct samu *sam_pwent;
+ DOM_SID user_sid;
+ NTSTATUS status;
+
+ sam_pwent = samu_new(talloc_tos());
+ if (sam_pwent == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ break;
+ }
+
+ sid_compose(&user_sid, get_global_sam_sid(), userentry.rid);
+
+ status = in->getsampwsid(in, sam_pwent, &user_sid);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(2, ("getsampwsid failed: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(sam_pwent);
+ continue;
+ }
+
+ if (!NT_STATUS_IS_OK(pdb_update_sam_account(sam_pwent))) {
+ printf("Update of user %s failed!\n",
+ pdb_get_username(sam_pwent));
+ }
+ TALLOC_FREE(sam_pwent);
+ }
+ pdb_search_destroy(u_search);
+ return 0;
+}
+
+/*********************************************************
+ Set User Info
+**********************************************************/
+
+static int set_user_info (struct pdb_methods *in, 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)
+{
+ bool updated_autolock = False, updated_badpw = False;
+ struct samu *sam_pwent=NULL;
+ bool ret;
+
+ if ( (sam_pwent = samu_new( NULL )) == NULL ) {
+ return 1;
+ }
+
+ ret = NT_STATUS_IS_OK(in->getsampwnam (in, sam_pwent, username));
+ if (ret==False) {
+ fprintf (stderr, "Username not found!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ if (hours) {
+ uint8 hours_array[MAX_HOURS_LEN];
+ uint32 hours_len;
+
+ hours_len = pdb_get_hours_len(sam_pwent);
+ memset(hours_array, 0xff, hours_len);
+
+ pdb_set_hours(sam_pwent, hours_array, 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) {
+ uint32 not_settable = ~(ACB_DISABLED|ACB_HOMDIRREQ|ACB_PWNOTREQ|
+ ACB_PWNOEXP|ACB_AUTOLOCK);
+
+ uint32 newflag = pdb_decode_acct_ctrl(account_control);
+
+ if (newflag & not_settable) {
+ fprintf(stderr, "Can only set [NDHLX] flags\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+
+ pdb_set_acct_ctrl(sam_pwent,
+ (pdb_get_acct_ctrl(sam_pwent) & not_settable) | newflag,
+ PDB_CHANGED);
+ }
+ if (user_sid) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ 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 (NT_STATUS_IS_OK(in->update_sam_account (in, sam_pwent)))
+ print_user_info (in, username, 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 (struct pdb_methods *in, const char *username,
+ const char *fullname, const char *homedir,
+ const char *drive, const char *script,
+ const char *profile, char *user_sid, bool stdin_get)
+{
+ struct samu *sam_pwent;
+ char *password1, *password2;
+ int rc_pwd_cmp;
+ struct passwd *pwd;
+
+ get_global_sam_sid();
+
+ if ( !(pwd = getpwnam_alloc( NULL, username )) ) {
+ DEBUG(0,("Cannot locate Unix account for %s\n", username));
+ return -1;
+ }
+
+ if ( (sam_pwent = samu_new( NULL )) == NULL ) {
+ DEBUG(0, ("Memory allocation failure!\n"));
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(samu_alloc_rid_unix(sam_pwent, pwd ))) {
+ TALLOC_FREE( sam_pwent );
+ TALLOC_FREE( pwd );
+ DEBUG(0, ("could not create account to add new user %s\n", username));
+ return -1;
+ }
+
+ password1 = get_pass( "new password:", stdin_get);
+ password2 = get_pass( "retype new password:", stdin_get);
+ if ((rc_pwd_cmp = strcmp (password1, password2))) {
+ fprintf (stderr, "Passwords do not match!\n");
+ TALLOC_FREE(sam_pwent);
+ } else {
+ pdb_set_plaintext_passwd(sam_pwent, password1);
+ }
+
+ memset(password1, 0, strlen(password1));
+ SAFE_FREE(password1);
+ memset(password2, 0, strlen(password2));
+ SAFE_FREE(password2);
+
+ /* pwds do _not_ match? */
+ if (rc_pwd_cmp)
+ return -1;
+
+ 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) {
+ DOM_SID u_sid;
+ if (!string_to_sid(&u_sid, user_sid)) {
+ /* not a complete sid, may be a RID, try building a SID */
+ int u_rid;
+
+ if (sscanf(user_sid, "%d", &u_rid) != 1) {
+ fprintf(stderr, "Error passed string is not a complete user SID or RID!\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+ sid_copy(&u_sid, get_global_sam_sid());
+ sid_append_rid(&u_sid, u_rid);
+ }
+ pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED);
+ }
+
+ pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->add_sam_account (in, sam_pwent))) {
+ print_user_info (in, username, True, False);
+ } else {
+ fprintf (stderr, "Unable to add user! (does it already exist?)\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+ TALLOC_FREE(sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Add New Machine
+**********************************************************/
+
+static int new_machine (struct pdb_methods *in, const char *machine_in)
+{
+ struct samu *sam_pwent=NULL;
+ fstring machinename;
+ fstring machineaccount;
+ struct passwd *pwd = NULL;
+
+ get_global_sam_sid();
+
+ if (strlen(machine_in) == 0) {
+ fprintf(stderr, "No machine name given\n");
+ return -1;
+ }
+
+ fstrcpy(machinename, machine_in);
+ machinename[15]= '\0';
+
+ if (machinename[strlen (machinename) -1] == '$')
+ machinename[strlen (machinename) -1] = '\0';
+
+ strlower_m(machinename);
+
+ fstrcpy(machineaccount, machinename);
+ fstrcat(machineaccount, "$");
+
+ if ( !(pwd = getpwnam_alloc( NULL, machineaccount )) ) {
+ DEBUG(0,("Cannot locate Unix account for %s\n", machineaccount));
+ return -1;
+ }
+
+ if ( (sam_pwent = samu_new( NULL )) == NULL ) {
+ fprintf(stderr, "Memory allocation error!\n");
+ TALLOC_FREE(pwd);
+ return -1;
+ }
+
+ if ( !NT_STATUS_IS_OK(samu_alloc_rid_unix(sam_pwent, pwd )) ) {
+ fprintf(stderr, "Could not init sam from pw\n");
+ TALLOC_FREE(pwd);
+ return -1;
+ }
+
+ TALLOC_FREE(pwd);
+
+ pdb_set_plaintext_passwd (sam_pwent, machinename);
+ pdb_set_username (sam_pwent, machineaccount, PDB_CHANGED);
+ pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST, PDB_CHANGED);
+
+ if (NT_STATUS_IS_OK(in->add_sam_account (in, sam_pwent))) {
+ print_user_info (in, machineaccount, True, False);
+ } else {
+ fprintf (stderr, "Unable to add machine! (does it already exist?)\n");
+ TALLOC_FREE(sam_pwent);
+ return -1;
+ }
+ TALLOC_FREE(sam_pwent);
+ return 0;
+}
+
+/*********************************************************
+ Delete user entry
+**********************************************************/
+
+static int delete_user_entry (struct pdb_methods *in, const char *username)
+{
+ struct samu *samaccount = NULL;
+
+ if ( (samaccount = samu_new( NULL )) == NULL ) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->getsampwnam(in, samaccount, username))) {
+ fprintf (stderr, "user %s does not exist in the passdb\n", username);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete user %s\n", username);
+ return -1;
+ }
+ return 0;
+}
+
+/*********************************************************
+ Delete machine entry
+**********************************************************/
+
+static int delete_machine_entry (struct pdb_methods *in, const char *machinename)
+{
+ fstring name;
+ struct samu *samaccount = NULL;
+
+ if (strlen(machinename) == 0) {
+ fprintf(stderr, "No machine name given\n");
+ return -1;
+ }
+
+ fstrcpy(name, machinename);
+ name[15] = '\0';
+ if (name[strlen(name)-1] != '$')
+ fstrcat (name, "$");
+
+ if ( (samaccount = samu_new( NULL )) == NULL ) {
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->getsampwnam(in, samaccount, name))) {
+ fprintf (stderr, "machine %s does not exist in the passdb\n", name);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(in->delete_sam_account (in, samaccount))) {
+ fprintf (stderr, "Unable to delete machine %s\n", name);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*********************************************************
+ Start here.
+**********************************************************/
+
+int main (int argc, 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 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 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 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, *bdef;
+ char *configfile = NULL;
+ TALLOC_CTX *frame = talloc_stackframe();
+ poptContext pc;
+ 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},
+ {"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},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+
+ /* we shouldn't have silly checks like this */
+ if (getuid() != 0) {
+ d_fprintf(stderr, "You must be root to use pdbedit\n");
+ TALLOC_FREE(frame);
+ return -1;
+ }
+
+ bin = bout = bdef = NULL;
+
+ load_case_tables();
+
+ setup_logging("pdbedit", True);
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case 'C':
+ account_policy_value_set = True;
+ break;
+ case 's':
+ configfile = optarg;
+ break;
+ }
+ }
+
+ poptGetArg(pc); /* Drop argv[0], the program name */
+
+ if (user_name == NULL)
+ user_name = poptGetArg(pc);
+
+ if (!lp_load(get_dyn_CONFIGFILE(),True,False,False,True)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n", get_dyn_CONFIGFILE());
+ exit(1);
+ }
+
+ if(!initialize_password_db(False, NULL))
+ exit(1);
+
+ if (!init_names())
+ 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) +
+ (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);
+
+ if (setparms & BIT_BACKEND) {
+ if (!NT_STATUS_IS_OK(make_pdb_method_name( &bdef, backend ))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ } else {
+ if (!NT_STATUS_IS_OK(make_pdb_method_name(&bdef, lp_passdb_backend()))) {
+ fprintf(stderr, "Can't initialize passdb backend.\n");
+ return 1;
+ }
+ }
+
+ /* the lowest bit options are always accepted */
+ checkparms = setparms & ~MASK_ALWAYS_GOOD;
+
+ if (checkparms & BIT_FIX_INIT) {
+ return fix_users_list(bdef);
+ }
+
+ /* account policy operations */
+ if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) {
+ uint32 value;
+ int field = account_policy_name_to_fieldnum(account_policy);
+ if (field == 0) {
+ const char **names;
+ int count;
+ int i;
+ account_policy_names_list(&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]);
+ }
+ }
+ SAFE_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)) )
+ {
+ NTSTATUS status;
+
+ bin = bout = bdef;
+
+ if (backend_in) {
+ status = make_pdb_method_name(&bin, backend_in);
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ fprintf(stderr, "Unable to initialize %s.\n", backend_in);
+ return 1;
+ }
+ }
+
+ if (backend_out) {
+ status = make_pdb_method_name(&bout, backend_out);
+
+ if ( !NT_STATUS_IS_OK(status) ) {
+ fprintf(stderr, "Unable to initialize %s.\n", backend_out);
+ 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)) {
+ return print_users_list (bdef, verbose, spstyle);
+ }
+ if (!(checkparms & ~(BIT_USER + BIT_LIST))) {
+ return print_user_info (bdef, 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");
+ return -1;
+ }
+
+ /* account creation operations */
+ if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE))) {
+ if (checkparms & BIT_MACHINE) {
+ return new_machine (bdef, user_name);
+ } else {
+ return new_user (bdef, 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))) {
+ if (checkparms & BIT_MACHINE) {
+ return delete_machine_entry (bdef, user_name);
+ } else {
+ return delete_user_entry (bdef, user_name);
+ }
+ }
+
+ /* account modification operations */
+ if (!(checkparms & ~(BIT_MODIFY + BIT_USER))) {
+ return set_user_info (bdef, user_name, full_name, home_dir,
+ acct_desc, home_drive, logon_script, profile_path, account_control,
+ user_sid, user_domain, badpw_reset, hours_reset);
+ }
+ }
+
+ if (setparms >= 0x20) {
+ fprintf (stderr, "Incompatible or insufficient options on command line!\n");
+ }
+ poptPrintHelp(pc, stderr, 0);
+
+ TALLOC_FREE(frame);
+ return 1;
+}
diff --git a/source3/utils/profiles.c b/source3/utils/profiles.c
new file mode 100644
index 0000000000..0b8a0d4278
--- /dev/null
+++ b/source3/utils/profiles.c
@@ -0,0 +1,303 @@
+/*
+ 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 "regfio.h"
+
+/* GLOBAL VARIABLES */
+
+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, var);
+ va_end(args);
+ SAFE_FREE(var);
+}
+
+/********************************************************************
+********************************************************************/
+
+static bool swap_sid_in_acl( SEC_DESC *sd, DOM_SID *s1, DOM_SID *s2 )
+{
+ SEC_ACL *acl;
+ int i;
+ bool update = False;
+
+ verbose_output(" Owner SID: %s\n", sid_string_tos(sd->owner_sid));
+ if ( sid_equal( sd->owner_sid, s1 ) ) {
+ sid_copy( sd->owner_sid, s2 );
+ update = True;
+ verbose_output(" New Owner SID: %s\n",
+ sid_string_tos(sd->owner_sid));
+
+ }
+
+ verbose_output(" Group SID: %s\n", sid_string_tos(sd->group_sid));
+ if ( sid_equal( sd->group_sid, s1 ) ) {
+ sid_copy( sd->group_sid, s2 );
+ update = True;
+ verbose_output(" New Group SID: %s\n",
+ sid_string_tos(sd->group_sid));
+ }
+
+ acl = sd->dacl;
+ verbose_output(" DACL: %d entries:\n", acl->num_aces);
+ for ( i=0; i<acl->num_aces; i++ ) {
+ verbose_output(" Trustee SID: %s\n",
+ sid_string_tos(&acl->aces[i].trustee));
+ if ( sid_equal( &acl->aces[i].trustee, s1 ) ) {
+ sid_copy( &acl->aces[i].trustee, s2 );
+ update = True;
+ verbose_output(" New Trustee SID: %s\n",
+ sid_string_tos(&acl->aces[i].trustee));
+ }
+ }
+
+#if 0
+ acl = sd->sacl;
+ verbose_output(" SACL: %d entries: \n", acl->num_aces);
+ for ( i=0; i<acl->num_aces; i++ ) {
+ verbose_output(" Trustee SID: %s\n",
+ sid_string_tos(&acl->aces[i].trustee));
+ if ( sid_equal( &acl->aces[i].trustee, s1 ) ) {
+ sid_copy( &acl->aces[i].trustee, s2 );
+ update = True;
+ verbose_output(" New Trustee SID: %s\n",
+ sid_string_tos(&acl->aces[i].trustee));
+ }
+ }
+#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;
+ SEC_DESC *new_sd;
+ REGVAL_CTR *values;
+ REGSUBKEY_CTR *subkeys;
+ int i;
+ char *path;
+
+ /* swap out the SIDs in the security descriptor */
+
+ if ( !(new_sd = dup_sec_desc( outfile->mem_ctx, nk->sec_desc->sec_desc )) ) {
+ 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 );
+
+ if ( !(subkeys = TALLOC_ZERO_P( NULL, REGSUBKEY_CTR )) ) {
+ DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+ return False;
+ }
+
+ if ( !(values = TALLOC_ZERO_P( subkeys, REGVAL_CTR )) ) {
+ TALLOC_FREE( subkeys );
+ DEBUG(0,("copy_registry_tree: talloc() failure!\n"));
+ return False;
+ }
+
+ /* copy values into the REGVAL_CTR */
+
+ for ( i=0; i<nk->num_values; i++ ) {
+ regval_ctr_addvalue( values, nk->values[i].valuename, nk->values[i].type,
+ (const char *)nk->values[i].data, (nk->values[i].data_size & ~VK_DATA_IN_OFFSET) );
+ }
+
+ /* copy subkeys into the 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;
+ }
+ }
+
+ /* values is a talloc()'d child of subkeys here so just throw it all away */
+
+ TALLOC_FREE( subkeys );
+
+ verbose_output("[%s]\n", path);
+
+ return True;
+}
+
+/*********************************************************************
+*********************************************************************/
+
+int main( int argc, 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
+ { "change-sid", 'c', POPT_ARG_STRING, NULL, 'c', "Provides SID to change" },
+ { "new-sid", 'n', POPT_ARG_STRING, NULL, 'n', "Provides SID to change to" },
+ { "verbose", 'v', POPT_ARG_NONE, &opt_verbose, 'v', "Verbose output" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ load_case_tables();
+
+ /* setup logging options */
+
+ setup_logging( "profiles", True );
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ pc = poptGetContext("profiles", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ 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;
+
+ }
+ }
+
+ 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_IREAD|S_IWRITE) )) ) {
+ 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/rpccheck.c b/source3/utils/rpccheck.c
new file mode 100644
index 0000000000..87632db16d
--- /dev/null
+++ b/source3/utils/rpccheck.c
@@ -0,0 +1,62 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Jean François Micouleau 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"
+
+main()
+{
+ char filter[]="0123456789ABCDEF";
+
+ char s[128];
+ char d=0;
+ int x=0;
+ prs_struct ps;
+ TALLOC_CTX *ctx;
+
+ /* change that struct */
+ SAMR_R_QUERY_USERINFO rpc_stub;
+
+ ZERO_STRUCT(rpc_stub);
+
+ setup_logging("", True);
+ DEBUGLEVEL=10;
+
+ ctx=talloc_init("main");
+ if (!ctx) exit(1);
+
+ if (!prs_init(&ps, 1600, 4, ctx, MARSHALL))
+ exit(1);
+
+ while (scanf("%s", s)!=-1) {
+ if (strlen(s)==2 && strchr_m(filter, *s)!=NULL && strchr_m(filter, *(s+1))!=NULL) {
+ d=strtol(s, NULL, 16);
+ if(!prs_append_data(&ps, &d, 1))
+ printf("error while reading data\n");
+ }
+ }
+
+ prs_switch_type(&ps, UNMARSHALL);
+ prs_set_offset(&ps, 0);
+
+ /* change that call */
+ if(!samr_io_r_query_userinfo("", &rpc_stub, &ps, 0))
+ printf("error while UNMARSHALLING the data\n");
+
+ printf("\n");
+}
diff --git a/source3/utils/sharesec.c b/source3/utils/sharesec.c
new file mode 100644
index 0000000000..9409690c8b
--- /dev/null
+++ b/source3/utils/sharesec.c
@@ -0,0 +1,619 @@
+/*
+ * 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/>.
+ */
+
+
+#include "includes.h"
+
+static TALLOC_CTX *ctx;
+
+enum acl_mode {SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD, SMB_ACL_SET, SMB_ACL_VIEW };
+
+struct perm_value {
+ const char *perm;
+ uint32 mask;
+};
+
+/* These values discovered by inspection */
+
+static const struct perm_value special_values[] = {
+ { "R", SEC_RIGHTS_FILE_READ },
+ { "W", SEC_RIGHTS_FILE_WRITE },
+ { "X", SEC_RIGHTS_FILE_EXECUTE },
+ { "D", SEC_STD_DELETE },
+ { "P", SEC_STD_WRITE_DAC },
+ { "O", SEC_STD_WRITE_OWNER },
+ { NULL, 0 },
+};
+
+#define SEC_RIGHTS_DIR_CHANGE ( SEC_RIGHTS_DIR_READ|SEC_STD_DELETE|SEC_RIGHTS_DIR_WRITE|SEC_DIR_TRAVERSE )
+
+static const struct perm_value standard_values[] = {
+ { "READ", SEC_RIGHTS_DIR_READ|SEC_DIR_TRAVERSE },
+ { "CHANGE", SEC_RIGHTS_DIR_CHANGE },
+ { "FULL", SEC_RIGHTS_DIR_ALL },
+ { NULL, 0 },
+};
+
+/********************************************************************
+ print an ACE on a FILE
+********************************************************************/
+
+static void print_ace(FILE *f, SEC_ACE *ace)
+{
+ const struct perm_value *v;
+ int do_print = 0;
+ uint32 got_mask;
+
+ fprintf(f, "%s:", sid_string_tos(&ace->trustee));
+
+ /* Ace type */
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ fprintf(f, "ALLOWED");
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ fprintf(f, "DENIED");
+ } else {
+ fprintf(f, "%d", ace->type);
+ }
+
+ /* Not sure what flags can be set in a file ACL */
+
+ fprintf(f, "/%d/", ace->flags);
+
+ /* Standard permissions */
+
+ for (v = standard_values; v->perm; v++) {
+ if (ace->access_mask == v->mask) {
+ fprintf(f, "%s", v->perm);
+ return;
+ }
+ }
+
+ /* Special permissions. Print out a hex value if we have
+ leftover bits in the mask. */
+
+ got_mask = ace->access_mask;
+
+ again:
+ for (v = special_values; v->perm; v++) {
+ if ((ace->access_mask & v->mask) == v->mask) {
+ if (do_print) {
+ fprintf(f, "%s", v->perm);
+ }
+ got_mask &= ~v->mask;
+ }
+ }
+
+ if (!do_print) {
+ if (got_mask != 0) {
+ fprintf(f, "0x%08x", ace->access_mask);
+ } else {
+ do_print = 1;
+ goto again;
+ }
+ }
+}
+
+/********************************************************************
+ print a ascii version of a security descriptor on a FILE handle
+********************************************************************/
+
+static void sec_desc_print(FILE *f, SEC_DESC *sd)
+{
+ uint32 i;
+
+ fprintf(f, "REVISION:%d\n", sd->revision);
+
+ /* Print owner and group sid */
+
+ fprintf(f, "OWNER:%s\n", sid_string_tos(sd->owner_sid));
+
+ fprintf(f, "GROUP:%s\n", sid_string_tos(sd->group_sid));
+
+ /* Print aces */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ SEC_ACE *ace = &sd->dacl->aces[i];
+ fprintf(f, "ACL:");
+ print_ace(f, ace);
+ fprintf(f, "\n");
+ }
+
+}
+
+/********************************************************************
+ parse an ACE in the same format as print_ace()
+********************************************************************/
+
+static bool parse_ace(SEC_ACE *ace, const char *orig_str)
+{
+ char *p;
+ const char *cp;
+ char *tok;
+ unsigned int atype = 0;
+ unsigned int aflags = 0;
+ unsigned int amask = 0;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+ char *str = SMB_STRDUP(orig_str);
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!str) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) {
+ printf("ACE '%s': missing ':'.\n", orig_str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ string_to_sid(&sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!string_to_sid(&sid, str)) {
+ printf("ACE '%s': failed to convert '%s' to SID\n",
+ orig_str, str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ cp = p;
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ printf("ACE '%s': failed to find '/' character.\n",
+ orig_str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+ /* no flags on share permissions */
+
+ if (!(next_token_talloc(frame, &cp, &tok, "/") &&
+ sscanf(tok, "%i", &aflags) && aflags == 0)) {
+ printf("ACE '%s': bad integer flags entry at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ printf("ACE '%s': missing / at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ printf("ACE '%s': bad hex number at '%s'\n",
+ orig_str, tok);
+ TALLOC_FREE(frame);
+ SAFE_FREE(str);
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ bool found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) {
+ printf("ACE '%s': bad permission value at '%s'\n",
+ orig_str, p);
+ TALLOC_FREE(frame);
+ SAFE_FREE(str);
+ return False;
+ }
+ p++;
+ }
+
+ if (*p) {
+ TALLOC_FREE(frame);
+ SAFE_FREE(str);
+ return False;
+ }
+
+ done:
+ mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return True;
+}
+
+
+/********************************************************************
+********************************************************************/
+
+static SEC_DESC* parse_acl_string(TALLOC_CTX *mem_ctx, const char *szACL, size_t *sd_size )
+{
+ SEC_DESC *sd = NULL;
+ SEC_ACE *ace;
+ SEC_ACL *acl;
+ 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, SEC_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( &ace[i], acl_string ) )
+ return NULL;
+
+ pacl = end_acl;
+ pacl++;
+ }
+
+ if ( !(acl = make_sec_acl( mem_ctx, NT4_ACL_REVISION, num_ace, ace )) )
+ return NULL;
+
+ sd = make_sec_desc( mem_ctx, SEC_DESC_REVISION, SEC_DESC_SELF_RELATIVE,
+ NULL, NULL, NULL, acl, sd_size);
+
+ return sd;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static bool add_ace(TALLOC_CTX *mem_ctx, SEC_ACL **the_acl, SEC_ACE *ace)
+{
+ SEC_ACL *new_ace;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ return (((*the_acl) = make_sec_acl(mem_ctx, 3, 1, ace)) != NULL);
+ }
+
+ if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) {
+ return False;
+ }
+ memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new_ace = make_sec_acl(mem_ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new_ace;
+ 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(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return 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(SEC_ACE));
+}
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->aces[j] = the_acl->aces[j+1];
+ }
+ 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)
+{
+ SEC_DESC *sd;
+ SEC_DESC *old = NULL;
+ size_t sd_size = 0;
+ uint32 i, j;
+
+ if (mode != SMB_ACL_SET) {
+ 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) && !(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:
+ sec_desc_print( stdout, old);
+ 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 (sec_ace_equal(&sd->dacl->aces[i], &old->dacl->aces[j])) {
+ uint32 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(stdout, &sd->dacl->aces[i]);
+ 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 (sid_equal(&sd->dacl->aces[i].trustee,
+ &old->dacl->aces[j].trustee)) {
+ old->dacl->aces[j] = sd->dacl->aces[i];
+ found = True;
+ }
+ }
+
+ if (!found) {
+ printf("ACL for SID %s not found\n",
+ sid_string_tos(&sd->dacl->aces[i].trustee));
+ }
+ }
+
+ 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;
+ }
+
+ /* Denied ACE entries must come before allowed ones */
+ sort_acl(old->dacl);
+
+ if ( !set_share_security( sharename, old ) ) {
+ fprintf( stderr, "Failed to store acl for share [%s]\n", sharename );
+ return 2;
+ }
+ return 0;
+}
+
+/********************************************************************
+ main program
+********************************************************************/
+
+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;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "remove", 'r', POPT_ARG_STRING, &the_acl, 'r', "Delete an ACE", "ACL" },
+ { "modify", 'm', POPT_ARG_STRING, &the_acl, 'm', "Modify an acl", "ACL" },
+ { "add", 'a', POPT_ARG_STRING, &the_acl, 'a', "Add an ACE", "ACL" },
+ { "replace", 'R', POPT_ARG_STRING, &the_acl, 'R', "Set share mission ACL", "ACLS" },
+ { "view", 'v', POPT_ARG_NONE, NULL, 'v', "View current share permissions" },
+ { "machine-sid", 'M', POPT_ARG_NONE, NULL, 'M', "Initialize the machine SID" },
+ { "force", 'F', POPT_ARG_NONE, NULL, 'F', "Force storing the ACL", "ACLS" },
+ POPT_COMMON_SAMBA
+ { NULL }
+ };
+
+ if ( !(ctx = talloc_stackframe()) ) {
+ fprintf( stderr, "Failed to initialize talloc context!\n");
+ return -1;
+ }
+
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ setup_logging( "sharesec", True );
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ pc = poptGetContext("sharesec", argc, argv, long_options, 0);
+
+ 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 'v':
+ mode = SMB_ACL_VIEW;
+ break;
+
+ case 'F':
+ force_acl = True;
+ break;
+
+ case 'M':
+ initialize_sid = True;
+ break;
+ }
+ }
+
+ setlinebuf(stdout);
+
+ load_case_tables();
+
+ lp_load( get_dyn_CONFIGFILE(), False, False, False, True );
+
+ /* check for initializing secrets.tdb first */
+
+ if ( initialize_sid ) {
+ DOM_SID *sid = get_global_sam_sid();
+
+ if ( !sid ) {
+ fprintf( stderr, "Failed to retrieve Machine SID!\n");
+ return 3;
+ }
+
+ printf ("%s\n", sid_string_tos( sid ) );
+ return 0;
+ }
+
+ if ( mode == SMB_ACL_VIEW && force_acl ) {
+ fprintf( stderr, "Invalid combination of -F and -v\n");
+ return -1;
+ }
+
+ /* get the sharename */
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ fstrcpy(sharename, poptGetArg(pc));
+
+ snum = lp_servicenumber( sharename );
+
+ if ( snum == -1 && !force_acl ) {
+ fprintf( stderr, "Invalid sharename: %s\n", sharename);
+ return -1;
+ }
+
+ retval = change_share_sec(ctx, sharename, the_acl, mode);
+
+ talloc_destroy(ctx);
+
+ return retval;
+}
diff --git a/source3/utils/smbcacls.c b/source3/utils/smbcacls.c
new file mode 100644
index 0000000000..d488ce2187
--- /dev/null
+++ b/source3/utils/smbcacls.c
@@ -0,0 +1,1032 @@
+/*
+ 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
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General 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"
+
+static int test_args = False;
+
+#define CREATE_ACCESS_READ READ_CONTROL_ACCESS
+
+/* numeric is set when the user wants numeric SIDs and ACEs rather
+ than going via LSA calls to resolve them */
+static int numeric = False;
+
+enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD };
+enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP};
+enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR};
+
+struct perm_value {
+ const char *perm;
+ uint32 mask;
+};
+
+/* These values discovered by inspection */
+
+static const struct perm_value special_values[] = {
+ { "R", 0x00120089 },
+ { "W", 0x00120116 },
+ { "X", 0x001200a0 },
+ { "D", 0x00010000 },
+ { "P", 0x00040000 },
+ { "O", 0x00080000 },
+ { NULL, 0 },
+};
+
+static const struct perm_value standard_values[] = {
+ { "READ", 0x001200a9 },
+ { "CHANGE", 0x001301bf },
+ { "FULL", 0x001f01ff },
+ { NULL, 0 },
+};
+
+/* Open cli connection and policy handle */
+
+static NTSTATUS cli_lsa_lookup_sid(struct cli_state *cli,
+ const DOM_SID *sid,
+ TALLOC_CTX *mem_ctx,
+ enum lsa_SidType *type,
+ char **domain, char **name)
+{
+ uint16 orig_cnum = cli->cnum;
+ struct rpc_pipe_client *p;
+ struct policy_handle handle;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ enum lsa_SidType *types;
+ char **domains;
+ char **names;
+
+ if (!cli_send_tconX(cli, "IPC$", "?????", "", 0)) {
+ return cli_nt_error(cli);
+ }
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = rpccli_lsa_open_policy(p, talloc_tos(), True,
+ GENERIC_EXECUTE_ACCESS, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = rpccli_lsa_lookup_sids(p, talloc_tos(), &handle, 1, sid,
+ &domains, &names, &types);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ *type = types[0];
+ *domain = talloc_move(mem_ctx, &domains[0]);
+ *name = talloc_move(mem_ctx, &names[0]);
+
+ status = NT_STATUS_OK;
+ fail:
+ TALLOC_FREE(p);
+ cli_tdis(cli);
+ cli->cnum = orig_cnum;
+ TALLOC_FREE(frame);
+ return status;
+}
+
+static NTSTATUS cli_lsa_lookup_name(struct cli_state *cli,
+ const char *name,
+ enum lsa_SidType *type,
+ DOM_SID *sid)
+{
+ uint16 orig_cnum = cli->cnum;
+ struct rpc_pipe_client *p;
+ struct policy_handle handle;
+ NTSTATUS status;
+ TALLOC_CTX *frame = talloc_stackframe();
+ DOM_SID *sids;
+ enum lsa_SidType *types;
+
+ if (!cli_send_tconX(cli, "IPC$", "?????", "", 0)) {
+ return cli_nt_error(cli);
+ }
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_lsarpc.syntax_id,
+ &p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = rpccli_lsa_open_policy(p, talloc_tos(), True,
+ GENERIC_EXECUTE_ACCESS, &handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ status = rpccli_lsa_lookup_names(p, talloc_tos(), &handle, 1, &name,
+ NULL, 1, &sids, &types);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ *type = types[0];
+ *sid = sids[0];
+
+ status = NT_STATUS_OK;
+ fail:
+ TALLOC_FREE(p);
+ cli_tdis(cli);
+ cli->cnum = orig_cnum;
+ TALLOC_FREE(frame);
+ return status;
+}
+
+/* convert a SID to a string, either numeric or username/group */
+static void SidToString(struct cli_state *cli, fstring str, const DOM_SID *sid)
+{
+ char *domain = NULL;
+ char *name = NULL;
+ enum lsa_SidType type;
+ NTSTATUS status;
+
+ sid_to_fstring(str, sid);
+
+ if (numeric) {
+ return;
+ }
+
+ status = cli_lsa_lookup_sid(cli, sid, talloc_tos(), &type,
+ &domain, &name);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ slprintf(str, sizeof(fstring) - 1, "%s%s%s",
+ domain, lp_winbind_separator(), name);
+
+}
+
+/* convert a string to a SID, either numeric or username/group */
+static bool StringToSid(struct cli_state *cli, DOM_SID *sid, const char *str)
+{
+ enum lsa_SidType type;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ return NT_STATUS_IS_OK(cli_lsa_lookup_name(cli, str, &type, sid));
+}
+
+
+/* print an ACE on a FILE, using either numeric or ascii representation */
+static void print_ace(struct cli_state *cli, FILE *f, SEC_ACE *ace)
+{
+ const struct perm_value *v;
+ fstring sidstr;
+ int do_print = 0;
+ uint32 got_mask;
+
+ SidToString(cli, sidstr, &ace->trustee);
+
+ fprintf(f, "%s:", sidstr);
+
+ if (numeric) {
+ fprintf(f, "%d/%d/0x%08x",
+ ace->type, ace->flags, ace->access_mask);
+ return;
+ }
+
+ /* Ace type */
+
+ if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) {
+ fprintf(f, "ALLOWED");
+ } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) {
+ fprintf(f, "DENIED");
+ } else {
+ fprintf(f, "%d", ace->type);
+ }
+
+ /* Not sure what flags can be set in a file ACL */
+
+ fprintf(f, "/%d/", ace->flags);
+
+ /* Standard permissions */
+
+ for (v = standard_values; v->perm; v++) {
+ if (ace->access_mask == v->mask) {
+ fprintf(f, "%s", v->perm);
+ return;
+ }
+ }
+
+ /* Special permissions. Print out a hex value if we have
+ leftover bits in the mask. */
+
+ got_mask = ace->access_mask;
+
+ again:
+ for (v = special_values; v->perm; v++) {
+ if ((ace->access_mask & v->mask) == v->mask) {
+ if (do_print) {
+ fprintf(f, "%s", v->perm);
+ }
+ got_mask &= ~v->mask;
+ }
+ }
+
+ if (!do_print) {
+ if (got_mask != 0) {
+ fprintf(f, "0x%08x", ace->access_mask);
+ } else {
+ do_print = 1;
+ goto again;
+ }
+ }
+}
+
+
+/* parse an ACE in the same format as print_ace() */
+static bool parse_ace(struct cli_state *cli, SEC_ACE *ace,
+ const char *orig_str)
+{
+ char *p;
+ const char *cp;
+ char *tok;
+ unsigned int atype = 0;
+ unsigned int aflags = 0;
+ unsigned int amask = 0;
+ DOM_SID sid;
+ SEC_ACCESS mask;
+ const struct perm_value *v;
+ char *str = SMB_STRDUP(orig_str);
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ if (!str) {
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ ZERO_STRUCTP(ace);
+ p = strchr_m(str,':');
+ if (!p) {
+ printf("ACE '%s': missing ':'.\n", orig_str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+ *p = '\0';
+ p++;
+ /* Try to parse numeric form */
+
+ if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 &&
+ StringToSid(cli, &sid, str)) {
+ goto done;
+ }
+
+ /* Try to parse text form */
+
+ if (!StringToSid(cli, &sid, str)) {
+ printf("ACE '%s': failed to convert '%s' to SID\n",
+ orig_str, str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ cp = p;
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ printf("ACE '%s': failed to find '/' character.\n",
+ orig_str);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) {
+ atype = SEC_ACE_TYPE_ACCESS_DENIED;
+ } else {
+ printf("ACE '%s': missing 'ALLOWED' or 'DENIED' entry at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ /* Only numeric form accepted for flags at present */
+
+ if (!(next_token_talloc(frame, &cp, &tok, "/") &&
+ sscanf(tok, "%i", &aflags))) {
+ printf("ACE '%s': bad integer flags entry at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (!next_token_talloc(frame, &cp, &tok, "/")) {
+ printf("ACE '%s': missing / at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+
+ if (strncmp(tok, "0x", 2) == 0) {
+ if (sscanf(tok, "%i", &amask) != 1) {
+ printf("ACE '%s': bad hex number at '%s'\n",
+ orig_str, tok);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+ goto done;
+ }
+
+ for (v = standard_values; v->perm; v++) {
+ if (strcmp(tok, v->perm) == 0) {
+ amask = v->mask;
+ goto done;
+ }
+ }
+
+ p = tok;
+
+ while(*p) {
+ bool found = False;
+
+ for (v = special_values; v->perm; v++) {
+ if (v->perm[0] == *p) {
+ amask |= v->mask;
+ found = True;
+ }
+ }
+
+ if (!found) {
+ printf("ACE '%s': bad permission value at '%s'\n",
+ orig_str, p);
+ SAFE_FREE(str);
+ TALLOC_FREE(frame);
+ return False;
+ }
+ p++;
+ }
+
+ if (*p) {
+ TALLOC_FREE(frame);
+ SAFE_FREE(str);
+ return False;
+ }
+
+ done:
+ mask = amask;
+ init_sec_ace(ace, &sid, atype, mask, aflags);
+ TALLOC_FREE(frame);
+ SAFE_FREE(str);
+ return True;
+}
+
+/* add an ACE to a list of ACEs in a SEC_ACL */
+static bool add_ace(SEC_ACL **the_acl, SEC_ACE *ace)
+{
+ SEC_ACL *new_ace;
+ SEC_ACE *aces;
+ if (! *the_acl) {
+ return (((*the_acl) = make_sec_acl(talloc_tos(), 3, 1, ace))
+ != NULL);
+ }
+
+ if (!(aces = SMB_CALLOC_ARRAY(SEC_ACE, 1+(*the_acl)->num_aces))) {
+ return False;
+ }
+ memcpy(aces, (*the_acl)->aces, (*the_acl)->num_aces * sizeof(SEC_ACE));
+ memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE));
+ new_ace = make_sec_acl(talloc_tos(),(*the_acl)->revision,1+(*the_acl)->num_aces, aces);
+ SAFE_FREE(aces);
+ (*the_acl) = new_ace;
+ return True;
+}
+
+/* parse a ascii version of a security descriptor */
+static SEC_DESC *sec_desc_parse(TALLOC_CTX *ctx, struct cli_state *cli, char *str)
+{
+ const char *p = str;
+ char *tok;
+ SEC_DESC *ret = NULL;
+ size_t sd_size;
+ DOM_SID *grp_sid=NULL, *owner_sid=NULL;
+ SEC_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 (owner_sid) {
+ printf("Only specify owner once\n");
+ goto done;
+ }
+ owner_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!owner_sid ||
+ !StringToSid(cli, owner_sid, tok+6)) {
+ printf("Failed to parse owner sid\n");
+ goto done;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"GROUP:", 6) == 0) {
+ if (grp_sid) {
+ printf("Only specify group once\n");
+ goto done;
+ }
+ grp_sid = SMB_CALLOC_ARRAY(DOM_SID, 1);
+ if (!grp_sid ||
+ !StringToSid(cli, grp_sid, tok+6)) {
+ printf("Failed to parse group sid\n");
+ goto done;
+ }
+ continue;
+ }
+
+ if (strncmp(tok,"ACL:", 4) == 0) {
+ SEC_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, owner_sid, grp_sid,
+ NULL, dacl, &sd_size);
+
+ done:
+ SAFE_FREE(grp_sid);
+ SAFE_FREE(owner_sid);
+
+ return ret;
+}
+
+
+/* print a ascii version of a security descriptor on a FILE handle */
+static void sec_desc_print(struct cli_state *cli, FILE *f, SEC_DESC *sd)
+{
+ fstring sidstr;
+ uint32 i;
+
+ fprintf(f, "REVISION:%d\n", sd->revision);
+
+ /* Print owner and group sid */
+
+ if (sd->owner_sid) {
+ SidToString(cli, sidstr, sd->owner_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "OWNER:%s\n", sidstr);
+
+ if (sd->group_sid) {
+ SidToString(cli, sidstr, sd->group_sid);
+ } else {
+ fstrcpy(sidstr, "");
+ }
+
+ fprintf(f, "GROUP:%s\n", sidstr);
+
+ /* Print aces */
+ for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) {
+ SEC_ACE *ace = &sd->dacl->aces[i];
+ fprintf(f, "ACL:");
+ print_ace(cli, f, ace);
+ fprintf(f, "\n");
+ }
+
+}
+
+/*****************************************************
+dump the acls for a file
+*******************************************************/
+static int cacl_dump(struct cli_state *cli, char *filename)
+{
+ int result = EXIT_FAILED;
+ int fnum = -1;
+ SEC_DESC *sd;
+
+ if (test_args)
+ return EXIT_OK;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ goto done;
+ }
+
+ sd = cli_query_secdesc(cli, fnum, talloc_tos());
+
+ if (!sd) {
+ printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli));
+ goto done;
+ }
+
+ sec_desc_print(cli, stdout, sd);
+
+ result = EXIT_OK;
+
+done:
+ if (fnum != -1)
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+/*****************************************************
+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)
+{
+ int fnum;
+ DOM_SID sid;
+ SEC_DESC *sd, *old;
+ size_t sd_size;
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!StringToSid(cli, &sid, new_username))
+ return EXIT_PARSE_ERROR;
+
+ old = cli_query_secdesc(cli, fnum, talloc_tos());
+
+ cli_close(cli, fnum);
+
+ if (!old) {
+ printf("owner_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ sd = make_sec_desc(talloc_tos(),old->revision, old->type,
+ (change_mode == REQUEST_CHOWN) ? &sid : NULL,
+ (change_mode == REQUEST_CHGRP) ? &sid : NULL,
+ NULL, NULL, &sd_size);
+
+ fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ printf("Failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ }
+
+ cli_close(cli, fnum);
+
+ 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. */
+
+static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2)
+{
+ if (sec_ace_equal(ace1, ace2))
+ return 0;
+
+ if (ace1->type != ace2->type)
+ return ace2->type - ace1->type;
+
+ if (sid_compare(&ace1->trustee, &ace2->trustee))
+ return 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(SEC_ACE));
+}
+
+static void sort_acl(SEC_ACL *the_acl)
+{
+ uint32 i;
+ if (!the_acl) return;
+
+ qsort(the_acl->aces, the_acl->num_aces, sizeof(the_acl->aces[0]), QSORT_CAST ace_compare);
+
+ for (i=1;i<the_acl->num_aces;) {
+ if (sec_ace_equal(&the_acl->aces[i-1], &the_acl->aces[i])) {
+ int j;
+ for (j=i; j<the_acl->num_aces-1; j++) {
+ the_acl->aces[j] = the_acl->aces[j+1];
+ }
+ the_acl->num_aces--;
+ } else {
+ i++;
+ }
+ }
+}
+
+/*****************************************************
+set the ACLs on a file given an ascii description
+*******************************************************/
+static int cacl_set(struct cli_state *cli, char *filename,
+ char *the_acl, enum acl_mode mode)
+{
+ int fnum;
+ SEC_DESC *sd, *old;
+ uint32 i, j;
+ size_t sd_size;
+ int result = EXIT_OK;
+
+ sd = sec_desc_parse(talloc_tos(), cli, the_acl);
+
+ if (!sd) return EXIT_PARSE_ERROR;
+ if (test_args) return EXIT_OK;
+
+ /* The desired access below is the only one I could find that works
+ with NT4, W2KP and Samba */
+
+ fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ old = cli_query_secdesc(cli, fnum, talloc_tos());
+
+ if (!old) {
+ printf("calc_set: Failed to query old descriptor\n");
+ return EXIT_FAILED;
+ }
+
+ cli_close(cli, fnum);
+
+ /* 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 (sec_ace_equal(&sd->dacl->aces[i],
+ &old->dacl->aces[j])) {
+ uint32 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]);
+ 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 (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);
+ 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);
+
+ fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS);
+
+ if (fnum == -1) {
+ printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli));
+ return EXIT_FAILED;
+ }
+
+ if (!cli_set_secdesc(cli, fnum, sd)) {
+ printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli));
+ result = EXIT_FAILED;
+ }
+
+ /* Clean up */
+
+ cli_close(cli, fnum);
+
+ return result;
+}
+
+
+/*****************************************************
+ Return a connection to a server.
+*******************************************************/
+static struct cli_state *connect_one(const char *server, const char *share)
+{
+ struct cli_state *c = NULL;
+ struct sockaddr_storage ss;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+
+ zero_addr(&ss);
+
+ if (get_cmdline_auth_info_use_kerberos()) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS |
+ CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+ }
+
+ if (get_cmdline_auth_info_use_machine_account() &&
+ !set_cmdline_auth_info_machine_account_creds()) {
+ return NULL;
+ }
+
+ if (!get_cmdline_auth_info_got_pass()) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ set_cmdline_auth_info_password(pass);
+ }
+ }
+
+ nt_status = cli_full_connection(&c, global_myname(), server,
+ &ss, 0,
+ share, "?????",
+ get_cmdline_auth_info_username(),
+ lp_workgroup(),
+ get_cmdline_auth_info_password(),
+ flags,
+ get_cmdline_auth_info_signing_state(),
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ if (get_cmdline_auth_info_smb_encrypt()) {
+ nt_status = cli_cm_force_encryption(c,
+ get_cmdline_auth_info_username(),
+ get_cmdline_auth_info_password(),
+ lp_workgroup(),
+ share);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(c);
+ c = NULL;
+ }
+ }
+
+ return c;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, 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;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" },
+ { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" },
+ { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" },
+ { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" },
+ { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" },
+ { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" },
+ { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CONNECTION
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+
+ struct cli_state *cli;
+ TALLOC_CTX *frame = talloc_stackframe();
+ const char *owner_username = "";
+ char *server;
+
+ load_case_tables();
+
+
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ setup_logging( "smbcacls", True );
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ setlinebuf(stdout);
+
+ lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
+ load_interfaces();
+
+ pc = poptGetContext("smbcacls", argc, argv, long_options, 0);
+
+ 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;
+ }
+ }
+
+ /* Make connection to server */
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ path = talloc_strdup(frame, poptGetArg(pc));
+ if (!path) {
+ return -1;
+ }
+
+ if(!poptPeekArg(pc)) {
+ poptPrintUsage(pc, stderr, 0);
+ return -1;
+ }
+
+ filename = talloc_strdup(frame, poptGetArg(pc));
+ if (!filename) {
+ return -1;
+ }
+
+ string_replace(path,'/','\\');
+
+ server = talloc_strdup(frame, path+2);
+ if (!server) {
+ return -1;
+ }
+ share = strchr_m(server,'\\');
+ if (!share) {
+ printf("Invalid argument: %s\n", share);
+ return -1;
+ }
+
+ *share = 0;
+ share++;
+
+ if (!test_args) {
+ cli = connect_one(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;
+ }
+ }
+
+ /* Perform requested action */
+
+ if (change_mode != REQUEST_NONE) {
+ result = owner_set(cli, change_mode, filename, owner_username);
+ } else if (the_acl) {
+ result = cacl_set(cli, filename, the_acl, mode);
+ } else {
+ result = cacl_dump(cli, filename);
+ }
+
+ TALLOC_FREE(frame);
+
+ return result;
+}
diff --git a/source3/utils/smbcontrol.c b/source3/utils/smbcontrol.c
new file mode 100644
index 0000000000..750030d916
--- /dev/null
+++ b/source3/utils/smbcontrol.c
@@ -0,0 +1,1401 @@
+/*
+ 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"
+
+#if HAVE_LIBUNWIND_H
+#include <libunwind.h>
+#endif
+
+#if HAVE_LIBUNWIND_PTRACE_H
+#include <libunwind-ptrace.h>
+#endif
+
+#if 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)
+{
+ bool ret;
+ int n_sent = 0;
+
+ if (procid_to_pid(&pid) != 0)
+ return NT_STATUS_IS_OK(
+ messaging_send_buf(msg_ctx, pid, msg_type,
+ (uint8 *)buf, len));
+
+ ret = message_send_all(msg_ctx, msg_type, buf, len, &n_sent);
+ DEBUG(10,("smbcontrol/send_message: broadcast message to "
+ "%d processes\n", n_sent));
+
+ return ret;
+}
+
+static void timeout_handler(struct event_context *event_ctx,
+ struct timed_event *te,
+ const 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 messaging_context *msg_ctx,
+ bool multiple_replies)
+{
+ struct timed_event *te;
+ bool timed_out = False;
+
+ if (!(te = event_add_timed(messaging_event_context(msg_ctx), NULL,
+ timeval_current_ofs(timeout, 0),
+ "smbcontrol_timeout",
+ timeout_handler, (void *)&timed_out))) {
+ DEBUG(0, ("event_add_timed failed\n"));
+ return;
+ }
+
+ while (!timed_out) {
+ message_dispatch(msg_ctx);
+ if (num_replies > 0 && !multiple_replies)
+ break;
+ event_loop_once(messaging_event_context(msg_ctx));
+ }
+}
+
+/* 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)
+{
+ printf("PID %u: %.*s", (unsigned int)procid_to_pid(&pid),
+ (int)data->length, (const char *)data->data);
+ num_replies++;
+}
+
+/* Message handler callback that displays a string on stdout */
+
+static void print_string_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ printf("%.*s", (int)data->length, (const char *)data->data);
+ num_replies++;
+}
+
+/* Send no message. Useful for testing. */
+
+static bool do_noop(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 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);
+}
+
+#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 implementatino 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_connection(struct db_record *rec,
+ const struct connections_key *key,
+ const struct connections_data *crec,
+ void *priv)
+{
+ print_stack_trace(procid_to_pid(&crec->pid), (int *)priv);
+
+ return 0;
+}
+
+static bool do_daemon_stack_trace(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 {
+ connections_forall(stack_trace_connection, &count);
+ }
+
+ return True;
+}
+
+#else /* defined(HAVE_LIBUNWIND_PTRACE) && defined(HAVE_LINUX_PTRACE) */
+
+static bool do_daemon_stack_trace(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 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;
+ }
+
+#ifndef DEVELOPER
+ fprintf(stderr, "Fault injection is only available in "
+ "developer builds\n");
+ return False;
+#else /* DEVELOPER */
+ {
+ 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 */
+}
+
+/* Force a browser election */
+
+static bool do_election(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)
+{
+ char *src_string = procid_str(NULL, &pid);
+ printf("PONG from pid %s\n", src_string);
+ TALLOC_FREE(src_string);
+ num_replies++;
+}
+
+static bool do_ping(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(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 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 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(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 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(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 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(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(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(
+ 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(
+ 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(
+ argv[2], jobid, JOB_STATUS_DELETING,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ notify_job_status_byname(
+ argv[2], jobid, JOB_STATUS_DELETING|
+ JOB_STATUS_DELETED,
+ SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
+
+ goto send;
+
+ } else if (strcmp(cmd, "printer") == 0) {
+ uint32 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_COMMENT;
+ } else if (strcmp(argv[3], "port") == 0) {
+ attribute = PRINTER_NOTIFY_PORT_NAME;
+ } else if (strcmp(argv[3], "driver") == 0) {
+ attribute = PRINTER_NOTIFY_DRIVER_NAME;
+ } else {
+ fprintf(stderr, "Invalid printer command '%s'\n",
+ argv[3]);
+ return False;
+ }
+
+ notify_printer_byname(argv[2], attribute,
+ CONST_DISCARD(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 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);
+}
+
+/* force a blocking lock retry */
+
+static bool do_lockretry(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> lockretry\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_UNLOCK, NULL, 0);
+}
+
+/* force a validation of all brl entries, including re-sends. */
+
+static bool do_brl_revalidate(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> brl-revalidate\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_BRL_VALIDATE, NULL, 0);
+}
+
+/* Force a SAM synchronisation */
+
+static bool do_samsync(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samsync\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_SAM_SYNC, NULL, 0);
+}
+
+/* Force a SAM replication */
+
+static bool do_samrepl(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> samrepl\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_SMB_SAM_REPL, NULL, 0);
+}
+
+/* Display talloc pool usage */
+
+static bool do_poolusage(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n");
+ return False;
+ }
+
+ messaging_register(msg_ctx, NULL, MSG_POOL_USAGE, print_string_cb);
+
+ /* Send a message and register our interest in a reply */
+
+ if (!send_message(msg_ctx, pid, MSG_REQ_POOL_USAGE, NULL, 0))
+ return False;
+
+ wait_replies(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_POOL_USAGE, NULL);
+
+ return num_replies;
+}
+
+/* Perform a dmalloc mark */
+
+static bool do_dmalloc_mark(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 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);
+}
+
+/* Shutdown a server process */
+
+static bool do_shutdown(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 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_DEBUG, argv[1],
+ strlen(argv[1]) + 1);
+}
+
+static bool do_winbind_online(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd online\n");
+ return False;
+ }
+
+ /* Remove the entry in the winbindd_cache tdb to tell a later
+ starting winbindd that we're online. */
+
+ tdb = tdb_open_log(lock_path("winbindd_cache.tdb"), 0, TDB_DEFAULT, O_RDWR, 0600);
+ if (!tdb) {
+ fprintf(stderr, "Cannot open the tdb %s for writing.\n",
+ lock_path("winbindd_cache.tdb"));
+ return False;
+ }
+
+ 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 messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ TDB_CONTEXT *tdb;
+ bool ret = False;
+ int retry = 0;
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol winbindd offline\n");
+ 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(lock_path("winbindd_cache.tdb"),
+ WINBINDD_CACHE_TDB_DEFAULT_HASH_SIZE,
+ TDB_DEFAULT /* TDB_CLEAR_IF_FIRST */, O_RDWR|O_CREAT, 0600);
+
+ if (!tdb) {
+ fprintf(stderr, "Cannot open the tdb %s for writing.\n",
+ lock_path("winbindd_cache.tdb"));
+ return False;
+ }
+
+ /* 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++) {
+ TDB_DATA d;
+ uint8 buf[4];
+
+ ZERO_STRUCT(d);
+
+ SIVAL(buf, 0, time(NULL));
+ d.dptr = buf;
+ d.dsize = 4;
+
+ 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 || d.dsize != 4) {
+ SAFE_FREE(d.dptr);
+ DEBUG(10,("do_winbind_offline: offline state not set - retrying.\n"));
+ } else {
+ SAFE_FREE(d.dptr);
+ break;
+ }
+ }
+
+ tdb_close(tdb);
+ return ret;
+}
+
+static bool do_winbind_onlinestatus(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ struct server_id myid;
+
+ myid = pid_to_procid(sys_getpid());
+
+ 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, &myid,
+ sizeof(myid)))
+ return False;
+
+ wait_replies(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_dump_event_list(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ struct server_id myid;
+
+ myid = pid_to_procid(sys_getpid());
+
+ if (argc != 1) {
+ fprintf(stderr, "Usage: smbcontrol <dest> dump-event-list\n");
+ return False;
+ }
+
+ return send_message(msg_ctx, pid, MSG_DUMP_EVENT_LIST, NULL, 0);
+}
+
+static bool do_winbind_dump_domain_list(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ const char *domain = NULL;
+ int domain_len = 0;
+ struct server_id myid;
+ uint8_t *buf = NULL;
+ int buf_len = 0;
+
+ myid = pid_to_procid(sys_getpid());
+
+ 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);
+
+ buf_len = sizeof(myid)+domain_len;
+ buf = SMB_MALLOC_ARRAY(uint8_t, buf_len);
+ if (!buf) {
+ return false;
+ }
+
+ memcpy(buf, &myid, sizeof(myid));
+ memcpy(&buf[sizeof(myid)], domain, domain_len);
+
+ if (!send_message(msg_ctx, pid, MSG_WINBIND_DUMP_DOMAIN_LIST,
+ buf, buf_len))
+ {
+ SAFE_FREE(buf);
+ return false;
+ }
+
+ wait_replies(msg_ctx, procid_to_pid(&pid) == 0);
+
+ /* No replies were received within the timeout period */
+
+ SAFE_FREE(buf);
+ if (num_replies == 0) {
+ printf("No replies received\n");
+ }
+
+ messaging_deregister(msg_ctx, MSG_WINBIND_DUMP_DOMAIN_LIST, NULL);
+
+ return num_replies;
+}
+
+static void winbind_validate_cache_cb(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id pid,
+ DATA_BLOB *data)
+{
+ char *src_string = procid_str(NULL, &pid);
+ printf("Winbindd cache is %svalid. (answer from pid %s)\n",
+ (*(data->data) == 0 ? "" : "NOT "), src_string);
+ TALLOC_FREE(src_string);
+ num_replies++;
+}
+
+static bool do_winbind_validate_cache(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv)
+{
+ struct server_id myid = pid_to_procid(sys_getpid());
+
+ 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(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_config(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 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);
+ 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, global_scope(), 64, STR_TERMINATE);
+}
+
+static bool do_nodestatus(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);
+
+ (void)interpret_addr2(&p.ip, 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));
+}
+
+/* A list of message type supported */
+
+static const struct {
+ const char *name; /* Option name */
+ bool (*fn)(struct messaging_context *msg_ctx,
+ const struct server_id pid,
+ const int argc, const char **argv);
+ const char *help; /* Short help text */
+} msg_types[] = {
+ { "debug", do_debug, "Set debuglevel" },
+ { "force-election", do_election,
+ "Force a browse election" },
+ { "ping", do_ping, "Elicit a response" },
+ { "profile", do_profile, "" },
+ { "inject", do_inject_fault,
+ "Inject a fatal signal into a running smbd"},
+ { "stacktrace", do_daemon_stack_trace,
+ "Display a stack trace of a daemon" },
+ { "profilelevel", do_profilelevel, "" },
+ { "debuglevel", do_debuglevel, "Display current debuglevels" },
+ { "printnotify", do_printnotify, "Send a print notify message" },
+ { "close-share", do_closeshare, "Forcibly disconnect a share" },
+ { "lockretry", do_lockretry, "Force a blocking lock retry" },
+ { "brl-revalidate", do_brl_revalidate, "Revalidate all brl entries" },
+ { "samsync", do_samsync, "Initiate SAM synchronisation" },
+ { "samrepl", do_samrepl, "Initiate SAM replication" },
+ { "pool-usage", do_poolusage, "Display talloc memory usage" },
+ { "dmalloc-mark", do_dmalloc_mark, "" },
+ { "dmalloc-log-changed", do_dmalloc_changed, "" },
+ { "shutdown", do_shutdown, "Shut down daemon" },
+ { "drvupgrade", do_drvupgrade, "Notify a printer driver has changed" },
+ { "reload-config", do_reload_config, "Force smbd or winbindd to reload config file"},
+ { "nodestatus", do_nodestatus, "Ask nmbd to do a node status request"},
+ { "online", do_winbind_online, "Ask winbind to go into online state"},
+ { "offline", do_winbind_offline, "Ask winbind to go into offline state"},
+ { "onlinestatus", do_winbind_onlinestatus, "Request winbind online status"},
+ { "dump-event-list", do_dump_event_list, "Dump event list"},
+ { "validate-cache" , do_winbind_validate_cache,
+ "Validate winbind's credential cache" },
+ { "dump-domain-list", do_winbind_dump_domain_list, "Dump winbind domain list"},
+ { "noop", do_noop, "Do nothing" },
+ { 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\" or a "
+ "process ID\n");
+
+ fprintf(stderr, "\n");
+ fprintf(stderr, "<message-type> is one of:\n");
+
+ for (i = 0; msg_types[i].name; i++)
+ fprintf(stderr, "\t%-30s%s\n", msg_types[i].name,
+ msg_types[i].help);
+
+ fprintf(stderr, "\n");
+
+ exit(1);
+}
+
+/* Return the pid number for a string destination */
+
+static struct server_id parse_dest(const char *dest)
+{
+ struct server_id result = {-1};
+ pid_t pid;
+
+ /* 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 pid_to_procid(sys_getpid());
+ }
+
+ /* Fix winbind typo. */
+ if (strequal(dest, "winbind")) {
+ dest = "winbindd";
+ }
+
+ if (!(strequal(dest, "winbindd") || strequal(dest, "nmbd"))) {
+ /* Check for numeric pid number */
+
+ result = interpret_pid(dest);
+
+ /* Zero isn't valid if not smbd. */
+ if (result.pid && procid_valid(&result)) {
+ return result;
+ }
+ }
+
+ /* Look up other destinations in pidfile directory */
+
+ if ((pid = pidfile_pid(dest)) != 0) {
+ return pid_to_procid(pid);
+ }
+
+ fprintf(stderr,"Can't find pid for destination '%s'\n", dest);
+
+ return result;
+}
+
+/* Execute smbcontrol command */
+
+static bool do_command(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(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(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 },
+ { NULL }
+} ;
+
+/* Main program */
+
+int main(int argc, const char **argv)
+{
+ poptContext pc;
+ int opt;
+ struct event_context *evt_ctx;
+ struct messaging_context *msg_ctx;
+
+ static 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_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ int ret = 0;
+
+ load_case_tables();
+
+ setup_logging(argv[0],True);
+
+ /* Parse command line arguments using popt */
+
+ pc = poptGetContext(
+ "smbcontrol", argc, (const char **)argv, long_options, 0);
+
+ 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);
+
+ lp_load(get_dyn_CONFIGFILE(),False,False,False,True);
+
+ /* Need to invert sense of return code -- samba
+ * routines mostly return True==1 for success, but
+ * shell needs 0. */
+
+ if (!(evt_ctx = event_context_init(NULL)) ||
+ !(msg_ctx = messaging_init(NULL, server_id_self(), evt_ctx))) {
+ fprintf(stderr, "could not init messaging context\n");
+ TALLOC_FREE(frame);
+ exit(1);
+ }
+
+ ret = !do_command(msg_ctx, argc, argv);
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/smbcquotas.c b/source3/utils/smbcquotas.c
new file mode 100644
index 0000000000..11f8776a0e
--- /dev/null
+++ b/source3/utils/smbcquotas.c
@@ -0,0 +1,619 @@
+/*
+ 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"
+
+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 POLICY_HND 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.syntax_id,
+ &global_pipe_hnd);
+ if (!NT_STATUS_IS_OK(ret)) {
+ return False;
+ }
+ }
+
+ /* Open policy handle */
+
+ if (!got_policy_hnd) {
+
+ /* Some systems don't support SEC_RIGHTS_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, 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(DOM_SID *sid, const char *str)
+{
+ enum lsa_SidType *types = NULL;
+ DOM_SID *sids = NULL;
+ bool result = True;
+
+ if (strncmp(str, "S-", 2) == 0) {
+ return string_to_sid(sid, str);
+ }
+
+ 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 defined(HAVE_LONGLONG)
+ if (sscanf(p,"%llu/%llu",&pqt->softlim,&pqt->hardlim)!=2) {
+#else
+ if (sscanf(p,"%lu/%lu",&pqt->softlim,&pqt->hardlim)!=2) {
+#endif
+ 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 int do_quota(struct cli_state *cli,
+ enum SMB_QUOTA_TYPE qtype,
+ uint16 cmd,
+ const char *username_str,
+ SMB_NTQUOTA_STRUCT *pqt)
+{
+ uint32 fs_attrs = 0;
+ int quota_fnum = 0;
+ SMB_NTQUOTA_LIST *qtl = NULL;
+ SMB_NTQUOTA_STRUCT qt;
+ ZERO_STRUCT(qt);
+
+ if (!cli_get_fs_attr_info(cli, &fs_attrs)) {
+ d_printf("Failed to get the filesystem attributes %s.\n",
+ cli_errstr(cli));
+ return -1;
+ }
+
+ if (!(fs_attrs & FILE_VOLUME_QUOTAS)) {
+ d_printf("Quotas are not supported by the server.\n");
+ return 0;
+ }
+
+ if (!cli_get_quota_handle(cli, &quota_fnum)) {
+ d_printf("Quotas are not enabled on this share.\n");
+ d_printf("Failed to open %s %s.\n",
+ FAKE_FILE_NAME_QUOTA_WIN32,cli_errstr(cli));
+ 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:
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_SETLIM:
+ pqt->sid = qt.sid;
+ if (!cli_set_user_quota(cli, quota_fnum, pqt)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ if (!cli_get_user_quota(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ return -1;
+ }
+ dump_ntquota(&qt,verbose,numeric,SidToString);
+ break;
+ case QUOTA_LIST:
+ if (!cli_list_user_quota(cli, quota_fnum, &qtl)) {
+ d_printf("%s cli_set_user_quota %s\n",
+ cli_errstr(cli),username_str);
+ 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:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETLIM:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.softlim = pqt->softlim;
+ qt.hardlim = pqt->hardlim;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ dump_ntquota(&qt,True,numeric,NULL);
+ break;
+ case QUOTA_SETFLAGS:
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ qt.qflags = pqt->qflags;
+ if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_set_fs_quota_info\n",
+ cli_errstr(cli));
+ return -1;
+ }
+ if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) {
+ d_printf("%s cli_get_fs_quota_info\n",
+ cli_errstr(cli));
+ 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;
+ struct sockaddr_storage ss;
+ NTSTATUS nt_status;
+ uint32_t flags = 0;
+
+ zero_addr(&ss);
+
+ if (get_cmdline_auth_info_use_machine_account() &&
+ !set_cmdline_auth_info_machine_account_creds()) {
+ return NULL;
+ }
+
+ if (get_cmdline_auth_info_use_kerberos()) {
+ flags |= CLI_FULL_CONNECTION_USE_KERBEROS |
+ CLI_FULL_CONNECTION_FALLBACK_AFTER_KERBEROS;
+
+ }
+
+ if (!get_cmdline_auth_info_got_pass()) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ set_cmdline_auth_info_password(pass);
+ }
+ }
+
+ nt_status = cli_full_connection(&c, global_myname(), server,
+ &ss, 0,
+ share, "?????",
+ get_cmdline_auth_info_username(),
+ lp_workgroup(),
+ get_cmdline_auth_info_password(),
+ flags,
+ get_cmdline_auth_info_signing_state(),
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status)));
+ return NULL;
+ }
+
+ if (get_cmdline_auth_info_smb_encrypt()) {
+ nt_status = cli_cm_force_encryption(c,
+ get_cmdline_auth_info_username(),
+ get_cmdline_auth_info_password(),
+ lp_workgroup(),
+ share);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ cli_shutdown(c);
+ return NULL;
+ }
+ }
+
+ return c;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc, 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 poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "user", 'u', POPT_ARG_STRING, NULL, 'u', "Show quotas for user", "user" },
+ { "list", 'L', POPT_ARG_NONE, NULL, 'L', "List user quotas" },
+ { "fs", 'F', POPT_ARG_NONE, NULL, 'F', "Show filesystem quotas" },
+ { "set", 'S', POPT_ARG_STRING, NULL, 'S', "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", "SETSTRING" },
+ { "numeric", 'n', POPT_ARG_NONE, NULL, 'n', "Don't resolve sids or limits to names" },
+ { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "be verbose" },
+ { "test-args", 't', POPT_ARG_NONE, NULL, 'r', "Test arguments"},
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ { NULL }
+ };
+
+ load_case_tables();
+
+ ZERO_STRUCT(qt);
+
+ /* set default debug level to 1 regardless of what smb.conf sets */
+ setup_logging( "smbcquotas", True );
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 1;
+ dbf = x_stderr;
+ x_setbuf( x_stderr, NULL );
+
+ setlinebuf(stdout);
+
+ fault_setup(NULL);
+
+ lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
+ load_interfaces();
+
+ pc = poptGetContext("smbcquotas", argc, argv, long_options, 0);
+
+ 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;
+ }
+ }
+
+ if (todo == 0)
+ todo = USER_QUOTA;
+
+ if (!fix_user) {
+ username_str = talloc_strdup(frame, get_cmdline_auth_info_username());
+ 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);
+ }
+
+ 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) {
+ printf("Invalid argument: %s\n", share);
+ 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;
+ }
+
+ talloc_free(frame);
+
+ return result;
+}
diff --git a/source3/utils/smbfilter.c b/source3/utils/smbfilter.c
new file mode 100644
index 0000000000..d274e09299
--- /dev/null
+++ b/source3/utils/smbfilter.c
@@ -0,0 +1,287 @@
+/*
+ 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"
+
+#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 char packet[BUFFER_SIZE];
+
+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);
+ 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)
+{
+ int msg_type = CVAL(buf,0);
+ int type = CVAL(buf,smb_com);
+ fstring name1,name2;
+ unsigned x;
+
+ if (msg_type) {
+ /* it's a netbios special */
+ switch (msg_type) {
+ case 0x81:
+ /* session request */
+ name_extract(buf,4,name1);
+ name_extract(buf,4 + name_len(buf + 4),name2);
+ d_printf("sesion_request: %s -> %s\n",
+ name1, name2);
+ if (netbiosname) {
+ /* replace the destination netbios name */
+ name_mangle(netbiosname, buf+4, 0x20);
+ }
+ }
+ 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));
+ system("mv sessionsetup.dat sessionsetup1.dat");
+ 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)
+{
+ int s;
+
+ /* we have a connection from a new client, now connect to the server */
+ s = open_socket_out(SOCK_STREAM, dest_ss, 445, LONG_CONNECT_TIMEOUT);
+
+ if (s == -1) {
+ 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",strerror(errno));
+ exit(1);
+ }
+
+ while (c != -1 || s != -1) {
+ fd_set fds;
+ int num;
+
+ FD_ZERO(&fds);
+ if (s != -1) FD_SET(s, &fds);
+ if (c != -1) FD_SET(c, &fds);
+
+ num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL);
+ if (num <= 0) continue;
+
+ if (c != -1 && FD_ISSET(c, &fds)) {
+ 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);
+ if (!send_smb(s, packet)) {
+ d_printf("server is dead\n");
+ exit(1);
+ }
+ }
+ if (s != -1 && FD_ISSET(s, &fds)) {
+ 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_addr(&my_ss);
+ s = open_socket_in(SOCK_STREAM, 445, 0, &my_ss, True);
+
+ if (s == -1) {
+ d_printf("bind failed\n");
+ exit(1);
+ }
+
+ if (listen(s, 5) == -1) {
+ d_printf("listen failed\n");
+ }
+
+ if (!resolve_name(desthost, &dest_ss, 0x20)) {
+ d_printf("Unable to resolve host %s\n", desthost);
+ exit(1);
+ }
+
+ while (1) {
+ fd_set fds;
+ int num;
+ struct sockaddr_storage ss;
+ socklen_t in_addrlen = sizeof(ss);
+
+ FD_ZERO(&fds);
+ FD_SET(s, &fds);
+
+ num = sys_select_intr(s+1,&fds,NULL,NULL,NULL);
+ if (num > 0) {
+ c = accept(s, (struct sockaddr *)&ss, &in_addrlen);
+ if (c != -1) {
+ 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();
+
+ load_case_tables();
+
+ setup_logging(argv[0],True);
+
+ 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(configfile,True,False,False,True)) {
+ d_printf("Unable to load config file\n");
+ }
+
+ start_filter(desthost);
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/smbget.c b/source3/utils/smbget.c
new file mode 100644
index 0000000000..3d4a71b71d
--- /dev/null
+++ b/source3/utils/smbget.c
@@ -0,0 +1,644 @@
+/*
+ smbget: a wget-like utility with support for recursive downloading and
+ 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 "libsmbclient.h"
+
+#if _FILE_OFFSET_BITS==64
+#define OFF_T_FORMAT "%lld"
+#define OFF_T_FORMAT_CAST long long
+#else
+#define OFF_T_FORMAT "%ld"
+#define OFF_T_FORMAT_CAST long
+#endif
+
+static int columns = 0;
+
+static int debuglevel, update;
+static char *outputfile;
+
+
+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
+
+static const char *username = NULL, *password = NULL, *workgroup = NULL;
+static int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0, send_stdout = 0;
+static int blocksize = SMB_DEFAULT_BLOCKSIZE;
+
+static int smb_download_file(const char *base, const char *name, int recursive, int resume, 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, OFF_T_FORMAT"b", (OFF_T_FORMAT_CAST)s);
+}
+
+static void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen)
+{
+ static char hasasked = 0;
+ char *wgtmp, *usertmp;
+ char tmp[128];
+
+ if(hasasked) return;
+ hasasked = 1;
+
+ if(!nonprompt && !username) {
+ printf("Username for %s at %s [guest] ", shr, srv);
+ fgets(tmp, sizeof(tmp), stdin);
+ if(tmp[strlen(tmp)-1] == '\n')tmp[strlen(tmp)-1] = '\0';
+ strncpy(un, tmp, unlen-1);
+ } else if(username) strncpy(un, username, unlen-1);
+
+ if(!nonprompt && !password) {
+ char *prompt, *pass;
+ asprintf(&prompt, "Password for %s at %s: ", shr, srv);
+ pass = getpass(prompt);
+ free(prompt);
+ strncpy(pw, pass, pwlen-1);
+ } else if(password) strncpy(pw, password, pwlen-1);
+
+ if(workgroup)strncpy(wg, workgroup, wglen-1);
+
+ wgtmp = SMB_STRNDUP(wg, wglen);
+ usertmp = SMB_STRNDUP(un, unlen);
+ if(!quiet)printf("Using workgroup %s, %s%s\n", wgtmp, *usertmp?"user ":"guest user", usertmp);
+ free(wgtmp); free(usertmp);
+}
+
+static int 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;
+ struct stat remotestat;
+ 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, 1, resume, NULL);
+ fprintf(stderr, "Can't open directory %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ while(*relname == '/')relname++;
+ mkdir(relname, 0755);
+
+ tmpname = SMB_STRDUP(name);
+
+ while((dirent = smbc_readdir(dirhandle))) {
+ char *newname;
+ if(!strcmp(dirent->name, ".") || !strcmp(dirent->name, ".."))continue;
+ asprintf(&newname, "%s/%s", tmpname, dirent->name);
+ switch(dirent->smbc_type) {
+ case SMBC_DIR:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_WORKGROUP:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_SERVER:
+ smb_download_dir("smb://", dirent->name, resume);
+ break;
+
+ case SMBC_FILE:
+ smb_download_file(base, newname, 1, resume, NULL);
+ break;
+
+ case SMBC_FILE_SHARE:
+ smb_download_dir(base, newname, resume);
+ break;
+
+ case SMBC_PRINTER_SHARE:
+ if(!quiet)printf("Ignoring printer share %s\n", dirent->name);
+ break;
+
+ case SMBC_COMMS_SHARE:
+ if(!quiet)printf("Ignoring comms share %s\n", dirent->name);
+ break;
+
+ case SMBC_IPC_SHARE:
+ if(!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;
+ }
+ free(newname);
+ }
+ free(tmpname);
+
+ if(keep_permissions) {
+ if(smbc_fstat(dirhandle, &remotestat) < 0) {
+ fprintf(stderr, "Unable to get stats on %s on remote server\n", path);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+
+ if(chmod(relname, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local dir %s to %o\n", relname, remotestat.st_mode);
+ smbc_closedir(dirhandle);
+ return 0;
+ }
+ }
+
+ smbc_closedir(dirhandle);
+ return 1;
+}
+
+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[20], htotal[20], havg[20];
+ 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(columns) {
+ int required = strlen(name), available = columns - len - strlen("[] ");
+ if(required > available) asprintf(&filename, "...%s", name + required - available + 3);
+ else filename = SMB_STRNDUP(name, available);
+ } else filename = SMB_STRDUP(name);
+
+ fprintf(stderr, "\r[%s] %s", filename, status);
+
+ free(filename); free(status);
+}
+
+static int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) {
+ int remotehandle, localhandle;
+ time_t start_time = time(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;
+
+ 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 0;
+ }
+ smb_download_dir(base, name, resume);
+ return 0;
+
+ case ENOENT:
+ fprintf(stderr, "%s can't be found on the remote server\n", path);
+ return 0;
+
+ case ENOMEM:
+ fprintf(stderr, "Not enough memory\n");
+ exit(1);
+ return 0;
+
+ case ENODEV:
+ fprintf(stderr, "The share name used in %s does not exist\n", path);
+ return 0;
+
+ case EACCES:
+ fprintf(stderr, "You don't have enough permissions to access %s\n", path);
+ return 0;
+
+ default:
+ perror("smbc_open");
+ return 0;
+ }
+ }
+
+ if(smbc_fstat(remotehandle, &remotestat) < 0) {
+ fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno));
+ return 0;
+ }
+
+ if(outfile) newpath = outfile;
+ else if(!name[0]) {
+ newpath = strrchr(base, '/');
+ if(newpath)newpath++; else newpath = base;
+ } else newpath = name;
+
+ if(newpath[0] == '/')newpath++;
+
+ /* Open local file according to the mode */
+ if(update) {
+ /* if it is up-to-date, skip */
+ if(stat(newpath, &localstat) == 0 &&
+ localstat.st_mtime >= remotestat.st_mtime) {
+ if(verbose)
+ printf("%s is up-to-date, skipping\n", newpath);
+ smbc_close(remotehandle);
+ return 0;
+ }
+ /* 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 0;
+ }
+ /* no offset */
+ } else if(!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 0;
+ }
+
+ if (fstat(localhandle, &localstat) != 0) {
+ fprintf(stderr, "Can't fstat %s: %s\n", newpath, strerror(errno));
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 0;
+ }
+
+ start_offset = localstat.st_size;
+
+ if(localstat.st_size && localstat.st_size == remotestat.st_size) {
+ if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path);
+ else if(!quiet)fprintf(stderr, "%s\n", path);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 1;
+ }
+
+ 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(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n"
+ "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n",
+ newpath, (OFF_T_FORMAT_CAST)offset_check,
+ (OFF_T_FORMAT_CAST)localstat.st_size,
+ (OFF_T_FORMAT_CAST)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 "OFF_T_FORMAT" in local file %s\n",
+ (OFF_T_FORMAT_CAST)offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET);
+ if(off2 < 0) {
+ fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n",
+ (OFF_T_FORMAT_CAST)offset_check, newpath);
+ smbc_close(remotehandle); close(localhandle);
+ return 0;
+ }
+
+ if(off1 != off2) {
+ fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n",
+ (OFF_T_FORMAT_CAST)off1,
+ (OFF_T_FORMAT_CAST)off2);
+ return 0;
+ }
+
+ 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 0;
+ }
+
+ 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 0;
+ }
+
+ if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) {
+ if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", (OFF_T_FORMAT_CAST)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 0;
+ }
+ }
+ } else {
+ localhandle = STDOUT_FILENO;
+ start_offset = 0;
+ offset_download = 0;
+ offset_check = 0;
+ }
+
+ readbuf = (char *)SMB_MALLOC(blocksize);
+
+ /* Now, download all bytes from offset_download to the end */
+ for(curpos = offset_download; curpos < remotestat.st_size; curpos+=blocksize) {
+ ssize_t bytesread = smbc_read(remotehandle, readbuf, blocksize);
+ if(bytesread < 0) {
+ fprintf(stderr, "Can't read %u bytes at offset "OFF_T_FORMAT", file %s\n", (unsigned int)blocksize, (OFF_T_FORMAT_CAST)curpos, path);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ free(readbuf);
+ return 0;
+ }
+
+ total_bytes += bytesread;
+
+ if(write(localhandle, readbuf, bytesread) < 0) {
+ fprintf(stderr, "Can't write %u bytes to local file %s at offset "OFF_T_FORMAT"\n", (unsigned int)bytesread, path, (OFF_T_FORMAT_CAST)curpos);
+ free(readbuf);
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 0;
+ }
+
+ if(dots)fputc('.', stderr);
+ else if(!quiet) {
+ print_progress(newpath, start_time, time(NULL), start_offset, curpos, remotestat.st_size);
+ }
+ }
+
+ free(readbuf);
+
+ if(dots){
+ fputc('\n', stderr);
+ printf("%s downloaded\n", path);
+ } else if(!quiet) {
+ int i;
+ fprintf(stderr, "\r%s", path);
+ if(columns) {
+ for(i = strlen(path); i < columns; i++) {
+ fputc(' ', stderr);
+ }
+ }
+ fputc('\n', stderr);
+ }
+
+ if(keep_permissions && !send_stdout) {
+ if(fchmod(localhandle, remotestat.st_mode) < 0) {
+ fprintf(stderr, "Unable to change mode of local file %s to %o\n", path, remotestat.st_mode);
+ smbc_close(remotehandle);
+ close(localhandle);
+ return 0;
+ }
+ }
+
+ smbc_close(remotehandle);
+ if (localhandle != STDOUT_FILENO) close(localhandle);
+ return 1;
+}
+
+static void clean_exit(void)
+{
+ char bs[100];
+ human_readable(total_bytes, bs, sizeof(bs));
+ if(!quiet)fprintf(stderr, "Downloaded %s in %lu seconds\n", bs, time(NULL) - total_start_time);
+ exit(0);
+}
+
+static void signal_quit(int v)
+{
+ clean_exit();
+}
+
+static int readrcfile(const char *name, const struct poptOption long_options[])
+{
+ FILE *fd = fopen(name, "r");
+ int lineno = 0, i;
+ char var[101], val[101];
+ char found;
+ int *intdata; char **stringdata;
+ if(!fd) {
+ fprintf(stderr, "Can't open RC file %s\n", name);
+ return 1;
+ }
+
+ while(!feof(fd)) {
+ lineno++;
+ if(fscanf(fd, "%100s %100s\n", var, val) < 2) {
+ fprintf(stderr, "Can't parse line %d of %s, ignoring.\n", lineno, name);
+ continue;
+ }
+
+ found = 0;
+
+ for(i = 0; long_options[i].shortName; i++) {
+ if(!long_options[i].longName)continue;
+ if(strcmp(long_options[i].longName, var)) continue;
+ if(!long_options[i].arg)continue;
+
+ switch(long_options[i].argInfo) {
+ case POPT_ARG_NONE:
+ intdata = (int *)long_options[i].arg;
+ if(!strcmp(val, "on")) *intdata = 1;
+ else if(!strcmp(val, "off")) *intdata = 0;
+ else fprintf(stderr, "Illegal value %s for %s at line %d in %s\n", val, var, lineno, name);
+ break;
+ case POPT_ARG_INT:
+ intdata = (int *)long_options[i].arg;
+ *intdata = atoi(val);
+ break;
+ case POPT_ARG_STRING:
+ stringdata = (char **)long_options[i].arg;
+ *stringdata = SMB_STRDUP(val);
+ break;
+ default:
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ break;
+ }
+
+ found = 1;
+ }
+ if(!found) {
+ fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name);
+ }
+ }
+
+ fclose(fd);
+ return 0;
+}
+
+int main(int argc, const char **argv)
+{
+ int c = 0;
+ const char *file = NULL;
+ char *rcfile = NULL;
+ bool smb_encrypt = false;
+ int resume = 0, recursive = 0;
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct poptOption long_options[] = {
+ {"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" },
+ {"encrypt", 'e', POPT_ARG_NONE, NULL, 'e', "Encrypt SMB transport (UNIX extended servers only)" },
+ {"resume", 'r', POPT_ARG_NONE, &resume, 0, "Automatically resume aborted files" },
+ {"update", 'U', POPT_ARG_NONE, &update, 0, "Download only when remote file is newer than local file or local file is missing"},
+ {"recursive", 'R', POPT_ARG_NONE, &recursive, 0, "Recursively download files" },
+ {"username", 'u', POPT_ARG_STRING, &username, 'u', "Username to use" },
+ {"password", 'p', POPT_ARG_STRING, &password, 'p', "Password to use" },
+ {"workgroup", 'w', POPT_ARG_STRING, &workgroup, 'w', "Workgroup to use (optional)" },
+ {"nonprompt", 'n', POPT_ARG_NONE, &nonprompt, 'n', "Don't ask anything (non-interactive)" },
+ {"debuglevel", 'd', POPT_ARG_INT, &debuglevel, 'd', "Debuglevel to use" },
+ {"outputfile", 'o', POPT_ARG_STRING, &outputfile, 'o', "Write downloaded data to specified file" },
+ {"stdout", 'O', POPT_ARG_NONE, &send_stdout, 'O', "Write data to stdout" },
+ {"dots", 'D', POPT_ARG_NONE, &dots, 'D', "Show dots as progress indication" },
+ {"quiet", 'q', POPT_ARG_NONE, &quiet, 'q', "Be quiet" },
+ {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" },
+ {"keep-permissions", 'P', POPT_ARG_NONE, &keep_permissions, 'P', "Keep permissions" },
+ {"blocksize", 'b', POPT_ARG_INT, &blocksize, 'b', "Change number of bytes in a block"},
+ {"rcfile", 'f', POPT_ARG_STRING, NULL, 0, "Use specified rc file"},
+ POPT_AUTOHELP
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ load_case_tables();
+
+ /* only read rcfile if it exists */
+ asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME"));
+ if(access(rcfile, F_OK) == 0)
+ readrcfile(rcfile, long_options);
+ free(rcfile);
+
+#ifdef SIGWINCH
+ signal(SIGWINCH, change_columns);
+#endif
+ signal(SIGINT, signal_quit);
+ signal(SIGTERM, signal_quit);
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+
+ while((c = poptGetNextOpt(pc)) >= 0) {
+ switch(c) {
+ case 'f':
+ readrcfile(poptGetOptArg(pc), long_options);
+ break;
+ case 'a':
+ username = ""; password = "";
+ break;
+ case 'e':
+ smb_encrypt = true;
+ break;
+ }
+ }
+
+ if((send_stdout || resume || outputfile) && update) {
+ fprintf(stderr, "The -o, -R or -O and -U options can not be used together.\n");
+ return 1;
+ }
+ if((send_stdout || outputfile) && recursive) {
+ fprintf(stderr, "The -o or -O and -R options can not be used together.\n");
+ return 1;
+ }
+
+ if(outputfile && send_stdout) {
+ fprintf(stderr, "The -o and -O options cannot be used together.\n");
+ return 1;
+ }
+
+ if(smbc_init(get_auth_data, debuglevel) < 0) {
+ fprintf(stderr, "Unable to initialize libsmbclient\n");
+ return 1;
+ }
+
+ if (smb_encrypt) {
+ SMBCCTX *smb_ctx = smbc_set_context(NULL);
+ smbc_option_set(smb_ctx,
+ CONST_DISCARD(char *, "smb_encrypt_level"),
+ "require");
+ }
+
+ columns = get_num_cols();
+
+ total_start_time = time(NULL);
+
+ while ( (file = poptGetArg(pc)) ) {
+ if (!recursive)
+ return smb_download_file(file, "", recursive, resume, outputfile);
+ else
+ return smb_download_dir(file, "", resume);
+ }
+
+ clean_exit();
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/smbpasswd.c b/source3/utils/smbpasswd.c
new file mode 100644
index 0000000000..600fe52f0d
--- /dev/null
+++ b/source3/utils/smbpasswd.c
@@ -0,0 +1,603 @@
+/*
+ * 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"
+
+extern bool AllowDebugChange;
+
+/*
+ * 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\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)
+{
+ 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:LW")) != EOF) {
+ switch(ch) {
+ case 'L':
+#if !defined(NSS_WRAPPER)
+ if (getuid() != 0) {
+ fprintf(stderr, "smbpasswd -L can only be used by root.\n");
+ exit(1);
+ }
+#endif
+ local_flags |= LOCAL_AM_ROOT;
+ break;
+ case 'c':
+ 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;
+ 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':
+ lp_set_name_resolve_order(optarg);
+ break;
+ case 'D':
+ DEBUGLEVEL = atoi(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(configfile,True,False,False,True)) {
+ 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);
+
+ fstrcpy(new_pw, p);
+ SAFE_FREE(p);
+
+ p = get_pass("Retype new SMB password:", stdin_get);
+
+ 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, char *username,
+ char *old_passwd, 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 */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ ret = remote_password_change(remote_mach, username,
+ old_passwd, new_pw, &err_str);
+ if (err_str != NULL)
+ fprintf(stderr, "%s", err_str);
+ SAFE_FREE(err_str);
+ return ret;
+ }
+
+ 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);
+
+ 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) {
+ 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);
+ 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(NULL, 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 */
+ static fstring buf;
+
+ /*
+ * Remove any trailing '$' before we
+ * generate the initial machine password.
+ */
+
+ if (user_name[strlen(user_name)-1] == '$') {
+ user_name[strlen(user_name)-1] = 0;
+ }
+
+ if (local_flags & LOCAL_ADD_USER) {
+ SAFE_FREE(new_passwd);
+ new_passwd = smb_xstrdup(user_name);
+ strlower_m(new_passwd);
+ }
+
+ /*
+ * Now ensure the username ends in '$' for
+ * the machine add.
+ */
+
+ slprintf(buf, sizeof(buf)-1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+ } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) {
+ static fstring buf;
+
+ 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);
+ }
+ }
+
+ /* prepare uppercased and '$' terminated username */
+ slprintf(buf, sizeof(buf) - 1, "%s$", user_name);
+ fstrcpy(user_name, buf);
+
+ } else {
+
+ if (remote_machine != NULL) {
+ old_passwd = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ 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, user_name,
+ old_passwd, new_passwd,
+ local_flags))) {
+ fprintf(stderr,"Failed to modify password entry for user %s\n", user_name);
+ 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(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;
+
+ 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(NULL, 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);
+ }
+ }
+
+ /*
+ * 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) {
+ remote_machine = "127.0.0.1";
+ }
+
+ if (remote_machine != NULL) {
+ old_pw = get_pass("Old SMB password:",stdin_passwd_get);
+ }
+
+ 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, user_name, old_pw,
+ new_pw, 0))) {
+ fprintf(stderr,"Failed to change password for %s\n", user_name);
+ result = 1;
+ goto done;
+ }
+
+ printf("Password changed for user %s\n", user_name);
+
+ done:
+ SAFE_FREE(old_pw);
+ SAFE_FREE(new_pw);
+
+ return result;
+}
+
+
+
+/*********************************************************
+ Start here.
+**********************************************************/
+int main(int argc, char **argv)
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ int local_flags = 0;
+ int ret;
+
+ AllowDebugChange = False;
+
+#if defined(HAVE_SET_AUTH_PARAMETERS)
+ set_auth_parameters(argc, argv);
+#endif /* HAVE_SET_AUTH_PARAMETERS */
+
+ if (getuid() == 0) {
+ local_flags = LOCAL_AM_ROOT;
+ }
+
+ load_case_tables();
+
+ local_flags = process_options(argc, argv, local_flags);
+
+ setup_logging("smbpasswd", True);
+
+ /*
+ * Set the machine NETBIOS name if not already
+ * set from the config file.
+ */
+
+ if (!init_names())
+ return 1;
+
+ /* 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) {
+ secrets_init();
+ return process_root(local_flags);
+ }
+
+ ret = process_nonroot(local_flags);
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/smbtree.c b/source3/utils/smbtree.c
new file mode 100644
index 0000000000..ce2de4d7fe
--- /dev/null
+++ b/source3/utils/smbtree.c
@@ -0,0 +1,332 @@
+/*
+ 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"
+
+static int use_bcast;
+
+/* How low can we go? */
+
+enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE};
+static enum tree_level level = LEV_SHARE;
+
+/* Holds a list of workgroups or servers */
+
+struct name_list {
+ struct name_list *prev, *next;
+ char *name, *comment;
+ uint32 server_type;
+};
+
+static struct name_list *workgroups, *servers, *shares;
+
+static void free_name_list(struct name_list *list)
+{
+ while(list)
+ DLIST_REMOVE(list, list);
+}
+
+static void add_name(const char *machine_name, uint32 server_type,
+ const char *comment, void *state)
+{
+ struct name_list **name_list = (struct name_list **)state;
+ struct name_list *new_name;
+
+ new_name = SMB_MALLOC_P(struct name_list);
+
+ if (!new_name)
+ return;
+
+ ZERO_STRUCTP(new_name);
+
+ new_name->name = SMB_STRDUP(machine_name);
+ new_name->comment = SMB_STRDUP(comment);
+ new_name->server_type = server_type;
+
+ if (!new_name->name || !new_name->comment) {
+ SAFE_FREE(new_name->name);
+ SAFE_FREE(new_name->comment);
+ SAFE_FREE(new_name);
+ return;
+ }
+
+ DLIST_ADD(*name_list, new_name);
+}
+
+/****************************************************************************
+ display tree of smb workgroups, servers and shares
+****************************************************************************/
+static bool get_workgroups(struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct sockaddr_storage server_ss;
+ TALLOC_CTX *ctx = talloc_tos();
+ char *master_workgroup = NULL;
+
+ /* Try to connect to a #1d name of our current workgroup. If that
+ doesn't work broadcast for a master browser and then jump off
+ that workgroup. */
+
+ master_workgroup = talloc_strdup(ctx, lp_workgroup());
+ if (!master_workgroup) {
+ return false;
+ }
+
+ if (!use_bcast && !find_master_ip(lp_workgroup(), &server_ss)) {
+ DEBUG(4, ("Unable to find master browser for workgroup %s, falling back to broadcast\n",
+ master_workgroup));
+ use_bcast = True;
+ } else if(!use_bcast) {
+ char addr[INET6_ADDRSTRLEN];
+ print_sockaddr(addr, sizeof(addr), &server_ss);
+ if (!(cli = get_ipc_connect(addr, &server_ss, user_info)))
+ return False;
+ }
+
+ if (!(cli = get_ipc_connect_master_ip_bcast(talloc_tos(),
+ user_info,
+ &master_workgroup))) {
+ DEBUG(4, ("Unable to find master browser by "
+ "broadcast\n"));
+ return False;
+ }
+
+ if (!cli_NetServerEnum(cli, master_workgroup,
+ SV_TYPE_DOMAIN_ENUM, add_name, &workgroups))
+ return False;
+
+ return True;
+}
+
+/* Retrieve the list of servers for a given workgroup */
+
+static bool get_servers(char *workgroup, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+ struct sockaddr_storage server_ss;
+ char addr[INET6_ADDRSTRLEN];
+
+ /* Open an IPC$ connection to the master browser for the workgroup */
+
+ if (!find_master_ip(workgroup, &server_ss)) {
+ DEBUG(4, ("Cannot find master browser for workgroup %s\n",
+ workgroup));
+ return False;
+ }
+
+ print_sockaddr(addr, sizeof(addr), &server_ss);
+ if (!(cli = get_ipc_connect(addr, &server_ss, user_info)))
+ return False;
+
+ if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name,
+ &servers))
+ return False;
+
+ return True;
+}
+
+static bool get_rpc_shares(struct cli_state *cli,
+ void (*fn)(const char *, uint32, const char *, void *),
+ void *state)
+{
+ NTSTATUS status;
+ struct rpc_pipe_client *pipe_hnd;
+ TALLOC_CTX *mem_ctx;
+ WERROR werr;
+ struct srvsvc_NetShareInfoCtr info_ctr;
+ struct srvsvc_NetShareCtr1 ctr1;
+ int i;
+ uint32_t resume_handle = 0;
+ uint32_t total_entries = 0;
+
+ mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ DEBUG(0, ("talloc_new failed\n"));
+ return False;
+ }
+
+ status = cli_rpc_pipe_open_noauth(cli, &ndr_table_srvsvc.syntax_id,
+ &pipe_hnd);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(10, ("Could not connect to srvsvc pipe: %s\n",
+ nt_errstr(status)));
+ TALLOC_FREE(mem_ctx);
+ return False;
+ }
+
+ ZERO_STRUCT(info_ctr);
+ ZERO_STRUCT(ctr1);
+
+ info_ctr.level = 1;
+ info_ctr.ctr.ctr1 = &ctr1;
+
+ status = rpccli_srvsvc_NetShareEnumAll(pipe_hnd, mem_ctx,
+ pipe_hnd->desthost,
+ &info_ctr,
+ 0xffffffff,
+ &total_entries,
+ &resume_handle,
+ &werr);
+
+ if (!NT_STATUS_IS_OK(status) || !W_ERROR_IS_OK(werr)) {
+ TALLOC_FREE(mem_ctx);
+ TALLOC_FREE(pipe_hnd);
+ return False;
+ }
+
+ for (i=0; i<total_entries; i++) {
+ struct srvsvc_NetShareInfo1 info = info_ctr.ctr.ctr1->array[i];
+ fn(info.name, info.type, info.comment, state);
+ }
+
+ TALLOC_FREE(mem_ctx);
+ TALLOC_FREE(pipe_hnd);
+ return True;
+}
+
+
+static bool get_shares(char *server_name, struct user_auth_info *user_info)
+{
+ struct cli_state *cli;
+
+ if (!(cli = get_ipc_connect(server_name, NULL, user_info)))
+ return False;
+
+ if (get_rpc_shares(cli, add_name, &shares))
+ return True;
+
+ if (!cli_RNetShareEnum(cli, add_name, &shares))
+ return False;
+
+ return True;
+}
+
+static bool print_tree(struct user_auth_info *user_info)
+{
+ struct name_list *wg, *sv, *sh;
+
+ /* List workgroups */
+
+ if (!get_workgroups(user_info))
+ return False;
+
+ for (wg = workgroups; wg; wg = wg->next) {
+
+ printf("%s\n", wg->name);
+
+ /* List servers */
+
+ free_name_list(servers);
+ servers = NULL;
+
+ if (level == LEV_WORKGROUP ||
+ !get_servers(wg->name, user_info))
+ continue;
+
+ for (sv = servers; sv; sv = sv->next) {
+
+ printf("\t\\\\%-15s\t\t%s\n",
+ sv->name, sv->comment);
+
+ /* List shares */
+
+ free_name_list(shares);
+ shares = NULL;
+
+ if (level == LEV_SERVER ||
+ !get_shares(sv->name, user_info))
+ continue;
+
+ for (sh = shares; sh; sh = sh->next) {
+ printf("\t\t\\\\%s\\%-15s\t%s\n",
+ sv->name, sh->name, sh->comment);
+ }
+ }
+ }
+
+ return True;
+}
+
+/****************************************************************************
+ main program
+****************************************************************************/
+ int main(int argc,char *argv[])
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct user_auth_info local_auth_info;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ { "broadcast", 'b', POPT_ARG_VAL, &use_bcast, True, "Use broadcast instead of using the master browser" },
+ { "domains", 'D', POPT_ARG_VAL, &level, LEV_WORKGROUP, "List only domains (workgroups) of tree" },
+ { "servers", 'S', POPT_ARG_VAL, &level, LEV_SERVER, "List domains(workgroups) and servers of tree" },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_CREDENTIALS
+ POPT_TABLEEND
+ };
+ poptContext pc;
+
+ /* Initialise samba stuff */
+ load_case_tables();
+
+ setlinebuf(stdout);
+
+ dbf = x_stderr;
+
+ setup_logging(argv[0],True);
+
+ pc = poptGetContext("smbtree", argc, (const char **)argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ while(poptGetNextOpt(pc) != -1);
+ poptFreeContext(pc);
+
+ lp_load(get_dyn_CONFIGFILE(),True,False,False,True);
+ load_interfaces();
+
+ /* Parse command line args */
+
+ if (get_cmdline_auth_info_use_machine_account() &&
+ !set_cmdline_auth_info_machine_account_creds()) {
+ TALLOC_FREE(frame);
+ return 1;
+ }
+
+ if (!get_cmdline_auth_info_got_pass()) {
+ char *pass = getpass("Password: ");
+ if (pass) {
+ set_cmdline_auth_info_password(pass);
+ }
+ }
+
+ /* Now do our stuff */
+
+ if (!get_cmdline_auth_info_copy(&local_auth_info)) {
+ return 1;
+ }
+
+ if (!print_tree(&local_auth_info)) {
+ TALLOC_FREE(frame);
+ return 1;
+ }
+
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/smbw_sample.c b/source3/utils/smbw_sample.c
new file mode 100644
index 0000000000..eface2fc46
--- /dev/null
+++ b/source3/utils/smbw_sample.c
@@ -0,0 +1,96 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+static void usage(void)
+{
+ printf("
+smbw_sample - a sample program that uses smbw
+
+smbw_sample <options> path
+
+ options:
+ -W workgroup
+ -l logfile
+ -P prefix
+ -d debuglevel
+ -U username%%password
+ -R resolve order
+
+note that path must start with /smb/
+");
+}
+
+int main(int argc, char *argv[])
+{
+ DIR *dir;
+ struct dirent *dent;
+ int opt;
+ char *p;
+ extern char *optarg;
+ extern int optind;
+ char *path;
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ lp_load(get_dyn_CONFIGFILE(),1,0,0,1);
+ smbw_setup_shared();
+
+ while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) {
+ switch (opt) {
+ case 'W':
+ smbw_setshared("WORKGROUP", optarg);
+ break;
+ case 'l':
+ smbw_setshared("LOGFILE", optarg);
+ break;
+ case 'P':
+ smbw_setshared("PREFIX", optarg);
+ break;
+ case 'd':
+ smbw_setshared("DEBUG", optarg);
+ break;
+ case 'U':
+ p = strchr_m(optarg,'%');
+ if (p) {
+ *p=0;
+ smbw_setshared("PASSWORD",p+1);
+ }
+ smbw_setshared("USER", optarg);
+ break;
+ case 'R':
+ smbw_setshared("RESOLVE_ORDER",optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ path = argv[0];
+
+ smbw_init();
+
+ dir = smbw_opendir(path);
+ if (!dir) {
+ printf("failed to open %s\n", path);
+ exit(1);
+ }
+
+ while ((dent = smbw_readdir(dir))) {
+ printf("%s\n", dent->d_name);
+ }
+ smbw_closedir(dir);
+ TALLOC_FREE(frame);
+ return 0;
+}
diff --git a/source3/utils/status.c b/source3/utils/status.c
new file mode 100644
index 0000000000..1a66af949a
--- /dev/null
+++ b/source3/utils/status.c
@@ -0,0 +1,485 @@
+/*
+ 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"
+
+#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;
+
+const char *username = NULL;
+
+extern bool status_profile_dump(bool be_verbose);
+extern bool status_profile_rates(bool be_verbose);
+
+/* 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 (cluster_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 ) {
+ d_printf("ERROR: More than %d pids for user %s!\n",
+ SMB_MAXPIDS, uidtoname(Ucrit_uid));
+
+ return False;
+ }
+
+ Ucrit_pid[Ucrit_MaxPid++] = pid;
+
+ return True;
+}
+
+static void print_share_mode(const struct share_mode_entry *e,
+ const char *sharepath,
+ const char *fname,
+ void *dummy)
+{
+ static int count;
+
+ if (!is_valid_share_mode_entry(e)) {
+ return;
+ }
+
+ if (!process_exists(e->pid)) {
+ return;
+ }
+
+ if (count==0) {
+ d_printf("Locked files:\n");
+ d_printf("Pid Uid DenyMode Access R/W Oplock SharePath Name Time\n");
+ d_printf("--------------------------------------------------------------------------------------------------\n");
+ }
+ count++;
+
+ if (Ucrit_checkPid(e->pid)) {
+ d_printf("%-11s ",procid_str_static(&e->pid));
+ d_printf("%-9u ", (unsigned int)e->uid);
+ switch (map_share_mode_to_deny_mode(e->share_access,
+ e->private_options)) {
+ case DENY_NONE: d_printf("DENY_NONE "); break;
+ case DENY_ALL: d_printf("DENY_ALL "); break;
+ case DENY_DOS: d_printf("DENY_DOS "); break;
+ case DENY_READ: d_printf("DENY_READ "); break;
+ case DENY_WRITE:printf("DENY_WRITE "); break;
+ case DENY_FCB: d_printf("DENY_FCB "); break;
+ default: {
+ d_printf("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;
+ }
+ }
+ d_printf("0x%-8x ",(unsigned int)e->access_mask);
+ if ((e->access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))==
+ (FILE_READ_DATA|FILE_WRITE_DATA)) {
+ d_printf("RDWR ");
+ } else if (e->access_mask & FILE_WRITE_DATA) {
+ d_printf("WRONLY ");
+ } else {
+ d_printf("RDONLY ");
+ }
+
+ if((e->op_type & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
+ (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) {
+ d_printf("EXCLUSIVE+BATCH ");
+ } else if (e->op_type & EXCLUSIVE_OPLOCK) {
+ d_printf("EXCLUSIVE ");
+ } else if (e->op_type & BATCH_OPLOCK) {
+ d_printf("BATCH ");
+ } else if (e->op_type & LEVEL_II_OPLOCK) {
+ d_printf("LEVEL_II ");
+ } else {
+ d_printf("NONE ");
+ }
+
+ d_printf(" %s %s %s",sharepath, fname, time_to_asc((time_t)e->time.tv_sec));
+ }
+}
+
+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)
+{
+ static int count;
+ int i;
+ static const struct {
+ enum brl_type lock_type;
+ const char *desc;
+ } lock_types[] = {
+ { READ_LOCK, "R" },
+ { WRITE_LOCK, "W" },
+ { PENDING_READ_LOCK, "PR" },
+ { PENDING_WRITE_LOCK, "PW" },
+ { UNLOCK_LOCK, "U" }
+ };
+ const char *desc="X";
+ const char *sharepath = "";
+ const char *fname = "";
+ struct share_mode_lock *share_mode;
+
+ if (count==0) {
+ d_printf("Byte range locks:\n");
+ d_printf("Pid dev:inode R/W start size SharePath Name\n");
+ d_printf("--------------------------------------------------------------------------------\n");
+ }
+ count++;
+
+ share_mode = fetch_share_mode_unlocked(NULL, id, "__unspecified__", "__unspecified__");
+ if (share_mode) {
+ sharepath = share_mode->servicepath;
+ fname = share_mode->filename;
+ }
+
+ for (i=0;i<ARRAY_SIZE(lock_types);i++) {
+ if (lock_type == lock_types[i].lock_type) {
+ desc = lock_types[i].desc;
+ }
+ }
+
+ d_printf("%-10s %-15s %-4s %-9.0f %-9.0f %-24s %-24s\n",
+ procid_str_static(&pid), file_id_string_tos(&id),
+ desc,
+ (double)start, (double)size,
+ sharepath, fname);
+
+ TALLOC_FREE(share_mode);
+}
+
+static int traverse_fn1(struct db_record *rec,
+ const struct connections_key *key,
+ const struct connections_data *crec,
+ void *state)
+{
+ if (crec->cnum == -1)
+ return 0;
+
+ if (!process_exists(crec->pid) || !Ucrit_checkUid(crec->uid)) {
+ return 0;
+ }
+
+ d_printf("%-10s %s %-12s %s",
+ crec->servicename,procid_str_static(&crec->pid),
+ crec->machine,
+ time_to_asc(crec->start));
+
+ return 0;
+}
+
+static int traverse_sessionid(struct db_record *db, void *state)
+{
+ struct sessionid sessionid;
+ fstring uid_str, gid_str;
+
+ if (db->value.dsize != sizeof(sessionid))
+ return 0;
+
+ memcpy(&sessionid, db->value.dptr, sizeof(sessionid));
+
+ if (!process_exists(sessionid.pid) || !Ucrit_checkUid(sessionid.uid)) {
+ return 0;
+ }
+
+ Ucrit_addPid( sessionid.pid );
+
+ fstr_sprintf(uid_str, "%d", sessionid.uid);
+ fstr_sprintf(gid_str, "%d", sessionid.gid);
+
+ d_printf("%-7s %-12s %-12s %-12s (%s)\n",
+ procid_str_static(&sessionid.pid),
+ numeric_only ? uid_str : uidtoname(sessionid.uid),
+ numeric_only ? gid_str : gidtoname(sessionid.gid),
+ sessionid.remote_machine, sessionid.hostname);
+
+ return 0;
+}
+
+
+
+
+ int main(int argc, char *argv[])
+{
+ int c;
+ int profile_only = 0;
+ bool show_processes, show_locks, show_shares;
+ poptContext pc;
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"processes", 'p', POPT_ARG_NONE, NULL, 'p', "Show processes only" },
+ {"verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Be verbose" },
+ {"locks", 'L', POPT_ARG_NONE, NULL, 'L', "Show locks only" },
+ {"shares", 'S', POPT_ARG_NONE, NULL, 'S', "Show shares only" },
+ {"user", 'u', POPT_ARG_STRING, &username, 'u', "Switch to user" },
+ {"brief", 'b', POPT_ARG_NONE, NULL, 'b', "Be brief" },
+ {"profile", 'P', POPT_ARG_NONE, NULL, 'P', "Do profiling" },
+ {"profile-rates", 'R', POPT_ARG_NONE, NULL, 'R', "Show call rates" },
+ {"byterange", 'B', POPT_ARG_NONE, NULL, 'B', "Include byte range locks"},
+ {"numeric", 'n', POPT_ARG_NONE, NULL, 'n', "Numeric uid/gid"},
+ POPT_COMMON_SAMBA
+ POPT_TABLEEND
+ };
+ TALLOC_CTX *frame = talloc_stackframe();
+ int ret = 0;
+ struct messaging_context *msg_ctx;
+
+ sec_init();
+ load_case_tables();
+
+ setup_logging(argv[0],True);
+
+ dbf = x_stderr;
+
+ if (getuid() != geteuid()) {
+ d_printf("smbstatus should not be run setuid\n");
+ ret = 1;
+ goto done;
+ }
+
+ pc = poptGetContext(NULL, argc, (const char **) argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+
+ 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 '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;
+ }
+ }
+
+ /* 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) {
+ d_printf("using configfile = %s\n", get_dyn_CONFIGFILE());
+ }
+
+ if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ get_dyn_CONFIGFILE());
+ ret = -1;
+ goto done;
+ }
+
+ /*
+ * This implicitly initializes the global ctdbd connection, usable by
+ * the db_open() calls further down.
+ */
+
+ msg_ctx = messaging_init(NULL, procid_self(),
+ event_context_init(NULL));
+
+ if (msg_ctx == NULL) {
+ fprintf(stderr, "messaging_init failed\n");
+ ret = -1;
+ goto done;
+ }
+
+ if (!lp_load(get_dyn_CONFIGFILE(),False,False,False,True)) {
+ fprintf(stderr, "Can't load %s - run testparm to debug it\n",
+ get_dyn_CONFIGFILE());
+ ret = -1;
+ goto done;
+ }
+
+ switch (profile_only) {
+ case 'P':
+ /* Dump profile data */
+ return status_profile_dump(verbose);
+ case 'R':
+ /* Continuously display rate-converted data */
+ return status_profile_rates(verbose);
+ default:
+ break;
+ }
+
+ if ( show_processes ) {
+ struct db_context *db;
+ db = db_open(NULL, lock_path("sessionid.tdb"), 0,
+ TDB_CLEAR_IF_FIRST, O_RDONLY, 0644);
+ if (!db) {
+ d_printf("sessionid.tdb not initialised\n");
+ } else {
+ d_printf("\nSamba version %s\n",SAMBA_VERSION_STRING);
+ d_printf("PID Username Group Machine \n");
+ d_printf("-------------------------------------------------------------------\n");
+
+ db->traverse_read(db, traverse_sessionid, NULL);
+ TALLOC_FREE(db);
+ }
+
+ if (processes_only) {
+ goto done;
+ }
+ }
+
+ if ( show_shares ) {
+ if (verbose) {
+ d_printf("Opened %s\n", lock_path("connections.tdb"));
+ }
+
+ if (brief) {
+ goto done;
+ }
+
+ d_printf("\nService pid machine Connected at\n");
+ d_printf("-------------------------------------------------------\n");
+
+ connections_forall(traverse_fn1, NULL);
+
+ d_printf("\n");
+
+ if ( shares_only ) {
+ goto done;
+ }
+ }
+
+ if ( show_locks ) {
+ int result;
+ struct db_context *db;
+ db = db_open(NULL, lock_path("locking.tdb"), 0,
+ TDB_CLEAR_IF_FIRST, O_RDONLY, 0);
+
+ if (!db) {
+ d_printf("%s not initialised\n",
+ lock_path("locking.tdb"));
+ d_printf("This is normal if an SMB client has never "
+ "connected to your server.\n");
+ exit(0);
+ } else {
+ TALLOC_FREE(db);
+ }
+
+ if (!locking_init_readonly()) {
+ d_printf("Can't initialise locking module - exiting\n");
+ ret = 1;
+ goto done;
+ }
+
+ result = share_mode_forall(print_share_mode, NULL);
+
+ if (result == 0) {
+ d_printf("No locked files\n");
+ } else if (result == -1) {
+ d_printf("locked file list truncated\n");
+ }
+
+ d_printf("\n");
+
+ if (show_brl) {
+ brl_forall(print_brl, NULL);
+ }
+
+ locking_end();
+ }
+
+done:
+ TALLOC_FREE(frame);
+ return ret;
+}
diff --git a/source3/utils/status_profile.c b/source3/utils/status_profile.c
new file mode 100644
index 0000000000..48814fedea
--- /dev/null
+++ b/source3/utils/status_profile.c
@@ -0,0 +1,541 @@
+/*
+ * 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"
+
+bool status_profile_dump(bool be_verbose);
+bool status_profile_rates(bool be_verbose);
+
+#ifdef WITH_PROFILE
+static void profile_separator(const char * title)
+{
+ char line[79 + 1];
+ char * end;
+
+ 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);
+}
+#endif
+
+/*******************************************************************
+ dump the elements of the profile structure
+ ******************************************************************/
+bool status_profile_dump(bool verbose)
+{
+#ifdef WITH_PROFILE
+ if (!profile_setup(NULL, True)) {
+ fprintf(stderr,"Failed to initialise profile memory\n");
+ return False;
+ }
+
+ d_printf("smb_count: %u\n", profile_p->smb_count);
+ d_printf("uid_changes: %u\n", profile_p->uid_changes);
+
+ profile_separator("System Calls");
+ d_printf("opendir_count: %u\n", profile_p->syscall_opendir_count);
+ d_printf("opendir_time: %u\n", profile_p->syscall_opendir_time);
+ d_printf("readdir_count: %u\n", profile_p->syscall_readdir_count);
+ d_printf("readdir_time: %u\n", profile_p->syscall_readdir_time);
+ d_printf("mkdir_count: %u\n", profile_p->syscall_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->syscall_mkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->syscall_rmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->syscall_rmdir_time);
+ d_printf("closedir_count: %u\n", profile_p->syscall_closedir_count);
+ d_printf("closedir_time: %u\n", profile_p->syscall_closedir_time);
+ d_printf("open_count: %u\n", profile_p->syscall_open_count);
+ d_printf("open_time: %u\n", profile_p->syscall_open_time);
+ d_printf("close_count: %u\n", profile_p->syscall_close_count);
+ d_printf("close_time: %u\n", profile_p->syscall_close_time);
+ d_printf("read_count: %u\n", profile_p->syscall_read_count);
+ d_printf("read_time: %u\n", profile_p->syscall_read_time);
+ d_printf("read_bytes: %u\n", profile_p->syscall_read_bytes);
+ d_printf("write_count: %u\n", profile_p->syscall_write_count);
+ d_printf("write_time: %u\n", profile_p->syscall_write_time);
+ d_printf("write_bytes: %u\n", profile_p->syscall_write_bytes);
+ d_printf("pread_count: %u\n", profile_p->syscall_pread_count);
+ d_printf("pread_time: %u\n", profile_p->syscall_pread_time);
+ d_printf("pread_bytes: %u\n", profile_p->syscall_pread_bytes);
+ d_printf("pwrite_count: %u\n", profile_p->syscall_pwrite_count);
+ d_printf("pwrite_time: %u\n", profile_p->syscall_pwrite_time);
+ d_printf("pwrite_bytes: %u\n", profile_p->syscall_pwrite_bytes);
+#ifdef WITH_SENDFILE
+ d_printf("sendfile_count: %u\n", profile_p->syscall_sendfile_count);
+ d_printf("sendfile_time: %u\n", profile_p->syscall_sendfile_time);
+ d_printf("sendfile_bytes: %u\n", profile_p->syscall_sendfile_bytes);
+#endif
+ d_printf("lseek_count: %u\n", profile_p->syscall_lseek_count);
+ d_printf("lseek_time: %u\n", profile_p->syscall_lseek_time);
+ d_printf("rename_count: %u\n", profile_p->syscall_rename_count);
+ d_printf("rename_time: %u\n", profile_p->syscall_rename_time);
+ d_printf("fsync_count: %u\n", profile_p->syscall_fsync_count);
+ d_printf("fsync_time: %u\n", profile_p->syscall_fsync_time);
+ d_printf("stat_count: %u\n", profile_p->syscall_stat_count);
+ d_printf("stat_time: %u\n", profile_p->syscall_stat_time);
+ d_printf("fstat_count: %u\n", profile_p->syscall_fstat_count);
+ d_printf("fstat_time: %u\n", profile_p->syscall_fstat_time);
+ d_printf("lstat_count: %u\n", profile_p->syscall_lstat_count);
+ d_printf("lstat_time: %u\n", profile_p->syscall_lstat_time);
+ d_printf("unlink_count: %u\n", profile_p->syscall_unlink_count);
+ d_printf("unlink_time: %u\n", profile_p->syscall_unlink_time);
+ d_printf("chmod_count: %u\n", profile_p->syscall_chmod_count);
+ d_printf("chmod_time: %u\n", profile_p->syscall_chmod_time);
+ d_printf("fchmod_count: %u\n", profile_p->syscall_fchmod_count);
+ d_printf("fchmod_time: %u\n", profile_p->syscall_fchmod_time);
+ d_printf("chown_count: %u\n", profile_p->syscall_chown_count);
+ d_printf("chown_time: %u\n", profile_p->syscall_chown_time);
+ d_printf("fchown_count: %u\n", profile_p->syscall_fchown_count);
+ d_printf("fchown_time: %u\n", profile_p->syscall_fchown_time);
+ d_printf("chdir_count: %u\n", profile_p->syscall_chdir_count);
+ d_printf("chdir_time: %u\n", profile_p->syscall_chdir_time);
+ d_printf("getwd_count: %u\n", profile_p->syscall_getwd_count);
+ d_printf("getwd_time: %u\n", profile_p->syscall_getwd_time);
+ d_printf("ntimes_count: %u\n", profile_p->syscall_ntimes_count);
+ d_printf("ntimes_time: %u\n", profile_p->syscall_ntimes_time);
+ d_printf("ftruncate_count: %u\n", profile_p->syscall_ftruncate_count);
+ d_printf("ftruncate_time: %u\n", profile_p->syscall_ftruncate_time);
+ d_printf("fcntl_lock_count: %u\n", profile_p->syscall_fcntl_lock_count);
+ d_printf("fcntl_lock_time: %u\n", profile_p->syscall_fcntl_lock_time);
+ d_printf("readlink_count: %u\n", profile_p->syscall_readlink_count);
+ d_printf("readlink_time: %u\n", profile_p->syscall_readlink_time);
+ d_printf("symlink_count: %u\n", profile_p->syscall_symlink_count);
+ d_printf("symlink_time: %u\n", profile_p->syscall_symlink_time);
+
+ profile_separator("Stat Cache");
+ d_printf("lookups: %u\n", profile_p->statcache_lookups);
+ d_printf("misses: %u\n", profile_p->statcache_misses);
+ d_printf("hits: %u\n", profile_p->statcache_hits);
+
+ profile_separator("Write Cache");
+ d_printf("read_hits: %u\n", profile_p->writecache_read_hits);
+ d_printf("abutted_writes: %u\n", profile_p->writecache_abutted_writes);
+ d_printf("total_writes: %u\n", profile_p->writecache_total_writes);
+ d_printf("non_oplock_writes: %u\n", profile_p->writecache_non_oplock_writes);
+ d_printf("direct_writes: %u\n", profile_p->writecache_direct_writes);
+ d_printf("init_writes: %u\n", profile_p->writecache_init_writes);
+ d_printf("flushed_writes[SEEK]: %u\n", profile_p->writecache_flushed_writes[SEEK_FLUSH]);
+ d_printf("flushed_writes[READ]: %u\n", profile_p->writecache_flushed_writes[READ_FLUSH]);
+ d_printf("flushed_writes[WRITE]: %u\n", profile_p->writecache_flushed_writes[WRITE_FLUSH]);
+ d_printf("flushed_writes[READRAW]: %u\n", profile_p->writecache_flushed_writes[READRAW_FLUSH]);
+ d_printf("flushed_writes[OPLOCK_RELEASE]: %u\n", profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH]);
+ d_printf("flushed_writes[CLOSE]: %u\n", profile_p->writecache_flushed_writes[CLOSE_FLUSH]);
+ d_printf("flushed_writes[SYNC]: %u\n", profile_p->writecache_flushed_writes[SYNC_FLUSH]);
+ d_printf("flushed_writes[SIZECHANGE]: %u\n", profile_p->writecache_flushed_writes[SIZECHANGE_FLUSH]);
+ d_printf("num_perfect_writes: %u\n", profile_p->writecache_num_perfect_writes);
+ d_printf("num_write_caches: %u\n", profile_p->writecache_num_write_caches);
+ d_printf("allocated_write_caches: %u\n", profile_p->writecache_allocated_write_caches);
+
+ profile_separator("SMB Calls");
+ d_printf("mkdir_count: %u\n", profile_p->SMBmkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->SMBmkdir_time);
+ d_printf("rmdir_count: %u\n", profile_p->SMBrmdir_count);
+ d_printf("rmdir_time: %u\n", profile_p->SMBrmdir_time);
+ d_printf("open_count: %u\n", profile_p->SMBopen_count);
+ d_printf("open_time: %u\n", profile_p->SMBopen_time);
+ d_printf("create_count: %u\n", profile_p->SMBcreate_count);
+ d_printf("create_time: %u\n", profile_p->SMBcreate_time);
+ d_printf("close_count: %u\n", profile_p->SMBclose_count);
+ d_printf("close_time: %u\n", profile_p->SMBclose_time);
+ d_printf("flush_count: %u\n", profile_p->SMBflush_count);
+ d_printf("flush_time: %u\n", profile_p->SMBflush_time);
+ d_printf("unlink_count: %u\n", profile_p->SMBunlink_count);
+ d_printf("unlink_time: %u\n", profile_p->SMBunlink_time);
+ d_printf("mv_count: %u\n", profile_p->SMBmv_count);
+ d_printf("mv_time: %u\n", profile_p->SMBmv_time);
+ d_printf("getatr_count: %u\n", profile_p->SMBgetatr_count);
+ d_printf("getatr_time: %u\n", profile_p->SMBgetatr_time);
+ d_printf("setatr_count: %u\n", profile_p->SMBsetatr_count);
+ d_printf("setatr_time: %u\n", profile_p->SMBsetatr_time);
+ d_printf("read_count: %u\n", profile_p->SMBread_count);
+ d_printf("read_time: %u\n", profile_p->SMBread_time);
+ d_printf("write_count: %u\n", profile_p->SMBwrite_count);
+ d_printf("write_time: %u\n", profile_p->SMBwrite_time);
+ d_printf("lock_count: %u\n", profile_p->SMBlock_count);
+ d_printf("lock_time: %u\n", profile_p->SMBlock_time);
+ d_printf("unlock_count: %u\n", profile_p->SMBunlock_count);
+ d_printf("unlock_time: %u\n", profile_p->SMBunlock_time);
+ d_printf("ctemp_count: %u\n", profile_p->SMBctemp_count);
+ d_printf("ctemp_time: %u\n", profile_p->SMBctemp_time);
+ d_printf("mknew_count: %u\n", profile_p->SMBmknew_count);
+ d_printf("mknew_time: %u\n", profile_p->SMBmknew_time);
+ d_printf("checkpath_count: %u\n", profile_p->SMBcheckpath_count);
+ d_printf("checkpath_time: %u\n", profile_p->SMBcheckpath_time);
+ d_printf("exit_count: %u\n", profile_p->SMBexit_count);
+ d_printf("exit_time: %u\n", profile_p->SMBexit_time);
+ d_printf("lseek_count: %u\n", profile_p->SMBlseek_count);
+ d_printf("lseek_time: %u\n", profile_p->SMBlseek_time);
+ d_printf("lockread_count: %u\n", profile_p->SMBlockread_count);
+ d_printf("lockread_time: %u\n", profile_p->SMBlockread_time);
+ d_printf("writeunlock_count: %u\n", profile_p->SMBwriteunlock_count);
+ d_printf("writeunlock_time: %u\n", profile_p->SMBwriteunlock_time);
+ d_printf("readbraw_count: %u\n", profile_p->SMBreadbraw_count);
+ d_printf("readbraw_time: %u\n", profile_p->SMBreadbraw_time);
+ d_printf("readBmpx_count: %u\n", profile_p->SMBreadBmpx_count);
+ d_printf("readBmpx_time: %u\n", profile_p->SMBreadBmpx_time);
+ d_printf("readBs_count: %u\n", profile_p->SMBreadBs_count);
+ d_printf("readBs_time: %u\n", profile_p->SMBreadBs_time);
+ d_printf("writebraw_count: %u\n", profile_p->SMBwritebraw_count);
+ d_printf("writebraw_time: %u\n", profile_p->SMBwritebraw_time);
+ d_printf("writeBmpx_count: %u\n", profile_p->SMBwriteBmpx_count);
+ d_printf("writeBmpx_time: %u\n", profile_p->SMBwriteBmpx_time);
+ d_printf("writeBs_count: %u\n", profile_p->SMBwriteBs_count);
+ d_printf("writeBs_time: %u\n", profile_p->SMBwriteBs_time);
+ d_printf("writec_count: %u\n", profile_p->SMBwritec_count);
+ d_printf("writec_time: %u\n", profile_p->SMBwritec_time);
+ d_printf("setattrE_count: %u\n", profile_p->SMBsetattrE_count);
+ d_printf("setattrE_time: %u\n", profile_p->SMBsetattrE_time);
+ d_printf("getattrE_count: %u\n", profile_p->SMBgetattrE_count);
+ d_printf("getattrE_time: %u\n", profile_p->SMBgetattrE_time);
+ d_printf("lockingX_count: %u\n", profile_p->SMBlockingX_count);
+ d_printf("lockingX_time: %u\n", profile_p->SMBlockingX_time);
+ d_printf("trans_count: %u\n", profile_p->SMBtrans_count);
+ d_printf("trans_time: %u\n", profile_p->SMBtrans_time);
+ d_printf("transs_count: %u\n", profile_p->SMBtranss_count);
+ d_printf("transs_time: %u\n", profile_p->SMBtranss_time);
+ d_printf("ioctl_count: %u\n", profile_p->SMBioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->SMBioctl_time);
+ d_printf("ioctls_count: %u\n", profile_p->SMBioctls_count);
+ d_printf("ioctls_time: %u\n", profile_p->SMBioctls_time);
+ d_printf("copy_count: %u\n", profile_p->SMBcopy_count);
+ d_printf("copy_time: %u\n", profile_p->SMBcopy_time);
+ d_printf("move_count: %u\n", profile_p->SMBmove_count);
+ d_printf("move_time: %u\n", profile_p->SMBmove_time);
+ d_printf("echo_count: %u\n", profile_p->SMBecho_count);
+ d_printf("echo_time: %u\n", profile_p->SMBecho_time);
+ d_printf("writeclose_count: %u\n", profile_p->SMBwriteclose_count);
+ d_printf("writeclose_time: %u\n", profile_p->SMBwriteclose_time);
+ d_printf("openX_count: %u\n", profile_p->SMBopenX_count);
+ d_printf("openX_time: %u\n", profile_p->SMBopenX_time);
+ d_printf("readX_count: %u\n", profile_p->SMBreadX_count);
+ d_printf("readX_time: %u\n", profile_p->SMBreadX_time);
+ d_printf("writeX_count: %u\n", profile_p->SMBwriteX_count);
+ d_printf("writeX_time: %u\n", profile_p->SMBwriteX_time);
+ d_printf("trans2_count: %u\n", profile_p->SMBtrans2_count);
+ d_printf("trans2_time: %u\n", profile_p->SMBtrans2_time);
+ d_printf("transs2_count: %u\n", profile_p->SMBtranss2_count);
+ d_printf("transs2_time: %u\n", profile_p->SMBtranss2_time);
+ d_printf("findclose_count: %u\n", profile_p->SMBfindclose_count);
+ d_printf("findclose_time: %u\n", profile_p->SMBfindclose_time);
+ d_printf("findnclose_count: %u\n", profile_p->SMBfindnclose_count);
+ d_printf("findnclose_time: %u\n", profile_p->SMBfindnclose_time);
+ d_printf("tcon_count: %u\n", profile_p->SMBtcon_count);
+ d_printf("tcon_time: %u\n", profile_p->SMBtcon_time);
+ d_printf("tdis_count: %u\n", profile_p->SMBtdis_count);
+ d_printf("tdis_time: %u\n", profile_p->SMBtdis_time);
+ d_printf("negprot_count: %u\n", profile_p->SMBnegprot_count);
+ d_printf("negprot_time: %u\n", profile_p->SMBnegprot_time);
+ d_printf("sesssetupX_count: %u\n", profile_p->SMBsesssetupX_count);
+ d_printf("sesssetupX_time: %u\n", profile_p->SMBsesssetupX_time);
+ d_printf("ulogoffX_count: %u\n", profile_p->SMBulogoffX_count);
+ d_printf("ulogoffX_time: %u\n", profile_p->SMBulogoffX_time);
+ d_printf("tconX_count: %u\n", profile_p->SMBtconX_count);
+ d_printf("tconX_time: %u\n", profile_p->SMBtconX_time);
+ d_printf("dskattr_count: %u\n", profile_p->SMBdskattr_count);
+ d_printf("dskattr_time: %u\n", profile_p->SMBdskattr_time);
+ d_printf("search_count: %u\n", profile_p->SMBsearch_count);
+ d_printf("search_time: %u\n", profile_p->SMBsearch_time);
+ d_printf("ffirst_count: %u\n", profile_p->SMBffirst_count);
+ d_printf("ffirst_time: %u\n", profile_p->SMBffirst_time);
+ d_printf("funique_count: %u\n", profile_p->SMBfunique_count);
+ d_printf("funique_time: %u\n", profile_p->SMBfunique_time);
+ d_printf("fclose_count: %u\n", profile_p->SMBfclose_count);
+ d_printf("fclose_time: %u\n", profile_p->SMBfclose_time);
+ d_printf("nttrans_count: %u\n", profile_p->SMBnttrans_count);
+ d_printf("nttrans_time: %u\n", profile_p->SMBnttrans_time);
+ d_printf("nttranss_count: %u\n", profile_p->SMBnttranss_count);
+ d_printf("nttranss_time: %u\n", profile_p->SMBnttranss_time);
+ d_printf("ntcreateX_count: %u\n", profile_p->SMBntcreateX_count);
+ d_printf("ntcreateX_time: %u\n", profile_p->SMBntcreateX_time);
+ d_printf("ntcancel_count: %u\n", profile_p->SMBntcancel_count);
+ d_printf("ntcancel_time: %u\n", profile_p->SMBntcancel_time);
+ d_printf("splopen_count: %u\n", profile_p->SMBsplopen_count);
+ d_printf("splopen_time: %u\n", profile_p->SMBsplopen_time);
+ d_printf("splwr_count: %u\n", profile_p->SMBsplwr_count);
+ d_printf("splwr_time: %u\n", profile_p->SMBsplwr_time);
+ d_printf("splclose_count: %u\n", profile_p->SMBsplclose_count);
+ d_printf("splclose_time: %u\n", profile_p->SMBsplclose_time);
+ d_printf("splretq_count: %u\n", profile_p->SMBsplretq_count);
+ d_printf("splretq_time: %u\n", profile_p->SMBsplretq_time);
+ d_printf("sends_count: %u\n", profile_p->SMBsends_count);
+ d_printf("sends_time: %u\n", profile_p->SMBsends_time);
+ d_printf("sendb_count: %u\n", profile_p->SMBsendb_count);
+ d_printf("sendb_time: %u\n", profile_p->SMBsendb_time);
+ d_printf("fwdname_count: %u\n", profile_p->SMBfwdname_count);
+ d_printf("fwdname_time: %u\n", profile_p->SMBfwdname_time);
+ d_printf("cancelf_count: %u\n", profile_p->SMBcancelf_count);
+ d_printf("cancelf_time: %u\n", profile_p->SMBcancelf_time);
+ d_printf("getmac_count: %u\n", profile_p->SMBgetmac_count);
+ d_printf("getmac_time: %u\n", profile_p->SMBgetmac_time);
+ d_printf("sendstrt_count: %u\n", profile_p->SMBsendstrt_count);
+ d_printf("sendstrt_time: %u\n", profile_p->SMBsendstrt_time);
+ d_printf("sendend_count: %u\n", profile_p->SMBsendend_count);
+ d_printf("sendend_time: %u\n", profile_p->SMBsendend_time);
+ d_printf("sendtxt_count: %u\n", profile_p->SMBsendtxt_count);
+ d_printf("sendtxt_time: %u\n", profile_p->SMBsendtxt_time);
+ d_printf("invalid_count: %u\n", profile_p->SMBinvalid_count);
+ d_printf("invalid_time: %u\n", profile_p->SMBinvalid_time);
+
+ profile_separator("Pathworks Calls");
+ d_printf("setdir_count: %u\n", profile_p->pathworks_setdir_count);
+ d_printf("setdir_time: %u\n", profile_p->pathworks_setdir_time);
+
+ profile_separator("Trans2 Calls");
+ d_printf("open_count: %u\n", profile_p->Trans2_open_count);
+ d_printf("open_time: %u\n", profile_p->Trans2_open_time);
+ d_printf("findfirst_count: %u\n", profile_p->Trans2_findfirst_count);
+ d_printf("findfirst_time: %u\n", profile_p->Trans2_findfirst_time);
+ d_printf("findnext_count: %u\n", profile_p->Trans2_findnext_count);
+ d_printf("findnext_time: %u\n", profile_p->Trans2_findnext_time);
+ d_printf("qfsinfo_count: %u\n", profile_p->Trans2_qfsinfo_count);
+ d_printf("qfsinfo_time: %u\n", profile_p->Trans2_qfsinfo_time);
+ d_printf("setfsinfo_count: %u\n", profile_p->Trans2_setfsinfo_count);
+ d_printf("setfsinfo_time: %u\n", profile_p->Trans2_setfsinfo_time);
+ d_printf("qpathinfo_count: %u\n", profile_p->Trans2_qpathinfo_count);
+ d_printf("qpathinfo_time: %u\n", profile_p->Trans2_qpathinfo_time);
+ d_printf("setpathinfo_count: %u\n", profile_p->Trans2_setpathinfo_count);
+ d_printf("setpathinfo_time: %u\n", profile_p->Trans2_setpathinfo_time);
+ d_printf("qfileinfo_count: %u\n", profile_p->Trans2_qfileinfo_count);
+ d_printf("qfileinfo_time: %u\n", profile_p->Trans2_qfileinfo_time);
+ d_printf("setfileinfo_count: %u\n", profile_p->Trans2_setfileinfo_count);
+ d_printf("setfileinfo_time: %u\n", profile_p->Trans2_setfileinfo_time);
+ d_printf("fsctl_count: %u\n", profile_p->Trans2_fsctl_count);
+ d_printf("fsctl_time: %u\n", profile_p->Trans2_fsctl_time);
+ d_printf("ioctl_count: %u\n", profile_p->Trans2_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->Trans2_ioctl_time);
+ d_printf("findnotifyfirst_count: %u\n", profile_p->Trans2_findnotifyfirst_count);
+ d_printf("findnotifyfirst_time: %u\n", profile_p->Trans2_findnotifyfirst_time);
+ d_printf("findnotifynext_count: %u\n", profile_p->Trans2_findnotifynext_count);
+ d_printf("findnotifynext_time: %u\n", profile_p->Trans2_findnotifynext_time);
+ d_printf("mkdir_count: %u\n", profile_p->Trans2_mkdir_count);
+ d_printf("mkdir_time: %u\n", profile_p->Trans2_mkdir_time);
+ d_printf("session_setup_count: %u\n", profile_p->Trans2_session_setup_count);
+ d_printf("session_setup_time: %u\n", profile_p->Trans2_session_setup_time);
+ d_printf("get_dfs_referral_count: %u\n", profile_p->Trans2_get_dfs_referral_count);
+ d_printf("get_dfs_referral_time: %u\n", profile_p->Trans2_get_dfs_referral_time);
+ d_printf("report_dfs_inconsistancy_count: %u\n", profile_p->Trans2_report_dfs_inconsistancy_count);
+ d_printf("report_dfs_inconsistancy_time: %u\n", profile_p->Trans2_report_dfs_inconsistancy_time);
+
+ profile_separator("NT Transact Calls");
+ d_printf("create_count: %u\n", profile_p->NT_transact_create_count);
+ d_printf("create_time: %u\n", profile_p->NT_transact_create_time);
+ d_printf("ioctl_count: %u\n", profile_p->NT_transact_ioctl_count);
+ d_printf("ioctl_time: %u\n", profile_p->NT_transact_ioctl_time);
+ d_printf("set_security_desc_count: %u\n", profile_p->NT_transact_set_security_desc_count);
+ d_printf("set_security_desc_time: %u\n", profile_p->NT_transact_set_security_desc_time);
+ d_printf("notify_change_count: %u\n", profile_p->NT_transact_notify_change_count);
+ d_printf("notify_change_time: %u\n", profile_p->NT_transact_notify_change_time);
+ d_printf("rename_count: %u\n", profile_p->NT_transact_rename_count);
+ d_printf("rename_time: %u\n", profile_p->NT_transact_rename_time);
+ d_printf("query_security_desc_count: %u\n", profile_p->NT_transact_query_security_desc_count);
+ d_printf("query_security_desc_time: %u\n", profile_p->NT_transact_query_security_desc_time);
+
+ profile_separator("ACL Calls");
+ d_printf("get_nt_acl_count: %u\n", profile_p->get_nt_acl_count);
+ d_printf("get_nt_acl_time: %u\n", profile_p->get_nt_acl_time);
+ d_printf("fget_nt_acl_count: %u\n", profile_p->fget_nt_acl_count);
+ d_printf("fget_nt_acl_time: %u\n", profile_p->fget_nt_acl_time);
+ d_printf("fset_nt_acl_count: %u\n", profile_p->fset_nt_acl_count);
+ d_printf("fset_nt_acl_time: %u\n", profile_p->fset_nt_acl_time);
+ d_printf("chmod_acl_count: %u\n", profile_p->chmod_acl_count);
+ d_printf("chmod_acl_time: %u\n", profile_p->chmod_acl_time);
+ d_printf("fchmod_acl_count: %u\n", profile_p->fchmod_acl_count);
+ d_printf("fchmod_acl_time: %u\n", profile_p->fchmod_acl_time);
+
+ profile_separator("NMBD Calls");
+ d_printf("name_release_count: %u\n", profile_p->name_release_count);
+ d_printf("name_release_time: %u\n", profile_p->name_release_time);
+ d_printf("name_refresh_count: %u\n", profile_p->name_refresh_count);
+ d_printf("name_refresh_time: %u\n", profile_p->name_refresh_time);
+ d_printf("name_registration_count: %u\n", profile_p->name_registration_count);
+ d_printf("name_registration_time: %u\n", profile_p->name_registration_time);
+ d_printf("node_status_count: %u\n", profile_p->node_status_count);
+ d_printf("node_status_time: %u\n", profile_p->node_status_time);
+ d_printf("name_query_count: %u\n", profile_p->name_query_count);
+ d_printf("name_query_time: %u\n", profile_p->name_query_time);
+ d_printf("host_announce_count: %u\n", profile_p->host_announce_count);
+ d_printf("host_announce_time: %u\n", profile_p->host_announce_time);
+ d_printf("workgroup_announce_count: %u\n", profile_p->workgroup_announce_count);
+ d_printf("workgroup_announce_time: %u\n", profile_p->workgroup_announce_time);
+ d_printf("local_master_announce_count: %u\n", profile_p->local_master_announce_count);
+ d_printf("local_master_announce_time: %u\n", profile_p->local_master_announce_time);
+ d_printf("master_browser_announce_count: %u\n", profile_p->master_browser_announce_count);
+ d_printf("master_browser_announce_time: %u\n", profile_p->master_browser_announce_time);
+ d_printf("lm_host_announce_count: %u\n", profile_p->lm_host_announce_count);
+ d_printf("lm_host_announce_time: %u\n", profile_p->lm_host_announce_time);
+ d_printf("get_backup_list_count: %u\n", profile_p->get_backup_list_count);
+ d_printf("get_backup_list_time: %u\n", profile_p->get_backup_list_time);
+ d_printf("reset_browser_count: %u\n", profile_p->reset_browser_count);
+ d_printf("reset_browser_time: %u\n", profile_p->reset_browser_time);
+ d_printf("announce_request_count: %u\n", profile_p->announce_request_count);
+ d_printf("announce_request_time: %u\n", profile_p->announce_request_time);
+ d_printf("lm_announce_request_count: %u\n", profile_p->lm_announce_request_count);
+ d_printf("lm_announce_request_time: %u\n", profile_p->lm_announce_request_time);
+ d_printf("domain_logon_count: %u\n", profile_p->domain_logon_count);
+ d_printf("domain_logon_time: %u\n", profile_p->domain_logon_time);
+ d_printf("sync_browse_lists_count: %u\n", profile_p->sync_browse_lists_count);
+ d_printf("sync_browse_lists_time: %u\n", profile_p->sync_browse_lists_time);
+ d_printf("run_elections_count: %u\n", profile_p->run_elections_count);
+ d_printf("run_elections_time: %u\n", profile_p->run_elections_time);
+ d_printf("election_count: %u\n", profile_p->election_count);
+ d_printf("election_time: %u\n", profile_p->election_time);
+#else /* WITH_PROFILE */
+ fprintf(stderr, "Profile data unavailable\n");
+#endif /* WITH_PROFILE */
+
+ return True;
+}
+
+#ifdef WITH_PROFILE
+
+/* 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 int print_count_samples(
+ const struct profile_stats * const current,
+ const struct profile_stats * const last,
+ SMB_BIG_UINT delta_usec)
+{
+ int i;
+ int count = 0;
+ unsigned step;
+ SMB_BIG_UINT spent;
+ int delta_sec;
+ const char * name;
+ char buf[40];
+
+ if (delta_usec == 0) {
+ return 0;
+ }
+
+ buf[0] = '\0';
+ delta_sec = usec_to_sec(delta_usec);
+
+ for (i = 0; i < PR_VALUE_MAX; ++i) {
+ step = current->count[i] - last->count[i];
+ spent = current->time[i] - last->time[i];
+
+ if (step) {
+ ++count;
+
+ name = profile_value_name(i);
+
+ if (buf[0] == '\0') {
+ snprintf(buf, sizeof(buf),
+ "%s %d/sec (%.2f%%)",
+ name, step / delta_sec,
+ percent_time(spent, delta_usec));
+ } else {
+ printf("%-40s %s %d/sec (%.2f%%)\n",
+ buf, name, step / delta_sec,
+ percent_time(spent, delta_usec));
+ buf[0] = '\0';
+ }
+ }
+ }
+
+ return count;
+}
+
+static struct profile_stats sample_data[2];
+static SMB_BIG_UINT sample_time[2];
+
+bool status_profile_rates(bool verbose)
+{
+ SMB_BIG_UINT remain_usec;
+ SMB_BIG_UINT next_usec;
+ SMB_BIG_UINT 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;
+ }
+
+ memcpy(&sample_data[last], profile_p, sizeof(*profile_p));
+ for (;;) {
+ sample_time[current] = profile_timestamp();
+ next_usec = sample_time[current] + sample_interval_usec;
+
+ /* Take a sample. */
+ memcpy(&sample_data[current], profile_p, sizeof(*profile_p));
+
+ /* 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));
+ }
+
+ sys_usleep(remain_usec);
+ }
+
+ }
+
+ return True;
+}
+
+#else /* WITH_PROFILE */
+
+bool status_profile_rates(bool verbose)
+{
+ fprintf(stderr, "Profile data unavailable\n");
+ return False;
+}
+
+#endif /* WITH_PROFILE */
+
diff --git a/source3/utils/testparm.c b/source3/utils/testparm.c
new file mode 100644
index 0000000000..527db2d805
--- /dev/null
+++ b/source3/utils/testparm.c
@@ -0,0 +1,434 @@
+/*
+ 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"
+
+extern bool AllowDebugChange;
+
+/***********************************************
+ 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;
+
+ if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) {
+ fprintf(stderr, "ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n");
+ ret = 1;
+ }
+
+ if (lp_wins_support() && 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");
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_lockdir(), &st)) {
+ fprintf(stderr, "ERROR: lock directory %s does not exist\n",
+ lp_lockdir());
+ ret = 1;
+ } else if ((st.st_mode & 0777) != 0755) {
+ fprintf(stderr, "WARNING: lock directory %s should have permissions 0755 for browsing to work\n",
+ lp_lockdir());
+ ret = 1;
+ }
+
+ if (!directory_exist(lp_piddir(), &st)) {
+ fprintf(stderr, "ERROR: pid directory %s does not exist\n",
+ lp_piddir());
+ ret = 1;
+ }
+
+ if (lp_passdb_expand_explicit()) {
+ fprintf(stderr, "WARNING: passdb expand explicit = yes is "
+ "deprecated\n");
+ }
+
+ /*
+ * Password server sanity checks.
+ */
+
+ if((lp_security() == SEC_SERVER || lp_security() >= SEC_DOMAIN) && !lp_passwordserver()) {
+ const char *sec_setting;
+ if(lp_security() == SEC_SERVER)
+ sec_setting = "server";
+ else if(lp_security() == SEC_DOMAIN)
+ sec_setting = "domain";
+ else
+ sec_setting = "";
+
+ fprintf(stderr, "ERROR: The setting 'security=%s' requires the 'password server' parameter be set \
+to a valid password server.\n", sec_setting );
+ ret = 1;
+ }
+
+ /*
+ * 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() == NULL) ||
+ (strlen(lp_passwd_program()) == 0))
+ {
+ fprintf( stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \
+parameter.\n" );
+ ret = 1;
+ } else {
+ const char *passwd_prog;
+ char *truncated_prog = NULL;
+ const char *p;
+
+ passwd_prog = lp_passwd_program();
+ 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", truncated_prog, strerror(errno) );
+ ret = 1;
+ }
+ }
+
+#ifdef WITH_PAM
+ }
+#endif
+
+ if(lp_passwd_chat() == NULL) {
+ fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \
+parameter.\n");
+ ret = 1;
+ }
+
+ if ((lp_passwd_program() != NULL) &&
+ (strlen(lp_passwd_program()) > 0))
+ {
+ /* check if there's a %u parameter present */
+ if(strstr_m(lp_passwd_program(), "%u") == NULL) {
+ fprintf(stderr, "ERROR: the 'passwd program' (%s) requires a '%%u' parameter.\n", lp_passwd_program());
+ ret = 1;
+ }
+ }
+
+ /*
+ * Check that we have a valid script and that it hasn't
+ * been written to expect the old password.
+ */
+
+ if(lp_encrypted_passwords()) {
+ if(strstr_m( lp_passwd_chat(), "%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", lp_passwd_chat() );
+ ret = 1;
+ }
+ }
+ }
+
+ if (strlen(lp_winbind_separator()) != 1) {
+ fprintf(stderr,"ERROR: the 'winbind separator' parameter must be a single character.\n");
+ ret = 1;
+ }
+
+ if (*lp_winbind_separator() == '+') {
+ fprintf(stderr,"'winbind separator = +' might cause problems with group membership.\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", BASE_RID);
+ }
+
+ if (lp_algorithmic_rid_base() & 1) {
+ fprintf(stderr,"'algorithmic rid base' must be even.\n");
+ }
+
+#ifndef HAVE_DLOPEN
+ if (lp_preload_modules()) {
+ fprintf(stderr,"WARNING: 'preload modules = ' set while loading plugins not supported.\n");
+ }
+#endif
+
+ if (!lp_passdb_backend()) {
+ fprintf(stderr,"ERROR: passdb backend must have a value or be left out\n");
+ }
+
+ if (lp_os_level() > 255) {
+ fprintf(stderr,"WARNING: Maximum value for 'os level' is 255!\n");
+ }
+
+ return ret;
+}
+
+/**
+ * per-share logic tests
+ */
+static void do_per_share_checks(int s)
+{
+ const char **deny_list = lp_hostsdeny(s);
+ const char **allow_list = lp_hostsallow(s);
+ int i;
+
+ 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",
+ hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(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",
+ hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(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",
+ lp_servicename(s) );
+ }
+
+ if (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",
+ lp_servicename(s) );
+ }
+ if (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",
+ lp_servicename(s) );
+ }
+ if (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",
+ lp_servicename(s) );
+ }
+ if (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",
+ lp_servicename(s) );
+ }
+#ifdef HAVE_CUPS
+ if (lp_printing(s) == PRINT_CUPS && *(lp_printcommand(s)) != '\0') {
+ fprintf(stderr,"Warning: Service %s defines a print command, but \
+rameter is ignored when using CUPS libraries.\n",
+ lp_servicename(s) );
+ }
+#endif
+}
+
+ int main(int argc, const char *argv[])
+{
+ const char *config_file = get_dyn_CONFIGFILE();
+ int s;
+ static int silent_mode = False;
+ static int show_all_parameters = False;
+ int ret = 0;
+ poptContext pc;
+ static const char *term_code = "";
+ static char *parameter_name = NULL;
+ static const char *section_name = NULL;
+ static char *new_local_machine = NULL;
+ const char *cname;
+ const char *caddr;
+ static int show_defaults;
+ static int skip_logic_checks = 0;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"},
+ {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"},
+ {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"},
+ {"encoding", 't', POPT_ARG_STRING, &term_code, 0, "Print parameters with encoding"},
+ {"skip-logic-checks", 'l', POPT_ARG_NONE, &skip_logic_checks, 1, "Skip the global checks"},
+ {"show-all-parameters", '\0', POPT_ARG_VAL, &show_all_parameters, True, "Show the parameters, type, possible values" },
+ {"parameter-name", '\0', POPT_ARG_STRING, &parameter_name, 0, "Limit testparm to a named parameter" },
+ {"section-name", '\0', POPT_ARG_STRING, &section_name, 0, "Limit testparm to a named section" },
+ POPT_COMMON_VERSION
+ POPT_COMMON_DEBUGLEVEL
+ POPT_TABLEEND
+ };
+
+ TALLOC_CTX *frame = talloc_stackframe();
+
+ load_case_tables();
+ /*
+ * Set the default debug level to 2.
+ * Allow it to be overridden by the command line,
+ * not by smb.conf.
+ */
+ DEBUGLEVEL_CLASS[DBGC_ALL] = 2;
+
+ pc = poptGetContext(NULL, argc, argv, long_options,
+ POPT_CONTEXT_KEEP_FIRST);
+ poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]");
+
+ while(poptGetNextOpt(pc) != -1);
+
+ if (show_all_parameters) {
+ show_parameter_list();
+ exit(0);
+ }
+
+ setup_logging(poptGetArg(pc), True);
+
+ if (poptPeekArg(pc))
+ config_file = poptGetArg(pc);
+
+ cname = poptGetArg(pc);
+ caddr = poptGetArg(pc);
+
+ if ( cname && ! caddr ) {
+ printf ( "ERROR: You must specify both a machine name and an IP address.\n" );
+ return(1);
+ }
+
+ if (new_local_machine) {
+ set_local_machine_name(new_local_machine, True);
+ }
+
+ dbf = x_stderr;
+ /* Don't let the debuglevel be changed by smb.conf. */
+ AllowDebugChange = False;
+
+ fprintf(stderr,"Load smb config files from %s\n",config_file);
+
+ if (!lp_load_with_registry_shares(config_file,False,True,False,True)) {
+ fprintf(stderr,"Error loading services.\n");
+ return(1);
+ }
+
+ fprintf(stderr,"Loaded services file OK.\n");
+
+ if (skip_logic_checks == 0) {
+ ret = do_global_checks();
+ }
+
+ for (s=0;s<1000;s++) {
+ if (VALID_SNUM(s))
+ if (strlen(lp_servicename(s)) > 12) {
+ fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" );
+ fprintf(stderr, "These may not be accessible to some older clients.\n" );
+ fprintf(stderr, "(Eg. Windows9x, WindowsMe, and smbclient prior to Samba 3.0.)\n" );
+ break;
+ }
+ }
+
+ 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", 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);
+ return(1);
+ }
+ 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);
+ return(1);
+ }
+ } else {
+ if (isGlobal == True)
+ lp_dump(stdout, show_defaults, 0);
+ else
+ lp_dump_one(stdout, show_defaults, s);
+ }
+ return(ret);
+ }
+
+ 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_hostsdeny(-1), lp_hostsallow(-1), cname, caddr)
+ && allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) {
+ fprintf(stderr,"Allow connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ } else {
+ fprintf(stderr,"Deny connection from %s (%s) to %s\n",
+ cname,caddr,lp_servicename(s));
+ }
+ }
+ }
+ }
+ TALLOC_FREE(frame);
+ return(ret);
+}
+