diff options
Diffstat (limited to 'source3/utils')
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( "<" ); + break; + case '>': + (void)printf( ">" ); + break; + case '&': + (void)printf( "&" ); + break; + case '\"': + (void)printf( """ ); + 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, ¶meters, + 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(®path, "%s\\%s", parent, key->keyname) < 0) { + break; + } + d_printf("[%s]\n", regpath ); + dump_values( key ); + d_printf("\n"); + dump_registry_tree( file, key, regpath ); + SAFE_FREE(regpath); + } + + return true; +} + +/******************************************************************** +********************************************************************/ + +static bool write_registry_tree( REGF_FILE *infile, REGF_NK_REC *nk, + REGF_NK_REC *parent, REGF_FILE *outfile, + const char *parentpath ) +{ + REGF_NK_REC *key, *subkey; + 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, "a_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, ¶meter_name, 0, "Limit testparm to a named parameter" }, + {"section-name", '\0', POPT_ARG_STRING, §ion_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); +} + |