From ef2e26c91b80556af033d3335e55f5dfa6fff31d Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 13 Aug 2003 01:53:07 +0000 Subject: first public release of samba4 code (This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f) --- source4/utils/.cvsignore | 1 + source4/utils/debug2html.c | 253 +++++ source4/utils/editreg.c | 2069 +++++++++++++++++++++++++++++++++++ source4/utils/net.c | 642 +++++++++++ source4/utils/net.h | 61 ++ source4/utils/net_ads.c | 1176 ++++++++++++++++++++ source4/utils/net_ads_cldap.c | 354 ++++++ source4/utils/net_cache.c | 348 ++++++ source4/utils/net_help.c | 199 ++++ source4/utils/net_lookup.c | 234 ++++ source4/utils/net_rap.c | 1051 ++++++++++++++++++ source4/utils/net_rpc.c | 2262 +++++++++++++++++++++++++++++++++++++++ source4/utils/net_rpc_join.c | 354 ++++++ source4/utils/net_rpc_samsync.c | 725 +++++++++++++ source4/utils/net_time.c | 180 ++++ source4/utils/nmblookup.c | 338 ++++++ source4/utils/ntlm_auth.c | 551 ++++++++++ source4/utils/pdbedit.c | 696 ++++++++++++ source4/utils/profiles.c | 729 +++++++++++++ source4/utils/rewrite.c | 32 + source4/utils/rpccheck.c | 62 ++ source4/utils/smbcacls.c | 937 ++++++++++++++++ source4/utils/smbcontrol.c | 714 ++++++++++++ source4/utils/smbfilter.c | 245 +++++ source4/utils/smbgroupedit.c | 410 +++++++ source4/utils/smbpasswd.c | 605 +++++++++++ source4/utils/smbtree.c | 369 +++++++ source4/utils/smbw_sample.c | 94 ++ source4/utils/status.c | 665 ++++++++++++ source4/utils/tdb/Makefile | 29 + source4/utils/tdb/README | 167 +++ source4/utils/tdb/tdb.magic | 10 + source4/utils/tdb/tdbbackup.c | 315 ++++++ source4/utils/tdb/tdbdump.c | 89 ++ source4/utils/tdb/tdbtest.c | 263 +++++ source4/utils/tdb/tdbtool.c | 482 +++++++++ source4/utils/tdb/tdbtorture.c | 226 ++++ source4/utils/testparm.c | 338 ++++++ source4/utils/testprns.c | 61 ++ 39 files changed, 18336 insertions(+) create mode 100644 source4/utils/.cvsignore create mode 100644 source4/utils/debug2html.c create mode 100644 source4/utils/editreg.c create mode 100644 source4/utils/net.c create mode 100644 source4/utils/net.h create mode 100644 source4/utils/net_ads.c create mode 100644 source4/utils/net_ads_cldap.c create mode 100644 source4/utils/net_cache.c create mode 100644 source4/utils/net_help.c create mode 100644 source4/utils/net_lookup.c create mode 100644 source4/utils/net_rap.c create mode 100644 source4/utils/net_rpc.c create mode 100644 source4/utils/net_rpc_join.c create mode 100644 source4/utils/net_rpc_samsync.c create mode 100644 source4/utils/net_time.c create mode 100644 source4/utils/nmblookup.c create mode 100644 source4/utils/ntlm_auth.c create mode 100644 source4/utils/pdbedit.c create mode 100644 source4/utils/profiles.c create mode 100644 source4/utils/rewrite.c create mode 100644 source4/utils/rpccheck.c create mode 100644 source4/utils/smbcacls.c create mode 100644 source4/utils/smbcontrol.c create mode 100644 source4/utils/smbfilter.c create mode 100644 source4/utils/smbgroupedit.c create mode 100644 source4/utils/smbpasswd.c create mode 100644 source4/utils/smbtree.c create mode 100644 source4/utils/smbw_sample.c create mode 100644 source4/utils/status.c create mode 100644 source4/utils/tdb/Makefile create mode 100644 source4/utils/tdb/README create mode 100644 source4/utils/tdb/tdb.magic create mode 100644 source4/utils/tdb/tdbbackup.c create mode 100644 source4/utils/tdb/tdbdump.c create mode 100644 source4/utils/tdb/tdbtest.c create mode 100644 source4/utils/tdb/tdbtool.c create mode 100644 source4/utils/tdb/tdbtorture.c create mode 100644 source4/utils/testparm.c create mode 100644 source4/utils/testprns.c (limited to 'source4/utils') diff --git a/source4/utils/.cvsignore b/source4/utils/.cvsignore new file mode 100644 index 0000000000..6b8749f64e --- /dev/null +++ b/source4/utils/.cvsignore @@ -0,0 +1 @@ +net_proto.h \ No newline at end of file diff --git a/source4/utils/debug2html.c b/source4/utils/debug2html.c new file mode 100644 index 0000000000..f9a1f43f46 --- /dev/null +++ b/source4/utils/debug2html.c @@ -0,0 +1,253 @@ +/* ========================================================================== ** + * 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 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- ** + * 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 new, 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
 block.
+   *
+   * ------------------------------------------------------------------------ **
+   */
+  {
+  switch( new )
+    {
+    case dbg_null:
+    case dbg_ignore:
+      return( mode );
+    case dbg_message:
+      if( dbg_message != mode )
+        {
+        /* Switching to message mode. */
+        (void)printf( "
\n" );
+        return( dbg_message );
+        }
+      break;
+    default:
+      if( dbg_message == mode )
+        {
+        /* Switching out of message mode. */
+        (void)printf( "
\n\n" ); + return( dbg_null ); + } + } + + return( mode ); + } /* modechange */ + +static void newblock( dbg_Token old, dbg_Token new ) + /* ------------------------------------------------------------------------ ** + * 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( "," ); + break; + case dbg_level: + (void)printf( "]\n " ); + break; + case dbg_sourcefile: + (void)printf( ":" ); + break; + case dbg_lineno: + (void)printf( ")" ); + break; + } + + switch( new ) + { + case dbg_timestamp: + (void)printf( "[" ); + break; + case dbg_level: + (void)printf( " " ); + break; + case dbg_lineno: + (void)printf( "(" ); + 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, + new = dbg_null, + state = dbg_null, + mode = dbg_null; + + (void)printf( "\n" ); + (void)printf( "\n\n" ); + (void)printf( " Samba Debug Output\n\n\n\n" ); + + while( (!feof( stdin )) + && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) ) + { + for( i = 0; i < len; i++ ) + { + old = new; + new = dbg_char2token( &state, bufr[i] ); + if( new != old ) + { + mode = modechange( new, mode ); + newblock( old, new ); + } + charprint( new, bufr[i] ); + } + } + (void)modechange( dbg_eof, mode ); + + (void)printf( "\n\n" ); + return( 0 ); + } /* main */ diff --git a/source4/utils/editreg.c b/source4/utils/editreg.c new file mode 100644 index 0000000000..2cf8e2c9df --- /dev/null +++ b/source4/utils/editreg.c @@ -0,0 +1,2069 @@ +/* + Samba Unix/Linux SMB client utility editreg.c + Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/************************************************************************* + + A utility to edit a Windows NT/2K etc registry file. + + Many of the ideas in here come from other people and software. + I first looked in Wine in misc/registry.c and was also influenced by + http://www.wednesday.demon.co.uk/dosreg.html + + Which seems to contain comments from someone else. I reproduce them here + incase the site above disappears. It actually comes from + http://home.eunet.no/~pnordahl/ntpasswd/WinReg.txt. + + The goal here is to read the registry into memory, manipulate it, and then + write it out if it was changed by any actions of the user. + +The windows NT registry has 2 different blocks, where one can occur many +times... + +the "regf"-Block +================ + +"regf" is obviosly the abbreviation for "Registry file". "regf" is the +signature of the header-block which is always 4kb in size, although only +the first 64 bytes seem to be used and a checksum is calculated over +the first 0x200 bytes only! + +Offset Size Contents +0x00000000 D-Word ID: ASCII-"regf" = 0x66676572 +0x00000004 D-Word ???? //see struct REGF +0x00000008 D-Word ???? Always the same value as at 0x00000004 +0x0000000C Q-Word last modify date in WinNT date-format +0x00000014 D-Word 1 +0x00000018 D-Word 3 +0x0000001C D-Word 0 +0x00000020 D-Word 1 +0x00000024 D-Word Offset of 1st key record +0x00000028 D-Word Size of the data-blocks (Filesize-4kb) +0x0000002C D-Word 1 +0x000001FC D-Word Sum of all D-Words from 0x00000000 to +0x000001FB //XOR of all words. Nigel + +I have analyzed more registry files (from multiple machines running +NT 4.0 german version) and could not find an explanation for the values +marked with ???? the rest of the first 4kb page is not important... + +the "hbin"-Block +================ +I don't know what "hbin" stands for, but this block is always a multiple +of 4kb in size. + +Inside these hbin-blocks the different records are placed. The memory- +management looks like a C-compiler heap management to me... + +hbin-Header +=========== +Offset Size Contents +0x0000 D-Word ID: ASCII-"hbin" = 0x6E696268 +0x0004 D-Word Offset from the 1st hbin-Block +0x0008 D-Word Offset to the next hbin-Block +0x001C D-Word Block-size + +The values in 0x0008 and 0x001C should be the same, so I don't know +if they are correct or swapped... + +From offset 0x0020 inside a hbin-block data is stored with the following +format: + +Offset Size Contents +0x0000 D-Word Data-block size //this size must be a +multiple of 8. Nigel +0x0004 ???? Data + +If the size field is negative (bit 31 set), the corresponding block +is free and has a size of -blocksize! + +That does not seem to be true. All block lengths seem to be negative! (Richard Sharpe) + +The data is stored as one record per block. Block size is a multiple +of 4 and the last block reaches the next hbin-block, leaving no room. + +Records in the hbin-blocks +========================== + +nk-Record + + The nk-record can be treated as a kombination of tree-record and + key-record of the win 95 registry. + +lf-Record + + The lf-record is the counterpart to the RGKN-record (the + hash-function) + +vk-Record + + The vk-record consists information to a single value. + +sk-Record + + sk (? Security Key ?) is the ACL of the registry. + +Value-Lists + + The value-lists contain information about which values are inside a + sub-key and don't have a header. + +Datas + + The datas of the registry are (like the value-list) stored without a + header. + +All offset-values are relative to the first hbin-block and point to the +block-size field of the record-entry. to get the file offset, you have to add +the header size (4kb) and the size field (4 bytes)... + +the nk-Record +============= +Offset Size Contents +0x0000 Word ID: ASCII-"nk" = 0x6B6E +0x0002 Word for the root-key: 0x2C, otherwise 0x20 //key symbolic links 0x10. Nigel +0x0004 Q-Word write-date/time in windows nt notation +0x0010 D-Word Offset of Owner/Parent key +0x0014 D-Word number of sub-Keys +0x001C D-Word Offset of the sub-key lf-Records +0x0024 D-Word number of values +0x0028 D-Word Offset of the Value-List +0x002C D-Word Offset of the sk-Record + +0x0030 D-Word Offset of the Class-Name //see NK structure for the use of these fields. Nigel +0x0044 D-Word Unused (data-trash) //some kind of run time index. Does not appear to be important. Nigel +0x0048 Word name-length +0x004A Word class-name length +0x004C ???? key-name + +the Value-List +============== +Offset Size Contents +0x0000 D-Word Offset 1st Value +0x0004 D-Word Offset 2nd Value +0x???? D-Word Offset nth Value + +To determine the number of values, you have to look at the owner-nk-record! + +Der vk-Record +============= +Offset Size Contents +0x0000 Word ID: ASCII-"vk" = 0x6B76 +0x0002 Word name length +0x0004 D-Word length of the data //if top bit is set when offset contains data. Nigel +0x0008 D-Word Offset of Data +0x000C D-Word Type of value +0x0010 Word Flag +0x0012 Word Unused (data-trash) +0x0014 ???? Name + +If bit 0 of the flag-word is set, a name is present, otherwise the value has no name (=default) + +If the data-size is lower 5, the data-offset value is used to store the data itself! + +The data-types +============== +Wert Beteutung +0x0001 RegSZ: character string (in UNICODE!) +0x0002 ExpandSZ: string with "%var%" expanding (UNICODE!) +0x0003 RegBin: raw-binary value +0x0004 RegDWord: Dword +0x0007 RegMultiSZ: multiple strings, seperated with 0 + (UNICODE!) + +The "lf"-record +=============== +Offset Size Contents +0x0000 Word ID: ASCII-"lf" = 0x666C +0x0002 Word number of keys +0x0004 ???? Hash-Records + +Hash-Record +=========== +Offset Size Contents +0x0000 D-Word Offset of corresponding "nk"-Record +0x0004 D-Word ASCII: the first 4 characters of the key-name, padded with 0's. Case sensitiv! + +Keep in mind, that the value at 0x0004 is used for checking the data-consistency! If you change the +key-name you have to change the hash-value too! + +//These hashrecords must be sorted low to high within the lf record. Nigel. + +The "sk"-block +============== +(due to the complexity of the SAM-info, not clear jet) +(This is just a security descriptor in the data. R Sharpe.) + + +Offset Size Contents +0x0000 Word ID: ASCII-"sk" = 0x6B73 +0x0002 Word Unused +0x0004 D-Word Offset of previous "sk"-Record +0x0008 D-Word Offset of next "sk"-Record +0x000C D-Word usage-counter +0x0010 D-Word Size of "sk"-record in bytes +???? //standard self +relative security desciptor. Nigel +???? ???? Security and auditing settings... +???? + +The usage counter counts the number of references to this +"sk"-record. You can use one "sk"-record for the entire registry! + +Windows nt date/time format +=========================== +The time-format is a 64-bit integer which is incremented every +0,0000001 seconds by 1 (I don't know how accurate it realy is!) +It starts with 0 at the 1st of january 1601 0:00! All values are +stored in GMT time! The time-zone is important to get the real +time! + +Common values for win95 and win-nt +================================== +Offset values marking an "end of list", are either 0 or -1 (0xFFFFFFFF). +If a value has no name (length=0, flag(bit 0)=0), it is treated as the +"Default" entry... +If a value has no data (length=0), it is displayed as empty. + +simplyfied win-3.?? registry: +============================= + ++-----------+ +| next rec. |---+ +----->+------------+ +| first sub | | | | Usage cnt. | +| name | | +-->+------------+ | | length | +| value | | | | next rec. | | | text |------->+-------+ ++-----------+ | | | name rec. |--+ +------------+ | xxxxx | + +------------+ | | value rec. |-------->+------------+ +-------+ + v | +------------+ | Usage cnt. | ++-----------+ | | length | +| next rec. | | | text |------->+-------+ +| first sub |------+ +------------+ | xxxxx | +| name | +-------+ +| value | ++-----------+ + +Greatly simplyfied structure of the nt-registry: +================================================ + ++---------------------------------------------------------------+ +| | +v | ++---------+ +---------->+-----------+ +----->+---------+ | +| "nk" | | | lf-rec. | | | nk-rec. | | +| ID | | | # of keys | | | parent |---+ +| Date | | | 1st key |--+ | .... | +| parent | | +-----------+ +---------+ +| suk-keys|-----+ +| values |--------------------->+----------+ +| SK-rec. |---------------+ | 1. value |--> +----------+ +| class |--+ | +----------+ | vk-rec. | ++---------+ | | | .... | + v | | data |--> +-------+ + +------------+ | +----------+ | xxxxx | + | Class name | | +-------+ + +------------+ | + v + +---------+ +---------+ + +----->| next sk |--->| Next sk |--+ + | +---| prev sk |<---| prev sk | | + | | | .... | | ... | | + | | +---------+ +---------+ | + | | ^ | + | | | | + | +--------------------+ | + +----------------------------------+ + +--------------------------------------------------------------------------- + +Hope this helps.... (Although it was "fun" for me to uncover this things, + it took me several sleepless nights ;) + + B.D. + +*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int verbose = 0; + +/* + * These definitions are for the in-memory registry structure. + * It is a tree structure that mimics what you see with tools like regedit + */ + +/* + * DateTime struct for Windows + */ + +typedef struct date_time_s { + unsigned int low, high; +} NTTIME; + +/* + * Definition of a Key. It has a name, classname, date/time last modified, + * sub-keys, values, and a security descriptor + */ + +#define REG_ROOT_KEY 1 +#define REG_SUB_KEY 2 +#define REG_SYM_LINK 3 + +typedef struct reg_key_s { + char *name; /* Name of the key */ + char *class_name; + int type; /* One of REG_ROOT_KEY or REG_SUB_KEY */ + NTTIME last_mod; /* Time last modified */ + struct reg_key_s *owner; + struct key_list_s *sub_keys; + struct val_list_s *values; + struct key_sec_desc_s *security; +} REG_KEY; + +/* + * The KEY_LIST struct lists sub-keys. + */ + +typedef struct key_list_s { + int key_count; + REG_KEY *keys[1]; +} KEY_LIST; + +typedef struct val_key_s { + char *name; + int has_name; + int data_type; + int data_len; + void *data_blk; /* Might want a separate block */ +} VAL_KEY; + +typedef struct val_list_s { + int val_count; + VAL_KEY *vals[1]; +} VAL_LIST; + +#ifndef MAXSUBAUTHS +#define MAXSUBAUTHS 15 +#endif + +typedef struct dom_sid_s { + unsigned char ver, auths; + unsigned char auth[6]; + unsigned int sub_auths[MAXSUBAUTHS]; +} DOM_SID; + +typedef struct ace_struct_s { + unsigned char type, flags; + unsigned int perms; /* Perhaps a better def is in order */ + DOM_SID *trustee; +} ACE; + +typedef struct acl_struct_s { + unsigned short rev, refcnt; + unsigned short num_aces; + ACE *aces[1]; +} ACL; + +typedef struct sec_desc_s { + unsigned int rev, type; + DOM_SID *owner, *group; + ACL *sacl, *dacl; +} SEC_DESC; + +#define SEC_DESC_NON 0 +#define SEC_DESC_RES 1 +#define SEC_DESC_OCU 2 + +typedef struct key_sec_desc_s { + struct key_sec_desc_s *prev, *next; + int ref_cnt; + int state; + SEC_DESC *sec_desc; +} KEY_SEC_DESC; + + +/* + * An API for accessing/creating/destroying items above + */ + +/* + * Iterate over the keys, depth first, calling a function for each key + * and indicating if it is terminal or non-terminal and if it has values. + * + * In addition, for each value in the list, call a value list function + */ + +/* + * There should eventually be one to deal with security keys as well + */ + +typedef int (*key_print_f)(const char *path, char *key_name, char *class_name, + int root, int terminal, int values); + +typedef int (*val_print_f)(const char *path, char *val_name, int val_type, + int data_len, void *data_blk, int terminal, + int first, int last); + +typedef int (*sec_print_f)(SEC_DESC *sec_desc); + +typedef struct regf_struct_s REGF; + +int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path, + key_print_f key_print, sec_print_f sec_print, + val_print_f val_print); + +int nt_val_list_iterator(REGF *regf, VAL_LIST *val_list, int bf, char *path, + int terminal, val_print_f val_print) +{ + int i; + + if (!val_list) return 1; + + if (!val_print) return 1; + + for (i=0; ival_count; i++) { + if (!val_print(path, val_list->vals[i]->name, val_list->vals[i]->data_type, + val_list->vals[i]->data_len, val_list->vals[i]->data_blk, + terminal, + (i == 0), + (i == val_list->val_count))) { + + return 0; + + } + } + + return 1; +} + +int nt_key_list_iterator(REGF *regf, KEY_LIST *key_list, int bf, + const char *path, + key_print_f key_print, sec_print_f sec_print, + val_print_f val_print) +{ + int i; + + if (!key_list) return 1; + + for (i=0; i< key_list->key_count; i++) { + if (!nt_key_iterator(regf, key_list->keys[i], bf, path, key_print, + sec_print, val_print)) { + return 0; + } + } + return 1; +} + +int nt_key_iterator(REGF *regf, REG_KEY *key_tree, int bf, const char *path, + key_print_f key_print, sec_print_f sec_print, + val_print_f val_print) +{ + int path_len = strlen(path); + char *new_path; + + if (!regf || !key_tree) + return -1; + + /* List the key first, then the values, then the sub-keys */ + + if (key_print) { + + if (!(*key_print)(path, key_tree->name, + key_tree->class_name, + (key_tree->type == REG_ROOT_KEY), + (key_tree->sub_keys == NULL), + (key_tree->values?(key_tree->values->val_count):0))) + return 0; + } + + /* + * If we have a security print routine, call it + * If the security print routine returns false, stop. + */ + if (sec_print) { + if (key_tree->security && !(*sec_print)(key_tree->security->sec_desc)) + return 0; + } + + new_path = (char *)malloc(path_len + 1 + strlen(key_tree->name) + 1); + if (!new_path) return 0; /* Errors? */ + new_path[0] = '\0'; + strcat(new_path, path); + strcat(new_path, "\\"); + strcat(new_path, key_tree->name); + + /* + * Now, iterate through the values in the val_list + */ + + if (key_tree->values && + !nt_val_list_iterator(regf, key_tree->values, bf, new_path, + (key_tree->values!=NULL), + val_print)) { + + free(new_path); + return 0; + } + + /* + * Now, iterate through the keys in the key list + */ + + if (key_tree->sub_keys && + !nt_key_list_iterator(regf, key_tree->sub_keys, bf, new_path, key_print, + sec_print, val_print)) { + free(new_path); + return 0; + } + + free(new_path); + return 1; +} + +/* Make, delete keys */ + +int nt_delete_val_key(VAL_KEY *val_key) +{ + + if (val_key) { + if (val_key->data_blk) free(val_key->data_blk); + free(val_key); + }; + return 1; +} + +int nt_delete_val_list(VAL_LIST *vl) +{ + int i; + + if (vl) { + for (i=0; ival_count; i++) + nt_delete_val_key(vl->vals[i]); + free(vl); + } + return 1; +} + +int nt_delete_reg_key(REG_KEY *key); +int nt_delete_key_list(KEY_LIST *key_list) +{ + int i; + + if (key_list) { + for (i=0; ikey_count; i++) + nt_delete_reg_key(key_list->keys[i]); + free(key_list); + } + return 1; +} + +int nt_delete_sid(DOM_SID *sid) +{ + + if (sid) free(sid); + return 1; + +} + +int nt_delete_ace(ACE *ace) +{ + + if (ace) { + nt_delete_sid(ace->trustee); + free(ace); + } + return 1; + +} + +int nt_delete_acl(ACL *acl) +{ + + if (acl) { + int i; + + for (i=0; inum_aces; i++) + nt_delete_ace(acl->aces[i]); + + free(acl); + } + return 1; +} + +int nt_delete_sec_desc(SEC_DESC *sec_desc) +{ + + if (sec_desc) { + + nt_delete_sid(sec_desc->owner); + nt_delete_sid(sec_desc->group); + nt_delete_acl(sec_desc->sacl); + nt_delete_acl(sec_desc->dacl); + free(sec_desc); + + } + return 1; +} + +int nt_delete_key_sec_desc(KEY_SEC_DESC *key_sec_desc) +{ + + if (key_sec_desc) { + key_sec_desc->ref_cnt--; + if (key_sec_desc->ref_cnt<=0) { + /* + * There should always be a next and prev, even if they point to us + */ + key_sec_desc->next->prev = key_sec_desc->prev; + key_sec_desc->prev->next = key_sec_desc->next; + nt_delete_sec_desc(key_sec_desc->sec_desc); + } + } + return 1; +} + +int nt_delete_reg_key(REG_KEY *key) +{ + + if (key) { + if (key->name) free(key->name); + if (key->class_name) free(key->class_name); + + /* + * Do not delete the owner ... + */ + + if (key->sub_keys) nt_delete_key_list(key->sub_keys); + if (key->values) nt_delete_val_list(key->values); + if (key->security) nt_delete_key_sec_desc(key->security); + free(key); + } + return 1; +} + +/* + * Create/delete key lists and add delete keys to/from a list, count the keys + */ + + +/* + * Create/delete value lists, add/delete values, count them + */ + + +/* + * Create/delete security descriptors, add/delete SIDS, count SIDS, etc. + * We reference count the security descriptors. Any new reference increments + * the ref count. If we modify an SD, we copy the old one, dec the ref count + * and make the change. We also want to be able to check for equality so + * we can reduce the number of SDs in use. + */ + +/* + * Code to parse registry specification from command line or files + * + * Format: + * [cmd:]key:type:value + * + * cmd = a|d|c|add|delete|change|as|ds|cs + * + */ + + +/* + * Load and unload a registry file. + * + * Load, loads it into memory as a tree, while unload sealizes/flattens it + */ + +/* + * Get the starting record for NT Registry file + */ + +/* A map of sk offsets in the regf to KEY_SEC_DESCs for quick lookup etc */ +typedef struct sk_map_s { + int sk_off; + KEY_SEC_DESC *key_sec_desc; +} SK_MAP; + +/* + * Where we keep all the regf stuff for one registry. + * This is the structure that we use to tie the in memory tree etc + * together. By keeping separate structs, we can operate on different + * registries at the same time. + * Currently, the SK_MAP is an array of mapping structure. + * Since we only need this on input and output, we fill in the structure + * as we go on input. On output, we know how many SK items we have, so + * we can allocate the structure as we need to. + * If you add stuff here that is dynamically allocated, add the + * appropriate free statements below. + */ + +#define REGF_REGTYPE_NONE 0 +#define REGF_REGTYPE_NT 1 +#define REGF_REGTYPE_W9X 2 + +#define TTTONTTIME(r, t1, t2) (r)->last_mod_time.low = (t1); \ + (r)->last_mod_time.high = (t2); + +#define REGF_HDR_BLKSIZ 0x1000 + +struct regf_struct_s { + int reg_type; + char *regfile_name, *outfile_name; + int fd; + struct stat sbuf; + char *base; + int modified; + NTTIME last_mod_time; + REG_KEY *root; /* Root of the tree for this file */ + int sk_count, sk_map_size; + SK_MAP *sk_map; +}; + +/* + * Structures for dealing with the on-disk format of the registry + */ + +#define IVAL(buf) ((unsigned int) \ + (unsigned int)*((unsigned char *)(buf)+3)<<24| \ + (unsigned int)*((unsigned char *)(buf)+2)<<16| \ + (unsigned int)*((unsigned char *)(buf)+1)<<8| \ + (unsigned int)*((unsigned char *)(buf)+0)) + +#define SVAL(buf) ((unsigned short) \ + (unsigned short)*((unsigned char *)(buf)+1)<<8| \ + (unsigned short)*((unsigned char *)(buf)+0)) + +#define CVAL(buf) ((unsigned char)*((unsigned char *)(buf))) + +#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) +#define LOCN(base, f) ((base) + OFF(f)) + +/* + * All of the structures below actually have a four-byte lenght before them + * which always seems to be negative. The following macro retrieves that + * size as an integer + */ + +#define BLK_SIZE(b) ((int)*(int *)(((int *)b)-1)) + +typedef unsigned int DWORD; +typedef unsigned short WORD; + +#define REG_REGF_ID 0x66676572 + +typedef struct regf_block { + DWORD REGF_ID; /* regf */ + DWORD uk1; + DWORD uk2; + DWORD tim1, tim2; + DWORD uk3; /* 1 */ + DWORD uk4; /* 3 */ + DWORD uk5; /* 0 */ + DWORD uk6; /* 1 */ + DWORD first_key; /* offset */ + unsigned int dblk_size; + DWORD uk7[116]; /* 1 */ + DWORD chksum; +} REGF_HDR; + +typedef struct hbin_sub_struct { + DWORD dblocksize; + char data[1]; +} HBIN_SUB_HDR; + +#define REG_HBIN_ID 0x6E696268 + +typedef struct hbin_struct { + DWORD HBIN_ID; /* hbin */ + DWORD next_off; + DWORD prev_off; + DWORD uk1; + DWORD uk2; + DWORD uk3; + DWORD uk4; + DWORD blk_size; + HBIN_SUB_HDR hbin_sub_hdr; +} HBIN_HDR; + +#define REG_NK_ID 0x6B6E + +typedef struct nk_struct { + WORD NK_ID; + WORD type; + DWORD t1, t2; + DWORD uk1; + DWORD own_off; + DWORD subk_num; + DWORD uk2; + DWORD lf_off; + DWORD uk3; + DWORD val_cnt; + DWORD val_off; + DWORD sk_off; + DWORD clsnam_off; + DWORD unk4[4]; + DWORD unk5; + WORD nam_len; + WORD clsnam_len; + char key_nam[1]; /* Actual length determined by nam_len */ +} NK_HDR; + +#define REG_SK_ID 0x6B73 + +typedef struct sk_struct { + WORD SK_ID; + WORD uk1; + DWORD prev_off; + DWORD next_off; + DWORD ref_cnt; + DWORD rec_size; + char sec_desc[1]; +} SK_HDR; + +typedef struct ace_struct { + unsigned char type; + unsigned char flags; + unsigned short length; + unsigned int perms; + DOM_SID trustee; +} REG_ACE; + +typedef struct acl_struct { + WORD rev; + WORD size; + DWORD num_aces; + REG_ACE *aces; /* One or more ACEs */ +} REG_ACL; + +typedef struct sec_desc_rec { + WORD rev; + WORD type; + DWORD owner_off; + DWORD group_off; + DWORD sacl_off; + DWORD dacl_off; +} REG_SEC_DESC; + +typedef struct hash_struct { + DWORD nk_off; + char hash[4]; +} HASH_REC; + +#define REG_LF_ID 0x666C + +typedef struct lf_struct { + WORD LF_ID; + WORD key_count; + struct hash_struct hr[1]; /* Array of hash records, depending on key_count */ +} LF_HDR; + +typedef DWORD VL_TYPE[1]; /* Value list is an array of vk rec offsets */ + +#define REG_VK_ID 0x6B76 + +typedef struct vk_struct { + WORD VK_ID; + WORD nam_len; + DWORD dat_len; /* If top-bit set, offset contains the data */ + DWORD dat_off; + DWORD dat_type; + WORD flag; /* =1, has name, else no name (=Default). */ + WORD unk1; + char dat_name[1]; /* Name starts here ... */ +} VK_HDR; + +#define REG_TYPE_REGSZ 1 +#define REG_TYPE_EXPANDSZ 2 +#define REG_TYPE_BIN 3 +#define REG_TYPE_DWORD 4 +#define REG_TYPE_MULTISZ 7 + +typedef struct _val_str { + unsigned int val; + const char * str; +} VAL_STR; + +const VAL_STR reg_type_names[] = { + { 1, "REG_SZ" }, + { 2, "REG_EXPAND_SZ" }, + { 3, "REG_BIN" }, + { 4, "REG_DWORD" }, + { 7, "REG_MULTI_SZ" }, + { 0, NULL }, +}; + +const char *val_to_str(unsigned int val, const VAL_STR *val_array) +{ + int i = 0; + + if (!val_array) return NULL; + + while (val_array[i].val && val_array[i].str) { + + if (val_array[i].val == val) return val_array[i].str; + i++; + + } + + return NULL; + +} + +/* + * Convert from UniCode to Ascii ... Does not take into account other lang + * Restrict by ascii_max if > 0 + */ +int uni_to_ascii(unsigned char *uni, unsigned char *ascii, int ascii_max, + int uni_max) +{ + int i = 0; + + while (i < ascii_max && !(!uni[i*2] && !uni[i*2+1])) { + if (uni_max > 0 && (i*2) >= uni_max) break; + ascii[i] = uni[i*2]; + i++; + + } + + ascii[i] = '\0'; + + return i; +} + +/* + * Convert a data value to a string for display + */ +int data_to_ascii(unsigned char *datap, int len, int type, char *ascii, int ascii_max) +{ + unsigned char *asciip; + int i; + + switch (type) { + case REG_TYPE_REGSZ: + fprintf(stderr, "Len: %d\n", len); + return uni_to_ascii(datap, ascii, len, ascii_max); + break; + + case REG_TYPE_EXPANDSZ: + return uni_to_ascii(datap, ascii, len, ascii_max); + break; + + case REG_TYPE_BIN: + asciip = ascii; + for (i=0; (i 0) + *asciip = ' '; asciip++; + } + *asciip = '\0'; + return ((int)asciip - (int)ascii); + break; + + case REG_TYPE_DWORD: + if (*(int *)datap == 0) + return snprintf(ascii, ascii_max, "0"); + else + return snprintf(ascii, ascii_max, "0x%x", *(int *)datap); + break; + + case REG_TYPE_MULTISZ: + + break; + + default: + return 0; + break; + } + + return len; + +} + +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size); + +int nt_set_regf_input_file(REGF *regf, char *filename) +{ + return ((regf->regfile_name = strdup(filename)) != NULL); +} + +int nt_set_regf_output_file(REGF *regf, char *filename) +{ + return ((regf->outfile_name = strdup(filename)) != NULL); +} + +/* Create a regf structure and init it */ + +REGF *nt_create_regf(void) +{ + REGF *tmp = (REGF *)malloc(sizeof(REGF)); + if (!tmp) return tmp; + bzero(tmp, sizeof(REGF)); + return tmp; +} + +/* Free all the bits and pieces ... Assumes regf was malloc'd */ +/* If you add stuff to REGF, add the relevant free bits here */ +int nt_free_regf(REGF *regf) +{ + if (!regf) return 0; + + if (regf->regfile_name) free(regf->regfile_name); + if (regf->outfile_name) free(regf->outfile_name); + + /* Free the mmap'd area */ + + if (regf->base) munmap(regf->base, regf->sbuf.st_size); + regf->base = NULL; + close(regf->fd); /* Ignore the error :-) */ + + nt_delete_reg_key(regf->root); /* Free the tree */ + free(regf->sk_map); + regf->sk_count = regf->sk_map_size = 0; + + free(regf); + + return 1; +} + +/* Get the header of the registry. Return a pointer to the structure + * If the mmap'd area has not been allocated, then mmap the input file + */ +REGF_HDR *nt_get_regf_hdr(REGF *regf) +{ + if (!regf) + return NULL; /* What about errors */ + + if (!regf->regfile_name) + return NULL; /* What about errors */ + + if (!regf->base) { /* Try to mmap etc the file */ + + if ((regf->fd = open(regf->regfile_name, O_RDONLY, 0000)) <0) { + return NULL; /* What about errors? */ + } + + if (fstat(regf->fd, ®f->sbuf) < 0) { + return NULL; + } + + regf->base = mmap(0, regf->sbuf.st_size, PROT_READ, MAP_SHARED, regf->fd, 0); + + if ((int)regf->base == 1) { + fprintf(stderr, "Could not mmap file: %s, %s\n", regf->regfile_name, + strerror(errno)); + return NULL; + } + } + + /* + * At this point, regf->base != NULL, and we should be able to read the + * header + */ + + assert(regf->base != NULL); + + return (REGF_HDR *)regf->base; +} + +/* + * Validate a regf header + * For now, do nothing, but we should check the checksum + */ +int valid_regf_hdr(REGF_HDR *regf_hdr) +{ + if (!regf_hdr) return 0; + + return 1; +} + +/* + * Process an SK header ... + * Every time we see a new one, add it to the map. Otherwise, just look it up. + * We will do a simple linear search for the moment, since many KEYs have the + * same security descriptor. + * We allocate the map in increments of 10 entries. + */ + +/* + * Create a new entry in the map, and increase the size of the map if needed + */ + +SK_MAP *alloc_sk_map_entry(REGF *regf, KEY_SEC_DESC *tmp, int sk_off) +{ + if (!regf->sk_map) { /* Allocate a block of 10 */ + regf->sk_map = (SK_MAP *)malloc(sizeof(SK_MAP) * 10); + if (!regf->sk_map) { + free(tmp); + return NULL; + } + regf->sk_map_size = 10; + regf->sk_count = 1; + (regf->sk_map)[0].sk_off = sk_off; + (regf->sk_map)[0].key_sec_desc = tmp; + } + else { /* Simply allocate a new slot, unless we have to expand the list */ + int ndx = regf->sk_count; + if (regf->sk_count >= regf->sk_map_size) { + regf->sk_map = (SK_MAP *)realloc(regf->sk_map, + (regf->sk_map_size + 10)*sizeof(SK_MAP)); + if (!regf->sk_map) { + free(tmp); + return NULL; + } + /* + * ndx already points at the first entry of the new block + */ + regf->sk_map_size += 10; + } + (regf->sk_map)[ndx].sk_off = sk_off; + (regf->sk_map)[ndx].key_sec_desc = tmp; + regf->sk_count++; + } + return regf->sk_map; +} + +/* + * Search for a KEY_SEC_DESC in the sk_map, but dont create one if not + * found + */ + +KEY_SEC_DESC *lookup_sec_key(SK_MAP *sk_map, int count, int sk_off) +{ + int i; + + if (!sk_map) return NULL; + + for (i = 0; i < count; i++) { + + if (sk_map[i].sk_off == sk_off) + return sk_map[i].key_sec_desc; + + } + + return NULL; + +} + +/* + * Allocate a KEY_SEC_DESC if we can't find one in the map + */ + +KEY_SEC_DESC *lookup_create_sec_key(REGF *regf, SK_MAP *sk_map, int sk_off) +{ + KEY_SEC_DESC *tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off); + + if (tmp) { + return tmp; + } + else { /* Allocate a new one */ + tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC)); + if (!tmp) { + return NULL; + } + tmp->state = SEC_DESC_RES; + if (!alloc_sk_map_entry(regf, tmp, sk_off)) { + return NULL; + } + return tmp; + } +} + +/* + * Allocate storage and duplicate a SID + * We could allocate the SID to be only the size needed, but I am too lazy. + */ +DOM_SID *dup_sid(DOM_SID *sid) +{ + DOM_SID *tmp = (DOM_SID *)malloc(sizeof(DOM_SID)); + int i; + + if (!tmp) return NULL; + tmp->ver = sid->ver; + tmp->auths = sid->auths; + for (i=0; i<6; i++) { + tmp->auth[i] = sid->auth[i]; + } + for (i=0; iauths&&isub_auths[i] = sid->sub_auths[i]; + } + return tmp; +} + +/* + * Allocate space for an ACE and duplicate the registry encoded one passed in + */ +ACE *dup_ace(REG_ACE *ace) +{ + ACE *tmp = NULL; + + tmp = (ACE *)malloc(sizeof(ACE)); + + if (!tmp) return NULL; + + tmp->type = CVAL(&ace->type); + tmp->flags = CVAL(&ace->flags); + tmp->perms = IVAL(&ace->perms); + tmp->trustee = dup_sid(&ace->trustee); + return tmp; +} + +/* + * Allocate space for an ACL and duplicate the registry encoded one passed in + */ +ACL *dup_acl(REG_ACL *acl) +{ + ACL *tmp = NULL; + REG_ACE* ace; + int i, num_aces; + + num_aces = IVAL(&acl->num_aces); + + tmp = (ACL *)malloc(sizeof(ACL) + (num_aces - 1)*sizeof(ACE *)); + if (!tmp) return NULL; + + tmp->num_aces = num_aces; + tmp->refcnt = 1; + tmp->rev = SVAL(&acl->rev); + ace = (REG_ACE *)&acl->aces; + for (i=0; iaces[i] = dup_ace(ace); + ace = (REG_ACE *)((char *)ace + SVAL(&ace->length)); + /* XXX: FIXME, should handle malloc errors */ + } + + return tmp; +} + +SEC_DESC *process_sec_desc(REGF *regf, REG_SEC_DESC *sec_desc) +{ + SEC_DESC *tmp = NULL; + + tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC)); + + if (!tmp) { + return NULL; + } + + tmp->rev = SVAL(&sec_desc->rev); + tmp->type = SVAL(&sec_desc->type); + tmp->owner = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->owner_off))); + if (!tmp->owner) { + free(tmp); + return NULL; + } + tmp->group = dup_sid((DOM_SID *)((char *)sec_desc + IVAL(&sec_desc->group_off))); + if (!tmp->group) { + free(tmp); + return NULL; + } + + /* Now pick up the SACL and DACL */ + + if (sec_desc->sacl_off) + tmp->sacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->sacl_off))); + else + tmp->sacl = NULL; + + if (sec_desc->dacl_off) + tmp->dacl = dup_acl((REG_ACL *)((char *)sec_desc + IVAL(&sec_desc->dacl_off))); + else + tmp->dacl = NULL; + + return tmp; +} + +KEY_SEC_DESC *process_sk(REGF *regf, SK_HDR *sk_hdr, int sk_off, int size) +{ + KEY_SEC_DESC *tmp = NULL; + int sk_next_off, sk_prev_off, sk_size; + REG_SEC_DESC *sec_desc; + + if (!sk_hdr) return NULL; + + if (SVAL(&sk_hdr->SK_ID) != REG_SK_ID) { + fprintf(stderr, "Unrecognized SK Header ID: %08X, %s\n", (int)sk_hdr, + regf->regfile_name); + return NULL; + } + + if (-size < (sk_size = IVAL(&sk_hdr->rec_size))) { + fprintf(stderr, "Incorrect SK record size: %d vs %d. %s\n", + -size, sk_size, regf->regfile_name); + return NULL; + } + + /* + * Now, we need to look up the SK Record in the map, and return it + * Since the map contains the SK_OFF mapped to KEY_SEC_DESC, we can + * use that + */ + + if (regf->sk_map && + ((tmp = lookup_sec_key(regf->sk_map, regf->sk_count, sk_off)) != NULL) + && (tmp->state == SEC_DESC_OCU)) { + tmp->ref_cnt++; + return tmp; + } + + /* Here, we have an item in the map that has been reserved, or tmp==NULL. */ + + assert(tmp == NULL || (tmp && tmp->state != SEC_DESC_NON)); + + /* + * Now, allocate a KEY_SEC_DESC, and parse the structure here, and add the + * new KEY_SEC_DESC to the mapping structure, since the offset supplied is + * the actual offset of structure. The same offset will be used by all + * all future references to this structure + * We chould put all this unpleasantness in a function. + */ + + if (!tmp) { + tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC)); + if (!tmp) return NULL; + bzero(tmp, sizeof(KEY_SEC_DESC)); + + /* + * Allocate an entry in the SK_MAP ... + * We don't need to free tmp, because that is done for us if the + * sm_map entry can't be expanded when we need more space in the map. + */ + + if (!alloc_sk_map_entry(regf, tmp, sk_off)) { + return NULL; + } + } + + tmp->ref_cnt++; + tmp->state = SEC_DESC_OCU; + + /* + * Now, process the actual sec desc and plug the values in + */ + + sec_desc = (REG_SEC_DESC *)&sk_hdr->sec_desc[0]; + tmp->sec_desc = process_sec_desc(regf, sec_desc); + + /* + * Now forward and back links. Here we allocate an entry in the sk_map + * if it does not exist, and mark it reserved + */ + + sk_prev_off = IVAL(&sk_hdr->prev_off); + tmp->prev = lookup_create_sec_key(regf, regf->sk_map, sk_prev_off); + assert(tmp->prev != NULL); + sk_next_off = IVAL(&sk_hdr->next_off); + tmp->next = lookup_create_sec_key(regf, regf->sk_map, sk_next_off); + assert(tmp->next != NULL); + + return tmp; +} + +/* + * Process a VK header and return a value + */ +VAL_KEY *process_vk(REGF *regf, VK_HDR *vk_hdr, int size) +{ + char val_name[1024]; + int nam_len, dat_len, flag, dat_type, dat_off, vk_id; + const char *val_type; + VAL_KEY *tmp = NULL; + + if (!vk_hdr) return NULL; + + if ((vk_id = SVAL(&vk_hdr->VK_ID)) != REG_VK_ID) { + fprintf(stderr, "Unrecognized VK header ID: %0X, block: %0X, %s\n", + vk_id, (int)vk_hdr, regf->regfile_name); + return NULL; + } + + nam_len = SVAL(&vk_hdr->nam_len); + val_name[nam_len] = '\0'; + flag = SVAL(&vk_hdr->flag); + dat_type = IVAL(&vk_hdr->dat_type); + dat_len = IVAL(&vk_hdr->dat_len); /* If top bit, offset contains data */ + dat_off = IVAL(&vk_hdr->dat_off); + + tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY)); + if (!tmp) { + goto error; + } + bzero(tmp, sizeof(VAL_KEY)); + tmp->has_name = flag; + tmp->data_type = dat_type; + + if (flag & 0x01) { + strncpy(val_name, vk_hdr->dat_name, nam_len); + tmp->name = strdup(val_name); + if (!tmp->name) { + goto error; + } + } + else + strncpy(val_name, "", 10); + + /* + * Allocate space and copy the data as a BLOB + */ + + if (dat_len) { + + char *dtmp = (char *)malloc(dat_len&0x7FFFFFFF); + + if (!dtmp) { + goto error; + } + + tmp->data_blk = dtmp; + + if ((dat_len&0x80000000) == 0) { /* The data is pointed to by the offset */ + char *dat_ptr = LOCN(regf->base, dat_off); + bcopy(dat_ptr, dtmp, dat_len); + } + else { /* The data is in the offset */ + dat_len = dat_len & 0x7FFFFFFF; + bcopy(&dat_off, dtmp, dat_len); + } + + tmp->data_len = dat_len; + } + + val_type = val_to_str(dat_type, reg_type_names); + + /* + * We need to save the data area as well + */ + + if (verbose) fprintf(stdout, " %s : %s : \n", val_name, val_type); + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated struct */ + return NULL; + +} + +/* + * Process a VL Header and return a list of values + */ +VAL_LIST *process_vl(REGF *regf, VL_TYPE vl, int count, int size) +{ + int i, vk_off; + VK_HDR *vk_hdr; + VAL_LIST *tmp = NULL; + + if (!vl) return NULL; + + if (-size < (count+1)*sizeof(int)){ + fprintf(stderr, "Error in VL header format. Size less than space required. %d\n", -size); + return NULL; + } + + tmp = (VAL_LIST *)malloc(sizeof(VAL_LIST) + (count - 1) * sizeof(VAL_KEY *)); + if (!tmp) { + goto error; + } + + for (i=0; ibase, vk_off); + tmp->vals[i] = process_vk(regf, vk_hdr, BLK_SIZE(vk_hdr)); + if (!tmp->vals[i]){ + goto error; + } + } + + tmp->val_count = count; + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated structure */ + return NULL; +} + +/* + * Process an LF Header and return a list of sub-keys + */ +KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size) +{ + int count, i, nk_off; + unsigned int lf_id; + KEY_LIST *tmp; + + if (!lf_hdr) return NULL; + + if ((lf_id = SVAL(&lf_hdr->LF_ID)) != REG_LF_ID) { + fprintf(stderr, "Unrecognized LF Header format: %0X, Block: %0X, %s.\n", + lf_id, (int)lf_hdr, regf->regfile_name); + return NULL; + } + + assert(size < 0); + + count = SVAL(&lf_hdr->key_count); + + if (count <= 0) return NULL; + + /* Now, we should allocate a KEY_LIST struct and fill it in ... */ + + tmp = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (count - 1) * sizeof(REG_KEY *)); + if (!tmp) { + goto error; + } + + tmp->key_count = count; + + for (i=0; ihr[i].nk_off); + nk_hdr = (NK_HDR *)LOCN(regf->base, nk_off); + tmp->keys[i] = nt_get_key_tree(regf, nk_hdr, BLK_SIZE(nk_hdr)); + if (!tmp->keys[i]) { + goto error; + } + } + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated structure */ + return NULL; +} + +/* + * This routine is passed a NK_HDR pointer and retrieves the entire tree + * from there down. It return a REG_KEY *. + */ +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size) +{ + REG_KEY *tmp = NULL; + int name_len, clsname_len, lf_off, val_off, val_count, sk_off; + unsigned int nk_id; + LF_HDR *lf_hdr; + VL_TYPE *vl; + SK_HDR *sk_hdr; + char key_name[1024], cls_name[1024]; + + if (!nk_hdr) return NULL; + + if ((nk_id = SVAL(&nk_hdr->NK_ID)) != REG_NK_ID) { + fprintf(stderr, "Unrecognized NK Header format: %08X, Block: %0X. %s\n", + nk_id, (int)nk_hdr, regf->regfile_name); + return NULL; + } + + assert(size < 0); + + name_len = SVAL(&nk_hdr->nam_len); + clsname_len = SVAL(&nk_hdr->clsnam_len); + + /* + * The value of -size should be ge + * (sizeof(NK_HDR) - 1 + name_len) + * The -1 accounts for the fact that we included the first byte of + * the name in the structure. clsname_len is the length of the thing + * pointed to by clsnam_off + */ + + if (-size < (sizeof(NK_HDR) - 1 + name_len)) { + fprintf(stderr, "Incorrect NK_HDR size: %d, %0X\n", -size, (int)nk_hdr); + fprintf(stderr, "Sizeof NK_HDR: %d, name_len %d, clsname_len %d\n", + sizeof(NK_HDR), name_len, clsname_len); + /*return NULL;*/ + } + + if (verbose) fprintf(stdout, "NK HDR: Name len: %d, class name len: %d\n", + name_len, clsname_len); + + /* Fish out the key name and process the LF list */ + + assert(name_len < sizeof(key_name)); + + /* Allocate the key struct now */ + tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); + if (!tmp) return tmp; + bzero(tmp, sizeof(REG_KEY)); + + tmp->type = (SVAL(&nk_hdr->type)==0x2C?REG_ROOT_KEY:REG_SUB_KEY); + + strncpy(key_name, nk_hdr->key_nam, name_len); + key_name[name_len] = '\0'; + + if (verbose) fprintf(stdout, "Key name: %s\n", key_name); + + tmp->name = strdup(key_name); + if (!tmp->name) { + goto error; + } + + /* + * Fish out the class name, it is in UNICODE, while the key name is + * ASCII :-) + */ + + if (clsname_len) { /* Just print in Ascii for now */ + char *clsnamep; + int clsnam_off; + + clsnam_off = IVAL(&nk_hdr->clsnam_off); + clsnamep = LOCN(regf->base, clsnam_off); + + bzero(cls_name, clsname_len); + uni_to_ascii(clsnamep, cls_name, sizeof(cls_name), clsname_len); + + /* + * I am keeping class name as an ascii string for the moment. + * That means it needs to be converted on output. + * XXX: FIXME + */ + + tmp->class_name = strdup(cls_name); + if (!tmp->class_name) { + goto error; + } + + if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name); + + } + + /* + * If there are any values, process them here + */ + + val_count = IVAL(&nk_hdr->val_cnt); + + if (val_count) { + + val_off = IVAL(&nk_hdr->val_off); + vl = (VL_TYPE *)LOCN(regf->base, val_off); + + tmp->values = process_vl(regf, *vl, val_count, BLK_SIZE(vl)); + if (!tmp->values) { + goto error; + } + + } + + /* + * Also handle the SK header ... + */ + + sk_off = IVAL(&nk_hdr->sk_off); + sk_hdr = (SK_HDR *)LOCN(regf->base, sk_off); + + if (sk_off != -1) { + + tmp->security = process_sk(regf, sk_hdr, sk_off, BLK_SIZE(sk_hdr)); + + } + + lf_off = IVAL(&nk_hdr->lf_off); + + /* + * No more subkeys if lf_off == -1 + */ + + if (lf_off != -1) { + + lf_hdr = (LF_HDR *)LOCN(regf->base, lf_off); + + tmp->sub_keys = process_lf(regf, lf_hdr, BLK_SIZE(lf_hdr)); + if (!tmp->sub_keys){ + goto error; + } + + } + + return tmp; + + error: + if (tmp) nt_delete_reg_key(tmp); + return NULL; +} + +int nt_load_registry(REGF *regf) +{ + REGF_HDR *regf_hdr; + unsigned int regf_id, hbin_id; + HBIN_HDR *hbin_hdr; + NK_HDR *first_key; + + /* Get the header */ + + if ((regf_hdr = nt_get_regf_hdr(regf)) == NULL) { + return -1; + } + + /* Now process that header and start to read the rest in */ + + if ((regf_id = IVAL(®f_hdr->REGF_ID)) != REG_REGF_ID) { + fprintf(stderr, "Unrecognized NT registry header id: %0X, %s\n", + regf_id, regf->regfile_name); + return -1; + } + + /* + * Validate the header ... + */ + if (!valid_regf_hdr(regf_hdr)) { + fprintf(stderr, "Registry file header does not validate: %s\n", + regf->regfile_name); + return -1; + } + + /* Update the last mod date, and then go get the first NK record and on */ + + TTTONTTIME(regf, IVAL(®f_hdr->tim1), IVAL(®f_hdr->tim2)); + + /* + * The hbin hdr seems to be just uninteresting garbage. Check that + * it is there, but that is all. + */ + + hbin_hdr = (HBIN_HDR *)(regf->base + REGF_HDR_BLKSIZ); + + if ((hbin_id = IVAL(&hbin_hdr->HBIN_ID)) != REG_HBIN_ID) { + fprintf(stderr, "Unrecognized registry hbin hdr ID: %0X, %s\n", + hbin_id, regf->regfile_name); + return -1; + } + + /* + * Get a pointer to the first key from the hreg_hdr + */ + + first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key)); + + /* + * Now, get the registry tree by processing that NK recursively + */ + + regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key)); + + assert(regf->root != NULL); + + return 1; +} + +/* + * Routines to parse a REGEDIT4 file + * + * The file consists of: + * + * REGEDIT4 + * \[[-]key-path\]\n + * * + * + * There can be more than one key-path and value-spec. + * + * Since we want to support more than one type of file format, we + * construct a command-file structure that keeps info about the command file + */ + +#define FMT_UNREC -1 +#define FMT_REGEDIT4 0 +#define FMT_EDITREG1_1 1 + +typedef struct command_s { + int cmd; + char *key; + void *val_spec_list; +} CMD; + +/* + * We seek to offset 0, read in the required number of bytes, + * and compare to the correct value. + * We then seek back to the original location + */ +int regedit4_file_type(int fd) +{ + int cur_ofs = 0; + + cur_ofs = lseek(fd, 0, SEEK_CUR); /* Get current offset */ + if (cur_ofs < 0) { + fprintf(stderr, "Unable to get current offset: %s\n", strerror(errno)); + exit(1); + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + return FMT_UNREC; +} + +CMD *regedit4_get_cmd(int fd) +{ + return NULL; +} + +int regedit4_exec_cmd(CMD *cmd) +{ + + return 0; +} + +int editreg_1_1_file_type(int fd) +{ + + return FMT_UNREC; +} + +CMD *editreg_1_1_get_cmd(int fd) +{ + return NULL; +} + +int editreg_1_1_exec_cmd(CMD *cmd) +{ + + return -1; +} + +typedef struct command_ops_s { + int type; + int (*file_type)(int fd); + CMD *(*get_cmd)(int fd); + int (*exec_cmd)(CMD *cmd); +} CMD_OPS; + +CMD_OPS default_cmd_ops[] = { + {0, regedit4_file_type, regedit4_get_cmd, regedit4_exec_cmd}, + {1, editreg_1_1_file_type, editreg_1_1_get_cmd, editreg_1_1_exec_cmd}, + {-1, NULL, NULL, NULL} +}; + +typedef struct command_file_s { + char *name; + int type, fd; + CMD_OPS cmd_ops; +} CMD_FILE; + +/* + * Create a new command file structure + */ + +CMD_FILE *cmd_file_create(char *file) +{ + CMD_FILE *tmp; + struct stat sbuf; + int i = 0; + + /* + * Let's check if the file exists ... + * No use creating the cmd_file structure if the file does not exist + */ + + if (stat(file, &sbuf) < 0) { /* Not able to access file */ + + return NULL; + } + + tmp = (CMD_FILE *)malloc(sizeof(CMD_FILE)); + if (!tmp) { + return NULL; + } + + /* + * Let's fill in some of the fields; + */ + + tmp->name = strdup(file); + + if ((tmp->fd = open(file, O_RDONLY, 666)) < 0) { + free(tmp); + return NULL; + } + + /* + * Now, try to find the format by indexing through the table + */ + while (default_cmd_ops[i].type != -1) { + if ((tmp->type = default_cmd_ops[i].file_type(tmp->fd)) >= 0) { + tmp->cmd_ops = default_cmd_ops[i]; + return tmp; + } + i++; + } + + /* + * If we got here, return NULL, as we could not figure out the type + * of command file. + * + * What about errors? + */ + + free(tmp); + return NULL; +} + +/* + * Extract commands from the command file, and execute them. + * We pass a table of command callbacks for that + */ + +/* + * Main code from here on ... + */ + +/* + * key print function here ... + */ + +int print_key(const char *path, char *name, char *class_name, int root, + int terminal, int vals) +{ + + if (terminal) fprintf(stdout, "%s\\%s\n", path, name); + + return 1; +} + +/* + * Sec Desc print functions + */ + +void print_sid(DOM_SID *sid) +{ + int i, comps = sid->auths; + fprintf(stdout, "S-%u-%u", sid->ver, sid->auth[5]); + + for (i = 0; i < comps; i++) { + + fprintf(stdout, "-%u", sid->sub_auths[i]); + + } + fprintf(stdout, "\n"); +} + +int print_sec(SEC_DESC *sec_desc) +{ + + fprintf(stdout, " SECURITY\n"); + fprintf(stdout, " Owner: "); + print_sid(sec_desc->owner); + fprintf(stdout, " Group: "); + print_sid(sec_desc->group); + return 1; +} + +/* + * Value print function here ... + */ +int print_val(const char *path, char *val_name, int val_type, int data_len, + void *data_blk, int terminal, int first, int last) +{ + char data_asc[1024]; + + bzero(data_asc, sizeof(data_asc)); + if (!terminal && first) + fprintf(stdout, "%s\n", path); + data_to_ascii((unsigned char *)data_blk, data_len, val_type, data_asc, + sizeof(data_asc) - 1); + fprintf(stdout, " %s : %s : %s\n", (val_name?val_name:""), + val_to_str(val_type, reg_type_names), data_asc); + return 1; +} + +void usage(void) +{ + fprintf(stderr, "Usage: editreg [-v] [-k] [-c ] \n"); + fprintf(stderr, "Version: 0.1\n\n"); + fprintf(stderr, "\n\t-v\t sets verbose mode"); + fprintf(stderr, "\n\t-c \t specifies a command file"); + fprintf(stderr, "\n"); +} + +int main(int argc, char *argv[]) +{ + REGF *regf; + extern char *optarg; + extern int optind; + int opt; + int commands = 0; + char *cmd_file = NULL; + + if (argc < 2) { + usage(); + exit(1); + } + + /* + * Now, process the arguments + */ + + while ((opt = getopt(argc, argv, "vkc:")) != EOF) { + switch (opt) { + case 'c': + commands = 1; + cmd_file = optarg; + break; + + case 'v': + verbose++; + break; + + case 'k': + break; + + default: + usage(); + exit(1); + break; + } + } + + if ((regf = nt_create_regf()) == NULL) { + fprintf(stderr, "Could not create registry object: %s\n", strerror(errno)); + exit(2); + } + + if (!nt_set_regf_input_file(regf, argv[optind])) { + fprintf(stderr, "Could not set name of registry file: %s, %s\n", + argv[1], strerror(errno)); + exit(3); + } + + /* Now, open it, and bring it into memory :-) */ + + if (nt_load_registry(regf) < 0) { + fprintf(stderr, "Could not load registry: %s\n", argv[1]); + exit(4); + } + + /* + * At this point, we should have a registry in memory and should be able + * to iterate over it. + */ + + nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val); + return 0; +} diff --git a/source4/utils/net.c b/source4/utils/net.c new file mode 100644 index 0000000000..5c78d4d5a7 --- /dev/null +++ b/source4/utils/net.c @@ -0,0 +1,642 @@ +/* + 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. + + Reworked again by abartlet in December 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/*****************************************************/ +/* */ +/* 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" + +/***********************************************************************/ +/* 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 */ +/************************************************************************************/ + +/* Yes, these buggers are globals.... */ +const char *opt_requester_name = NULL; +const char *opt_host = NULL; +const char *opt_password = NULL; +const char *opt_user_name = NULL; +BOOL opt_user_specified = False; +const char *opt_workgroup = NULL; +int opt_long_list_entries = 0; +int opt_reboot = 0; +int opt_force = 0; +int opt_port = 0; +int opt_maxusers = -1; +const char *opt_comment = ""; +char *opt_container = "cn=Users"; +int opt_flags = -1; +int opt_jobid = 0; +int opt_timeout = 0; +const char *opt_target_workgroup = NULL; +static int opt_machine_pass = 0; + +BOOL opt_have_ip = False; +struct in_addr opt_dest_ip; + +extern BOOL AllowDebugChange; + +/* + run a function from a function table. If not found then + call the specified usage function +*/ +int net_run_function(int argc, const char **argv, struct functable *table, + int (*usage_fn)(int argc, const char **argv)) +{ + int i; + + if (argc < 1) { + d_printf("\nUsage: \n"); + return usage_fn(argc, argv); + } + for (i=0; table[i].funcname; i++) { + if (StrCaseCmp(argv[0], table[i].funcname) == 0) + return table[i].fn(argc-1, argv+1); + } + d_printf("No command: %s\n", argv[0]); + return usage_fn(argc, argv); +} + + +/**************************************************************************** +connect to \\server\ipc$ +****************************************************************************/ +NTSTATUS connect_to_ipc(struct cli_state **c, struct in_addr *server_ip, + const char *server_name) +{ + NTSTATUS nt_status; + + if (!opt_password) { + char *pass = getpass("Password:"); + if (pass) { + opt_password = strdup(pass); + } + } + + nt_status = cli_full_connection(c, opt_requester_name, server_name, + server_ip, opt_port, + "IPC$", "IPC", + opt_user_name, opt_workgroup, + opt_password, 0, NULL); + + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } else { + DEBUG(1,("Cannot connect to server. Error was %s\n", + nt_errstr(nt_status))); + + /* Display a nicer message depending on the result */ + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_LOGON_FAILURE)) + d_printf("The username or password was not correct.\n"); + + return nt_status; + } +} + +/**************************************************************************** +connect to \\server\ipc$ anonymously +****************************************************************************/ +NTSTATUS connect_to_ipc_anonymous(struct cli_state **c, + struct in_addr *server_ip, const char *server_name) +{ + NTSTATUS nt_status; + + nt_status = cli_full_connection(c, opt_requester_name, server_name, + server_ip, opt_port, + "IPC$", "IPC", + "", "", + "", 0, 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; + } +} + +BOOL net_find_server(unsigned flags, struct in_addr *server_ip, char **server_name) +{ + + if (opt_host) { + *server_name = strdup(opt_host); + } + + if (opt_have_ip) { + *server_ip = opt_dest_ip; + if (!*server_name) { + *server_name = strdup(inet_ntoa(opt_dest_ip)); + } + } else if (*server_name) { + /* resolve the IP address */ + if (!resolve_name(*server_name, server_ip, 0x20)) { + DEBUG(1,("Unable to resolve server name\n")); + return False; + } + } else if (flags & NET_FLAGS_PDC) { + struct in_addr pdc_ip; + + if (get_pdc_ip(opt_target_workgroup, &pdc_ip)) { + fstring dc_name; + + if (is_zero_ip(pdc_ip)) + return False; + + if (!lookup_dc_name(lp_netbios_name(), opt_target_workgroup, &pdc_ip, dc_name)) + return False; + + *server_name = strdup(dc_name); + *server_ip = pdc_ip; + } + + } else if (flags & NET_FLAGS_DMB) { + struct in_addr msbrow_ip; + /* if (!resolve_name(MSBROWSE, &msbrow_ip, 1)) */ + if (!resolve_name(opt_target_workgroup, &msbrow_ip, 0x1B)) { + DEBUG(1,("Unable to resolve domain browser via name lookup\n")); + return False; + } else { + *server_ip = msbrow_ip; + } + *server_name = strdup(inet_ntoa(opt_dest_ip)); + } else if (flags & NET_FLAGS_MASTER) { + struct in_addr brow_ips; + if (!resolve_name(opt_target_workgroup, &brow_ips, 0x1D)) { + /* go looking for workgroups */ + DEBUG(1,("Unable to resolve master browser via name lookup\n")); + return False; + } else { + *server_ip = brow_ips; + } + *server_name = strdup(inet_ntoa(opt_dest_ip)); + } else if (!(flags & NET_FLAGS_LOCALHOST_DEFAULT_INSANE)) { + extern struct in_addr loopback_ip; + *server_ip = loopback_ip; + *server_name = strdup("127.0.0.1"); + } + + if (!server_name || !*server_name) { + DEBUG(1,("no server to connect to\n")); + return False; + } + + return True; +} + + +BOOL net_find_dc(struct in_addr *server_ip, fstring server_name, const char *domain_name) +{ + if (get_pdc_ip(domain_name, server_ip)) { + fstring dc_name; + + if (is_zero_ip(*server_ip)) + return False; + + if (!lookup_dc_name(lp_netbios_name(), domain_name, server_ip, dc_name)) + return False; + + safe_strcpy(server_name, dc_name, FSTRING_LEN); + return True; + } else + return False; +} + + +struct cli_state *net_make_ipc_connection(unsigned flags) +{ + char *server_name = NULL; + struct in_addr server_ip; + struct cli_state *cli = NULL; + NTSTATUS nt_status; + + if (!net_find_server(flags, &server_ip, &server_name)) { + d_printf("\nUnable to find a suitable server\n"); + return NULL; + } + + if (flags & NET_FLAGS_ANONYMOUS) { + nt_status = connect_to_ipc_anonymous(&cli, &server_ip, server_name); + } else { + nt_status = connect_to_ipc(&cli, &server_ip, server_name); + } + + SAFE_FREE(server_name); + if (NT_STATUS_IS_OK(nt_status)) { + return cli; + } else { + return NULL; + } +} + +static int net_user(int argc, const char **argv) +{ + if (net_ads_check() == 0) + return net_ads_user(argc, argv); + + /* if server is not specified, default to PDC? */ + if (net_rpc_check(NET_FLAGS_PDC)) + return net_rpc_user(argc, argv); + + return net_rap_user(argc, argv); +} + +static int net_group(int argc, const char **argv) +{ + if (net_ads_check() == 0) + return net_ads_group(argc, argv); + + if (argc == 0 && net_rpc_check(NET_FLAGS_PDC)) + return net_rpc_group(argc, argv); + + return net_rap_group(argc, argv); +} + +static int net_join(int argc, const char **argv) +{ + if (net_ads_check() == 0) { + if (net_ads_join(argc, argv) == 0) + return 0; + else + d_printf("ADS join did not work, trying RPC...\n"); + } + return net_rpc_join(argc, argv); +} + +static int net_share(int argc, const char **argv) +{ + if (net_rpc_check(0)) + return net_rpc_share(argc, argv); + return net_rap_share(argc, argv); +} + +static int net_file(int argc, const char **argv) +{ + if (net_rpc_check(0)) + return net_rpc_file(argc, argv); + return net_rap_file(argc, argv); +} + +/* + Retrieve our local SID or the SID for the specified name + */ +static int net_getlocalsid(int argc, const char **argv) +{ + DOM_SID sid; + const char *name; + fstring sid_str; + + if (argc >= 1) { + name = argv[0]; + } + else { + name = lp_netbios_name(); + } + + if (!secrets_fetch_domain_sid(name, &sid)) { + DEBUG(0, ("Can't fetch domain SID for name: %s\n", name)); + return 1; + } + sid_to_string(sid_str, &sid); + d_printf("SID for domain %s is: %s\n", name, sid_str); + return 0; +} + +static int net_setlocalsid(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(lp_netbios_name(), &sid)) { + DEBUG(0,("Can't store domain SID as a pdc/bdc.\n")); + return 1; + } + + return 0; +} + +static int net_getdomainsid(int argc, const char **argv) +{ + DOM_SID domain_sid; + fstring sid_str; + + if (!secrets_fetch_domain_sid(lp_netbios_name(), &domain_sid)) { + d_printf("Could not fetch local SID\n"); + return 1; + } + sid_to_string(sid_str, &domain_sid); + d_printf("SID for domain %s is: %s\n", lp_netbios_name(), sid_str); + + if (!secrets_fetch_domain_sid(lp_workgroup(), &domain_sid)) { + d_printf("Could not fetch domain SID\n"); + return 1; + } + + sid_to_string(sid_str, &domain_sid); + d_printf("SID for domain %s is: %s\n", lp_workgroup(), sid_str); + + return 0; +} + +static uint32 get_maxrid(void) +{ + SAM_ACCOUNT *pwd = NULL; + uint32 max_rid = 0; + GROUP_MAP *map = NULL; + int num_entries = 0; + int i; + + if (!pdb_setsampwent(False)) { + DEBUG(0, ("load_sampwd_entries: Unable to open passdb.\n")); + return 0; + } + + for (; (NT_STATUS_IS_OK(pdb_init_sam(&pwd))) + && pdb_getsampwent(pwd) == True; pwd=NULL) { + uint32 rid; + + if (!sid_peek_rid(pdb_get_user_sid(pwd), &rid)) { + DEBUG(0, ("can't get RID for user '%s'\n", + pdb_get_username(pwd))); + pdb_free_sam(&pwd); + continue; + } + + if (rid > max_rid) + max_rid = rid; + + DEBUG(1,("%d is user '%s'\n", rid, pdb_get_username(pwd))); + pdb_free_sam(&pwd); + } + + pdb_endsampwent(); + pdb_free_sam(&pwd); + + if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &num_entries, + ENUM_ONLY_MAPPED, MAPPING_WITHOUT_PRIV)) + return max_rid; + + for (i = 0; i < num_entries; i++) { + uint32 rid; + + if (!sid_peek_check_rid(get_global_sam_sid(), &map[i].sid, + &rid)) { + DEBUG(3, ("skipping map for group '%s', SID %s\n", + map[i].nt_name, + sid_string_static(&map[i].sid))); + continue; + } + DEBUG(1,("%d is group '%s'\n", rid, map[i].nt_name)); + + if (rid > max_rid) + max_rid = rid; + } + + SAFE_FREE(map); + + return max_rid; +} + +static int net_maxrid(int argc, const char **argv) +{ + uint32 rid; + + if (argc != 0) { + DEBUG(0, ("usage: net initrid\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}, + {"RAP", net_rap}, + {"ADS", net_ads}, + + /* eventually these should auto-choose the transport ... */ + {"FILE", net_file}, + {"SHARE", net_share}, + {"SESSION", net_rap_session}, + {"SERVER", net_rap_server}, + {"DOMAIN", net_rap_domain}, + {"PRINTQ", net_rap_printq}, + {"USER", net_user}, + {"GROUP", net_group}, + {"VALIDATE", net_rap_validate}, + {"GROUPMEMBER", net_rap_groupmember}, + {"ADMIN", net_rap_admin}, + {"SERVICE", net_rap_service}, + {"PASSWORD", net_rap_password}, + {"TIME", net_time}, + {"LOOKUP", net_lookup}, + {"JOIN", net_join}, + {"CACHE", net_cache}, + {"GETLOCALSID", net_getlocalsid}, + {"SETLOCALSID", net_setlocalsid}, + {"GETDOMAINSID", net_getdomainsid}, + {"MAXRID", net_maxrid}, + + {"HELP", net_help}, + {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; + static char *servicesf = dyn_CONFIGFILE; + static char *debuglevel = NULL; + + struct poptOption long_options[] = { + {"help", 'h', POPT_ARG_NONE, 0, 'h'}, + {"workgroup", 'w', POPT_ARG_STRING, &opt_target_workgroup}, + {"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup}, + {"user", 'U', POPT_ARG_STRING, &opt_user_name, 'U'}, + {"ipaddress", 'I', POPT_ARG_STRING, 0,'I'}, + {"port", 'p', POPT_ARG_INT, &opt_port}, + {"myname", 'n', POPT_ARG_STRING, &opt_requester_name}, + {"conf", 's', POPT_ARG_STRING, &servicesf}, + {"server", 'S', POPT_ARG_STRING, &opt_host}, + {"container", 'c', POPT_ARG_STRING, &opt_container}, + {"comment", 'C', POPT_ARG_STRING, &opt_comment}, + {"maxusers", 'M', POPT_ARG_INT, &opt_maxusers}, + {"flags", 'F', POPT_ARG_INT, &opt_flags}, + {"jobid", 'j', POPT_ARG_INT, &opt_jobid}, + {"long", 'l', POPT_ARG_NONE, &opt_long_list_entries}, + {"reboot", 'r', POPT_ARG_NONE, &opt_reboot}, + {"force", 'f', POPT_ARG_NONE, &opt_force}, + {"timeout", 't', POPT_ARG_INT, &opt_timeout}, + {"machine-pass",'P', POPT_ARG_NONE, &opt_machine_pass}, + {"debuglevel", 'd', POPT_ARG_STRING, &debuglevel}, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0} + }; + + zero_ip(&opt_dest_ip); + + dbf = x_stderr; + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'h': + net_help(argc, argv); + exit(0); + break; + case 'I': + opt_dest_ip = *interpret_addr2(poptGetOptArg(pc)); + if (is_zero_ip(opt_dest_ip)) + d_printf("\nInvalid ip address specified\n"); + else + opt_have_ip = True; + break; + case 'U': + opt_user_specified = True; + opt_user_name = strdup(opt_user_name); + p = strchr(opt_user_name,'%'); + if (p) { + *p = 0; + opt_password = p+1; + } + break; + default: + d_printf("\nInvalid option %s: %s\n", + poptBadOption(pc, 0), poptStrerror(opt)); + net_help(argc, argv); + exit(1); + } + } + + if (debuglevel) { + debug_parse_levels(debuglevel); + AllowDebugChange = False; + } + + lp_load(servicesf,True,False,False); + + argv_new = (const char **)poptGetArgs(pc); + + argc_new = argc; + for (i=0; i"\ +"\n\tjoins the local machine to a ADS realm\n"\ +"\nnet ads leave"\ +"\n\tremoves the local machine from a ADS realm\n"\ +"\nnet ads testjoin"\ +"\n\ttests that an exiting join is OK\n"\ +"\nnet ads user"\ +"\n\tlist, add, or delete users in the realm\n"\ +"\nnet ads group"\ +"\n\tlist, add, or delete groups in the realm\n"\ +"\nnet ads info"\ +"\n\tshows some info on the server\n"\ +"\nnet ads status"\ +"\n\tdump the machine account details to stdout\n" +"\nnet ads lookup"\ +"\n\tperform a CLDAP search on the server\n" +"\nnet ads password -Uadmin_username@realm%%admin_pass"\ +"\n\tchange a user's password using an admin account"\ +"\n\t(note: use realm in UPPERCASE)\n"\ +"\nnet ads chostpass"\ +"\n\tchange the trust account password of this machine in the AD tree\n"\ +"\nnet ads printer [info | publish | remove] "\ +"\n\t lookup, add, or remove directory entry for a printer\n"\ +"\nnet ads search"\ +"\n\tperform a raw LDAP search and dump the results\n" +"\nnet ads dn"\ +"\n\tperform a raw LDAP search and dump attributes of a particular DN\n" + ); + return -1; +} + + +/* + this implements the CLDAP based netlogon lookup requests + for finding the domain controller of a ADS domain +*/ +static int net_ads_lookup(int argc, const char **argv) +{ + ADS_STRUCT *ads; + + ads = ads_init(NULL, NULL, opt_host); + if (ads) { + ads->auth.flags |= ADS_AUTH_NO_BIND; + } + + ads_connect(ads); + + if (!ads || !ads->config.realm) { + d_printf("Didn't find the cldap server!\n"); + return -1; + } + + return ads_cldap_netlogon(ads); +} + + + +static int net_ads_info(int argc, const char **argv) +{ + ADS_STRUCT *ads; + + ads = ads_init(NULL, NULL, opt_host); + + if (ads) { + ads->auth.flags |= ADS_AUTH_NO_BIND; + } + + ads_connect(ads); + + if (!ads || !ads->config.realm) { + d_printf("Didn't find the ldap server!\n"); + return -1; + } + + d_printf("LDAP server: %s\n", inet_ntoa(ads->ldap_ip)); + 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)); + + 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_STRUCT *ads_startup(void) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + BOOL need_password = False; + BOOL second_time = False; + + ads = ads_init(NULL, NULL, opt_host); + + if (!opt_user_name) { + opt_user_name = "administrator"; + } + + if (opt_user_specified) { + need_password = True; + } + +retry: + if (!opt_password && need_password) { + char *prompt; + asprintf(&prompt,"%s password: ", opt_user_name); + opt_password = getpass(prompt); + free(prompt); + } + + if (opt_password) { + use_in_memory_ccache(); + ads->auth.password = strdup(opt_password); + } + + ads->auth.user_name = strdup(opt_user_name); + + status = ads_connect(ads); + if (!ADS_ERR_OK(status)) { + if (!need_password && !second_time) { + need_password = True; + second_time = True; + goto retry; + } else { + DEBUG(1,("ads_connect: %s\n", ads_errstr(status))); + return NULL; + } + } + return 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. +*/ +int net_ads_check(void) +{ + ADS_STRUCT *ads; + + ads = ads_startup(); + if (!ads) + return -1; + ads_destroy(&ads); + return 0; +} + +/* + determine the netbios workgroup name for a domain + */ +static int net_ads_workgroup(int argc, const char **argv) +{ + ADS_STRUCT *ads; + TALLOC_CTX *ctx; + char *workgroup; + + if (!(ads = ads_startup())) return -1; + + if (!(ctx = talloc_init("net_ads_workgroup"))) { + return -1; + } + + if (!ADS_ERR_OK(ads_workgroup_name(ads, ctx, &workgroup))) { + d_printf("Failed to find workgroup for realm '%s'\n", + ads->config.realm); + talloc_destroy(ctx); + return -1; + } + + d_printf("Workgroup: %s\n", workgroup); + + talloc_destroy(ctx); + + return 0; +} + + + +static BOOL usergrp_display(char *field, void **values, void *data_area) +{ + char **disp_fields = (char **) data_area; + + if (!field) { /* must be end of record */ + if (!strchr_m(disp_fields[0], '$')) { + if (disp_fields[1]) + d_printf("%-21.21s %-50.50s\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] = strdup((char *) values[0]); + } + if (StrCaseCmp(field, "description") == 0) + disp_fields[1] = strdup((char *) values[0]); + return True; +} + +static int net_ads_user_usage(int argc, const char **argv) +{ + return net_help_user(argc, argv); +} + +static int ads_user_add(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + char *upn, *userdn; + void *res=NULL; + int rc = -1; + + if (argc < 1) return net_ads_user_usage(argc, argv); + + if (!(ads = ads_startup())) return -1; + + status = ads_find_user_acct(ads, &res, argv[0]); + + if (!ADS_ERR_OK(status)) { + d_printf("ads_user_add: %s\n", ads_errstr(status)); + goto done; + } + + if (ads_count_replies(ads, res)) { + d_printf("ads_user_add: User %s already exists\n", argv[0]); + goto done; + } + + status = ads_add_user_acct(ads, argv[0], opt_container, opt_comment); + + if (!ADS_ERR_OK(status)) { + d_printf("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 = 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_printf("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); + return rc; +} + +static int ads_user_info(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + void *res; + const char *attrs[] = {"memberOf", NULL}; + char *searchstring=NULL; + char **grouplist; + char *escaped_user = escape_ldap_string_alloc(argv[0]); + + if (argc < 1) return net_ads_user_usage(argc, argv); + + if (!(ads = ads_startup())) return -1; + + if (!escaped_user) { + d_printf("ads_user_info: failed to escape user %s\n", argv[0]); + return -1; + } + + asprintf(&searchstring, "(sAMAccountName=%s)", escaped_user); + rc = ads_search(ads, &res, searchstring, attrs); + safe_free(searchstring); + + if (!ADS_ERR_OK(rc)) { + d_printf("ads_search: %s\n", ads_errstr(rc)); + return -1; + } + + grouplist = ldap_get_values(ads->ld, 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); + return 0; +} + +static int ads_user_delete(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + void *res; + char *userdn; + + if (argc < 1) return net_ads_user_usage(argc, argv); + + if (!(ads = ads_startup())) return -1; + + rc = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(rc)) { + DEBUG(0, ("User %s does not exist\n", argv[0])); + 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]); + return 0; + } + d_printf("Error deleting user %s: %s\n", argv[0], + ads_errstr(rc)); + return -1; +} + +int net_ads_user(int argc, const char **argv) +{ + struct functable func[] = { + {"ADD", ads_user_add}, + {"INFO", ads_user_info}, + {"DELETE", ads_user_delete}, + {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 (!(ads = ads_startup())) return -1; + + if (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, + "(objectclass=user)", + opt_long_list_entries ? longattrs : + shortattrs, usergrp_display, + disp_fields); + ads_destroy(&ads); + return 0; + } + + return net_run_function(argc, argv, func, net_ads_user_usage); +} + +static int net_ads_group_usage(int argc, const char **argv) +{ + return net_help_group(argc, argv); +} + +static int ads_group_add(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + void *res=NULL; + int rc = -1; + + if (argc < 1) return net_ads_group_usage(argc, argv); + + if (!(ads = ads_startup())) return -1; + + status = ads_find_user_acct(ads, &res, argv[0]); + + if (!ADS_ERR_OK(status)) { + d_printf("ads_group_add: %s\n", ads_errstr(status)); + goto done; + } + + if (ads_count_replies(ads, res)) { + d_printf("ads_group_add: Group %s already exists\n", argv[0]); + ads_msgfree(ads, res); + goto done; + } + + status = ads_add_group_acct(ads, argv[0], opt_container, opt_comment); + + if (ADS_ERR_OK(status)) { + d_printf("Group %s added\n", argv[0]); + rc = 0; + } else { + d_printf("Could not add group %s: %s\n", argv[0], + ads_errstr(status)); + } + + done: + if (res) + ads_msgfree(ads, res); + ads_destroy(&ads); + return rc; +} + +static int ads_group_delete(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + void *res; + char *groupdn; + + if (argc < 1) return net_ads_group_usage(argc, argv); + + if (!(ads = ads_startup())) return -1; + + rc = ads_find_user_acct(ads, &res, argv[0]); + if (!ADS_ERR_OK(rc)) { + DEBUG(0, ("Group %s does not exist\n", argv[0])); + 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]); + return 0; + } + d_printf("Error deleting group %s: %s\n", argv[0], + ads_errstr(rc)); + return -1; +} + +int net_ads_group(int argc, const char **argv) +{ + struct functable func[] = { + {"ADD", ads_group_add}, + {"DELETE", ads_group_delete}, + {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 (!(ads = ads_startup())) return -1; + + if (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, + "(objectclass=group)", + opt_long_list_entries ? longattrs : + shortattrs, usergrp_display, + disp_fields); + + ads_destroy(&ads); + return 0; + } + return net_run_function(argc, argv, func, net_ads_group_usage); +} + +static int net_ads_status(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + void *res; + + if (!(ads = ads_startup())) return -1; + + rc = ads_find_machine_acct(ads, &res, lp_netbios_name()); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_find_machine_acct: %s\n", ads_errstr(rc)); + return -1; + } + + if (ads_count_replies(ads, res) == 0) { + d_printf("No machine account for '%s' found\n", lp_netbios_name()); + return -1; + } + + ads_dump(ads, res); + + return 0; +} + +static int net_ads_leave(int argc, const char **argv) +{ + ADS_STRUCT *ads = NULL; + ADS_STATUS rc; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + if (!opt_password) { + char *user_name; + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_password = secrets_fetch_machine_password(); + opt_user_name = user_name; + } + + if (!(ads = ads_startup())) { + return -1; + } + + rc = ads_leave_realm(ads, lp_netbios_name()); + if (!ADS_ERR_OK(rc)) { + d_printf("Failed to delete host '%s' from the '%s' realm.\n", + lp_netbios_name(), ads->config.realm); + return -1; + } + + d_printf("Removed '%s' from realm '%s'\n", lp_netbios_name(), ads->config.realm); + + return 0; +} + +static int net_ads_join_ok(void) +{ + char *user_name; + ADS_STRUCT *ads = NULL; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_user_name = user_name; + opt_password = secrets_fetch_machine_password(); + + if (!(ads = ads_startup())) { + return -1; + } + + ads_destroy(&ads); + return 0; +} + +/* + check that an existing join is OK + */ +int net_ads_testjoin(int argc, const char **argv) +{ + use_in_memory_ccache(); + + /* Display success or failure */ + if (net_ads_join_ok() != 0) { + fprintf(stderr,"Join to domain is not valid\n"); + return -1; + } + + printf("Join is OK\n"); + return 0; +} + +/* + join a domain using ADS + */ +int net_ads_join(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + char *password; + char *tmp_password; + const char *org_unit = "Computers"; + char *dn; + void *res; + DOM_SID dom_sid; + char *ou_str; + + if (argc > 0) org_unit = argv[0]; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + tmp_password = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH); + password = strdup(tmp_password); + + if (!(ads = ads_startup())) return -1; + + ou_str = ads_ou_string(org_unit); + asprintf(&dn, "%s,%s", ou_str, ads->config.bind_path); + free(ou_str); + + rc = ads_search_dn(ads, &res, dn, NULL); + ads_msgfree(ads, res); + + if (rc.error_type == ADS_ERROR_LDAP && rc.err.rc == LDAP_NO_SUCH_OBJECT) { + d_printf("ads_join_realm: organizational unit %s does not exist (dn:%s)\n", + org_unit, dn); + return -1; + } + free(dn); + + if (!ADS_ERR_OK(rc)) { + d_printf("ads_join_realm: %s\n", ads_errstr(rc)); + return -1; + } + + rc = ads_join_realm(ads, lp_netbios_name(), org_unit); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_join_realm: %s\n", ads_errstr(rc)); + return -1; + } + + rc = ads_domain_sid(ads, &dom_sid); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_domain_sid: %s\n", ads_errstr(rc)); + return -1; + } + + rc = ads_set_machine_password(ads, lp_netbios_name(), password); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_set_machine_password: %s\n", ads_errstr(rc)); + return -1; + } + + if (!secrets_store_domain_sid(lp_workgroup(), &dom_sid)) { + DEBUG(1,("Failed to save domain sid\n")); + return -1; + } + + if (!secrets_store_machine_password(password)) { + DEBUG(1,("Failed to save machine password\n")); + return -1; + } + + d_printf("Joined '%s' to realm '%s'\n", lp_netbios_name(), ads->config.realm); + + free(password); + + return 0; +} + +int net_ads_printer_usage(int argc, const char **argv) +{ + d_printf( +"\nnet ads printer info " +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish " +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove " +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n"); + return -1; +} + +static int net_ads_printer_info(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + const char *servername, *printername; + void *res = NULL; + + if (!(ads = ads_startup())) return -1; + + if (argc > 0) + printername = argv[0]; + else + printername = "*"; + + if (argc > 1) + servername = argv[1]; + else + servername = lp_netbios_name(); + + rc = ads_find_printer_on_server(ads, &res, printername, servername); + + if (!ADS_ERR_OK(rc)) { + d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc)); + ads_msgfree(ads, res); + return -1; + } + + if (ads_count_replies(ads, res) == 0) { + d_printf("Printer '%s' not found\n", printername); + ads_msgfree(ads, res); + return -1; + } + + ads_dump(ads, res); + ads_msgfree(ads, res); + + return 0; +} + +void do_drv_upgrade_printer(int msg_type, pid_t src, void *buf, size_t len) +{ + return; +} + +static int net_ads_printer_publish(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + const char *servername; + struct cli_state *cli; + struct in_addr server_ip; + 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; + void *res = NULL; + + if (!(ads = ads_startup())) return -1; + + if (argc < 1) + return net_ads_printer_usage(argc, argv); + + if (argc == 2) + servername = argv[1]; + else + servername = lp_netbios_name(); + + ads_find_machine_acct(ads, &res, servername); + srv_dn = ldap_get_dn(ads->ld, res); + srv_cn = ldap_explode_dn(srv_dn, 1); + asprintf(&prt_dn, "cn=%s-%s,%s", srv_cn[0], argv[0], srv_dn); + + resolve_name(servername, &server_ip, 0x20); + + nt_status = cli_full_connection(&cli, lp_netbios_name(), servername, + &server_ip, 0, + "IPC$", "IPC", + opt_user_name, opt_workgroup, + opt_password ? opt_password : "", + CLI_FULL_CONNECTION_USE_KERBEROS, + NULL); + + cli_nt_session_open(cli, PI_SPOOLSS); + get_remote_printer_publishing_data(cli, mem_ctx, &mods, argv[0]); + + rc = ads_add_printer_entry(ads, prt_dn, mem_ctx, &mods); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_publish_printer: %s\n", ads_errstr(rc)); + return -1; + } + + d_printf("published printer\n"); + + return 0; +} + +static int net_ads_printer_remove(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + const char *servername; + char *prt_dn; + void *res = NULL; + + if (!(ads = ads_startup())) return -1; + + if (argc < 1) + return net_ads_printer_usage(argc, argv); + + if (argc > 1) + servername = argv[1]; + else + servername = lp_netbios_name(); + + rc = ads_find_printer_on_server(ads, &res, argv[0], servername); + + if (!ADS_ERR_OK(rc)) { + d_printf("ads_find_printer_on_server: %s\n", ads_errstr(rc)); + ads_msgfree(ads, res); + return -1; + } + + if (ads_count_replies(ads, res) == 0) { + d_printf("Printer '%s' not found\n", argv[1]); + ads_msgfree(ads, res); + 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_printf("ads_del_dn: %s\n", ads_errstr(rc)); + return -1; + } + + return 0; +} + +static int net_ads_printer(int argc, const char **argv) +{ + struct functable func[] = { + {"INFO", net_ads_printer_info}, + {"PUBLISH", net_ads_printer_publish}, + {"REMOVE", net_ads_printer_remove}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_ads_printer_usage); +} + + +static int net_ads_password(int argc, const char **argv) +{ + ADS_STRUCT *ads; + const char *auth_principal = opt_user_name; + const char *auth_password = opt_password; + char *realm = NULL; + char *new_password = NULL; + char *c; + char *prompt; + ADS_STATUS ret; + + + if ((argc != 1) || (opt_user_name == NULL) || + (opt_password == NULL) || (strchr(opt_user_name, '@') == NULL) || + (strchr(argv[0], '@') == NULL)) { + return net_ads_usage(argc, argv); + } + + use_in_memory_ccache(); + c = strchr(auth_principal, '@'); + realm = ++c; + + /* use the realm so we can eventually change passwords for users + in realms other than default */ + if (!(ads = ads_init(realm, NULL, NULL))) return -1; + + asprintf(&prompt, "Enter new password for %s:", argv[0]); + + new_password = getpass(prompt); + + ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, + auth_password, argv[0], new_password, ads->auth.time_offset); + if (!ADS_ERR_OK(ret)) { + d_printf("Password change failed :-( ...\n"); + ads_destroy(&ads); + free(prompt); + return -1; + } + + d_printf("Password change for %s completed.\n", argv[0]); + ads_destroy(&ads); + free(prompt); + + return 0; +} + + +static int net_ads_change_localhost_pass(int argc, const char **argv) +{ + ADS_STRUCT *ads; + char *host_principal; + char *hostname; + ADS_STATUS ret; + char *user_name; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + asprintf(&user_name, "%s$", lp_netbios_name()); + opt_user_name = user_name; + + opt_password = secrets_fetch_machine_password(); + + use_in_memory_ccache(); + + if (!(ads = ads_startup())) { + return -1; + } + + hostname = strdup(lp_netbios_name()); + strlower(hostname); + asprintf(&host_principal, "%s@%s", hostname, ads->config.realm); + SAFE_FREE(hostname); + d_printf("Changing password for principal: HOST/%s\n", host_principal); + + ret = ads_change_trust_account_password(ads, host_principal); + + if (!ADS_ERR_OK(ret)) { + d_printf("Password change failed :-( ...\n"); + ads_destroy(&ads); + SAFE_FREE(host_principal); + return -1; + } + + d_printf("Password change for principal HOST/%s succeeded.\n", host_principal); + ads_destroy(&ads); + SAFE_FREE(host_principal); + + return 0; +} + +/* + help for net ads search +*/ +static int net_ads_search_usage(int argc, const char **argv) +{ + d_printf( + "\nnet ads search \n"\ + "\nperform a raw LDAP search on a ADS server and dump the results\n"\ + "The expression is a standard LDAP search expression, and the\n"\ + "attributes are a list of LDAP fields to show in the results\n\n"\ + "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n" + ); + net_common_flags_usage(argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_search(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + const char *exp; + const char **attrs; + void *res = NULL; + + if (argc < 1) { + return net_ads_search_usage(argc, argv); + } + + if (!(ads = ads_startup())) { + return -1; + } + + exp = argv[0]; + attrs = (argv + 1); + + rc = ads_do_search_all(ads, ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + exp, attrs, &res); + if (!ADS_ERR_OK(rc)) { + d_printf("search failed: %s\n", ads_errstr(rc)); + 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(int argc, const char **argv) +{ + d_printf( + "\nnet ads dn \n"\ + "\nperform a raw LDAP search on a ADS server and dump the results\n"\ + "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\ + "to show in the results\n\n"\ + "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n" + ); + net_common_flags_usage(argc, argv); + return -1; +} + + +/* + general ADS search function. Useful in diagnosing problems in ADS +*/ +static int net_ads_dn(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + const char *dn; + const char **attrs; + void *res = NULL; + + if (argc < 1) { + return net_ads_dn_usage(argc, argv); + } + + if (!(ads = ads_startup())) { + 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_printf("search failed: %s\n", ads_errstr(rc)); + 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; +} + + +int net_ads_help(int argc, const char **argv) +{ + struct functable func[] = { + {"USER", net_ads_user_usage}, + {"GROUP", net_ads_group_usage}, + {"PRINTER", net_ads_printer_usage}, + {"SEARCH", net_ads_search_usage}, +#if 0 + {"INFO", net_ads_info}, + {"JOIN", net_ads_join}, + {"LEAVE", net_ads_leave}, + {"STATUS", net_ads_status}, + {"PASSWORD", net_ads_password}, + {"CHOSTPASS", net_ads_change_localhost_pass}, +#endif + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_ads_usage); +} + +int net_ads(int argc, const char **argv) +{ + struct functable func[] = { + {"INFO", net_ads_info}, + {"JOIN", net_ads_join}, + {"TESTJOIN", net_ads_testjoin}, + {"LEAVE", net_ads_leave}, + {"STATUS", net_ads_status}, + {"USER", net_ads_user}, + {"GROUP", net_ads_group}, + {"PASSWORD", net_ads_password}, + {"CHOSTPASS", net_ads_change_localhost_pass}, + {"PRINTER", net_ads_printer}, + {"SEARCH", net_ads_search}, + {"DN", net_ads_dn}, + {"WORKGROUP", net_ads_workgroup}, + {"LOOKUP", net_ads_lookup}, + {"HELP", net_ads_help}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_ads_usage); +} + +#else + +static int net_ads_noads(void) +{ + d_printf("ADS support not compiled in\n"); + return -1; +} + +int net_ads_usage(int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_help(int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_join(int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_user(int argc, const char **argv) +{ + return net_ads_noads(); +} + +int net_ads_group(int argc, const char **argv) +{ + return net_ads_noads(); +} + +/* this one shouldn't display a message */ +int net_ads_check(void) +{ + return -1; +} + +int net_ads(int argc, const char **argv) +{ + return net_ads_usage(argc, argv); +} + +#endif diff --git a/source4/utils/net_ads_cldap.c b/source4/utils/net_ads_cldap.c new file mode 100644 index 0000000000..132238533d --- /dev/null +++ b/source4/utils/net_ads_cldap.c @@ -0,0 +1,354 @@ +/* + Samba Unix/Linux SMB client library + net ads cldap functions + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2003 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "../utils/net.h" + +#ifdef HAVE_ADS + +struct netlogon_string { + uint32 comp_len; + char **component; + uint8 extra_flag; +}; + +struct cldap_netlogon_reply { + uint32 type; + uint32 flags; + GUID guid; + + struct netlogon_string forest; + struct netlogon_string domain; + struct netlogon_string hostname; + + struct netlogon_string netbios_domain; + struct netlogon_string netbios_hostname; + + struct netlogon_string user_name; + struct netlogon_string site_name; + + struct netlogon_string unk0; + + uint32 version; + uint16 lmnt_token; + uint16 lm20_token; +}; + +/* + These strings are rather interesting... They are composed of a series of + length encoded strings, terminated by either 1) a zero length string or 2) + a 0xc0 byte with what appears to be a one byte flags immediately following. +*/ +static unsigned pull_netlogon_string(struct netlogon_string *ret,const char *d) +{ + char *p = (char *)d; + + ZERO_STRUCTP(ret); + + do { + unsigned len = (unsigned char)*p; + p++; + + if (len > 0 && len != 0xc0) { + ret->component = realloc(ret->component, + ++ret->comp_len * + sizeof(char *)); + + ret->component[ret->comp_len - 1] = + smb_xstrndup(p, len); + p += len; + } else { + if (len == 0xc0) { + ret->extra_flag = *p; + p++; + }; + break; + } + } while (1); + + return (p - d); +} + +/* + do a cldap netlogon query +*/ +static int send_cldap_netlogon(int sock, const char *domain, + const char *hostname, unsigned ntversion) +{ + ASN1_DATA data; + char ntver[4]; + + SIVAL(ntver, 0, ntversion); + + memset(&data, 0, sizeof(data)); + + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + asn1_write_Integer(&data, 4); + asn1_push_tag(&data, ASN1_APPLICATION(3)); + asn1_write_OctetString(&data, NULL, 0); + asn1_write_enumerated(&data, 0); + asn1_write_enumerated(&data, 0); + asn1_write_Integer(&data, 0); + asn1_write_Integer(&data, 0); + asn1_write_BOOLEAN2(&data, False); + asn1_push_tag(&data, ASN1_CONTEXT(0)); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "DnsDomain", 9); + asn1_write_OctetString(&data, domain, strlen(domain)); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "Host", 4); + asn1_write_OctetString(&data, hostname, strlen(hostname)); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "NtVer", 5); + asn1_write_OctetString(&data, ntver, 4); + asn1_pop_tag(&data); + + asn1_pop_tag(&data); + + asn1_push_tag(&data,ASN1_SEQUENCE(0)); + asn1_write_OctetString(&data, "NetLogon", 8); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + asn1_pop_tag(&data); + + if (data.has_error) { + d_printf("Failed to build cldap netlogon at offset %d\n", (int)data.ofs); + asn1_free(&data); + return -1; + } + + if (write(sock, data.data, data.length) != data.length) { + d_printf("failed to send cldap query (%s)\n", strerror(errno)); + } + + file_save("cldap_query.dat", data.data, data.length); + asn1_free(&data); + + return 0; +} + + +/* + receive a cldap netlogon reply +*/ +static int recv_cldap_netlogon(int sock, struct cldap_netlogon_reply *reply) +{ + int ret; + ASN1_DATA data; + DATA_BLOB blob; + DATA_BLOB os1, os2, os3; + uint32 i1; + char *p; + + blob = data_blob(NULL, 8192); + + ret = read(sock, blob.data, blob.length); + + if (ret <= 0) { + d_printf("no reply received to cldap netlogon\n"); + return -1; + } + blob.length = ret; + + file_save("cldap_reply.dat", blob.data, blob.length); + + asn1_load(&data, blob); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_read_Integer(&data, &i1); + asn1_start_tag(&data, ASN1_APPLICATION(4)); + asn1_read_OctetString(&data, &os1); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_start_tag(&data, ASN1_SEQUENCE(0)); + asn1_read_OctetString(&data, &os2); + asn1_start_tag(&data, ASN1_SET); + asn1_read_OctetString(&data, &os3); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + asn1_end_tag(&data); + + if (data.has_error) { + d_printf("Failed to parse cldap reply\n"); + return -1; + } + + file_save("cldap_reply_core.dat", os3.data, os3.length); + + p = os3.data; + + reply->type = IVAL(p, 0); p += 4; + reply->flags = IVAL(p, 0); p += 4; + + memcpy(&reply->guid.info, p, GUID_SIZE); + p += GUID_SIZE; + + p += pull_netlogon_string(&reply->forest, p); + p += pull_netlogon_string(&reply->domain, p); + p += pull_netlogon_string(&reply->hostname, p); + p += pull_netlogon_string(&reply->netbios_domain, p); + p += pull_netlogon_string(&reply->netbios_hostname, p); + p += pull_netlogon_string(&reply->user_name, p); + p += pull_netlogon_string(&reply->site_name, p); + + p += pull_netlogon_string(&reply->unk0, p); + + reply->version = IVAL(p, 0); + reply->lmnt_token = SVAL(p, 4); + reply->lm20_token = SVAL(p, 6); + + data_blob_free(&os1); + data_blob_free(&os2); + data_blob_free(&os3); + data_blob_free(&blob); + + return 0; +} + +/* + free a netlogon string +*/ +static void netlogon_string_free(struct netlogon_string *str) +{ + int i; + + for (i = 0; i < str->comp_len; ++i) { + SAFE_FREE(str->component[i]); + } + SAFE_FREE(str->component); +} + +/* + free a cldap reply packet +*/ +static void cldap_reply_free(struct cldap_netlogon_reply *reply) +{ + netlogon_string_free(&reply->forest); + netlogon_string_free(&reply->domain); + netlogon_string_free(&reply->hostname); + netlogon_string_free(&reply->netbios_domain); + netlogon_string_free(&reply->netbios_hostname); + netlogon_string_free(&reply->user_name); + netlogon_string_free(&reply->site_name); + netlogon_string_free(&reply->unk0); +} + +static void d_print_netlogon_string(const char *label, + struct netlogon_string *str) +{ + int i; + + if (str->comp_len) { + d_printf("%s", label); + if (str->extra_flag) { + d_printf("[%d]", str->extra_flag); + } + d_printf(": "); + for (i = 0; i < str->comp_len; ++i) { + d_printf("%s%s", (i ? "." : ""), str->component[i]); + } + d_printf("\n"); + } +} + +/* + do a cldap netlogon query +*/ +int ads_cldap_netlogon(ADS_STRUCT *ads) +{ + int sock; + int ret; + struct cldap_netlogon_reply reply; + + sock = open_udp_socket(inet_ntoa(ads->ldap_ip), ads->ldap_port); + if (sock == -1) { + d_printf("Failed to open udp socket to %s:%u\n", + inet_ntoa(ads->ldap_ip), + ads->ldap_port); + return -1; + } + + ret = send_cldap_netlogon(sock, ads->config.realm, lp_netbios_name(), 6); + if (ret != 0) { + return ret; + } + ret = recv_cldap_netlogon(sock, &reply); + close(sock); + + if (ret == -1) { + return -1; + } + + d_printf("Information for Domain Controller: %s\n\n", + ads->config.ldap_server_name); + + d_printf("Response Type: 0x%x\n", reply.type); + d_printf("GUID: "); + print_guid(&reply.guid); + 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", + (reply.flags & ADS_PDC) ? "yes" : "no", + (reply.flags & ADS_GC) ? "yes" : "no", + (reply.flags & ADS_LDAP) ? "yes" : "no", + (reply.flags & ADS_DS) ? "yes" : "no", + (reply.flags & ADS_KDC) ? "yes" : "no", + (reply.flags & ADS_TIMESERV) ? "yes" : "no", + (reply.flags & ADS_CLOSEST) ? "yes" : "no", + (reply.flags & ADS_WRITABLE) ? "yes" : "no", + (reply.flags & ADS_GOOD_TIMESERV) ? "yes" : "no", + (reply.flags & ADS_NDNC) ? "yes" : "no"); + + d_print_netlogon_string("Forest", &reply.forest); + d_print_netlogon_string("Domain", &reply.domain); + d_print_netlogon_string("Hostname", &reply.hostname); + + d_print_netlogon_string("Pre-Win2k Domain", &reply.netbios_domain); + d_print_netlogon_string("Pre-Win2k Hostname", &reply.netbios_hostname); + + d_print_netlogon_string("User name", &reply.user_name); + d_print_netlogon_string("Site Name", &reply.site_name); + d_print_netlogon_string("Unknown Field", &reply.unk0); + + d_printf("NT Version: %d\n", reply.version); + d_printf("LMNT Token: %.2x\n", reply.lmnt_token); + d_printf("LM20 Token: %.2x\n", reply.lm20_token); + + cldap_reply_free(&reply); + + return ret; +} + + +#endif diff --git a/source4/utils/net_cache.c b/source4/utils/net_cache.c new file mode 100644 index 0000000000..93c4f1aa1d --- /dev/null +++ b/source4/utils/net_cache.c @@ -0,0 +1,348 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#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; + 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 */ + memcpy(&timeout_tm, localtime(&timeout), sizeof(struct tm)); + now_tm = localtime(&now_t); + + /* 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); + timeout_str[strlen(timeout_str) - 1] = '\0'; /* remove tailing CR */ + } else + asprintf(&timeout_str, "%.2d:%.2d:%.2d", timeout_tm.tm_hour, + timeout_tm.tm_min, timeout_tm.tm_sec); + + d_printf("Key: %s\t Timeout: %s\t Value: %s %s\n", keystr, + timeout_str, datastr, timeout > now_t ? "": "(expired)"); +} + +static void delete_cache_entry(const char* keystr, const char* datastr, + const time_t timeout, void* dptr) +{ + if (!gencache_del(keystr)) + d_printf("Couldn't delete entry! key = %s", 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 = 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 argv key, value and timeout are passed in command line + * @return 0 on success, otherwise failure + **/ +static int net_cache_add(int argc, const char **argv) +{ + const char *keystr, *datastr, *timeout_str; + time_t timeout; + + if (argc < 3) { + d_printf("\nUsage: net cache add \n"); + return -1; + } + + keystr = argv[0]; + datastr = argv[1]; + timeout_str = argv[2]; + + /* parse timeout given in command line */ + timeout = parse_timeout(timeout_str); + if (!timeout) { + d_printf("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_printf("Entry couldn't be added. Perhaps there's already such a key.\n"); + gencache_shutdown(); + return -1; +} + + +/** + * Set new value of an existing entry in the cache. Fail If the entry doesn't + * exist. + * + * @param argv key being searched and new value and timeout to set in the entry + * @return 0 on success, otherwise failure + **/ +static int net_cache_set(int argc, const char **argv) +{ + const char *keystr, *datastr, *timeout_str; + time_t timeout; + + if (argc < 3) { + d_printf("\nUsage: net cache set \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_printf("Invalid timeout argument.\n"); + return -1; + } + + if (gencache_set_only(keystr, datastr, timeout)) { + d_printf("Cache entry set successfully.\n"); + gencache_shutdown(); + return 0; + } + + d_printf("Entry couldn't be set. Perhaps there's no such a key.\n"); + gencache_shutdown(); + return -1; +} + + +/** + * Delete an entry in the cache + * + * @param argv key to delete an entry of + * @return 0 on success, otherwise failure + **/ +static int net_cache_del(int argc, const char **argv) +{ + const char *keystr = argv[0]; + + if (argc < 1) { + d_printf("\nUsage: net cache add \n"); + return -1; + } + + if(gencache_del(keystr)) { + d_printf("Entry deleted.\n"); + return 0; + } + + d_printf("Couldn't delete specified entry\n"); + return -1; +} + + +/** + * Get and display an entry from the cache + * + * @param argv key to search an entry of + * @return 0 on success, otherwise failure + **/ +static int net_cache_get(int argc, const char **argv) +{ + const char* keystr = argv[0]; + char* valuestr; + time_t timeout; + + if (argc < 1) { + d_printf("\nUsage: net cache get \n"); + return -1; + } + + if (gencache_get(keystr, &valuestr, &timeout)) { + print_cache_entry(keystr, valuestr, timeout, NULL); + return 0; + } + + d_printf("Failed to find entry\n"); + return -1; +} + + +/** + * Search an entry/entries in the cache + * + * @param argv key pattern to match the entries to + * @return 0 on success, otherwise failure + **/ +static int net_cache_search(int argc, const char **argv) +{ + const char* pattern; + + if (argc < 1) { + d_printf("Usage: net cache search \n"); + return -1; + } + + pattern = argv[0]; + gencache_iterate(print_cache_entry, NULL, pattern); + return 0; +} + + +/** + * List the contents of the cache + * + * @param argv ignored in this functionailty + * @return always returns 0 + **/ +static int net_cache_list(int argc, const char **argv) +{ + const char* pattern = "*"; + gencache_iterate(print_cache_entry, NULL, pattern); + gencache_shutdown(); + return 0; +} + + +/** + * Flush the whole cache + * + * @param argv ignored in this functionality + * @return always returns 0 + **/ +static int net_cache_flush(int argc, const char **argv) +{ + const char* pattern = "*"; + gencache_iterate(delete_cache_entry, NULL, pattern); + gencache_shutdown(); + return 0; +} + + +/** + * Short help + * + * @param argv ignored in this functionality + * @return always returns -1 + **/ +static int net_cache_usage(int argc, const char **argv) +{ + d_printf(" net cache add \t add add new cache entry\n"); + d_printf(" net cache set \t set new value for existing cache entry\n"); + d_printf(" net cache del \t delete existing cache entry by key\n"); + d_printf(" net cache flush \t delete all entries existing in the cache\n"); + d_printf(" net cache get \t get cache entry by key\n"); + d_printf(" net cache search \t search for entries in the cache, by given pattern\n"); + d_printf(" net cache list \t list all cache entries (just like search for \"*\")\n"); + return -1; +} + + +/** + * Entry point to 'net cache' subfunctionality + * + * @param argv arguments passed to further called functions + * @return whatever further functions return + **/ +int net_cache(int argc, const char **argv) +{ + struct functable func[] = { + {"add", net_cache_add}, + {"set", net_cache_set}, + {"del", net_cache_del}, + {"get", net_cache_get}, + {"search", net_cache_search}, + {"list", net_cache_list}, + {"flush", net_cache_flush}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_cache_usage); +} diff --git a/source4/utils/net_help.c b/source4/utils/net_help.c new file mode 100644 index 0000000000..4000a248ff --- /dev/null +++ b/source4/utils/net_help.c @@ -0,0 +1,199 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include "includes.h" +#include "../utils/net.h" + +int net_common_methods_usage(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(int argc, const char **argv) +{ + d_printf("Valid targets: choose one (none defaults to localhost)\n"); + d_printf("\t-S or --server=\t\tserver name\n"); + d_printf("\t-I or --ipaddress=\taddress of target server\n"); + d_printf("\t-w or --workgroup=\t\ttarget workgroup or domain\n"); + + d_printf("\n"); + d_printf("Valid miscellaneous options are:\n"); /* misc options */ + d_printf("\t-p or --port=\t\tconnection port on target\n"); + d_printf("\t-W or --myworkgroup=\tclient workgroup\n"); + d_printf("\t-d or --debug=\t\tdebug level (0-10)\n"); + d_printf("\t-n or --myname=\t\tclient name\n"); + d_printf("\t-U or --user=\t\tuser name\n"); + d_printf("\t-s or --conf=\t\tpathname of smb.conf file\n"); + d_printf("\t-l or --long\t\t\tDisplay full information\n"); + d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n"); + return -1; +} + +static int help_usage(int argc, const char **argv) +{ + d_printf( +"\n"\ +"Usage: net help \n"\ +"\n"\ +"Valid functions are:\n"\ +" RPC RAP ADS FILE SHARE SESSION SERVER DOMAIN PRINTQ USER GROUP VALIDATE\n"\ +" GROUPMEMBER ADMIN SERVICE PASSWORD TIME LOOKUP GETLOCALSID SETLOCALSID\n"); + return -1; +} + +int net_help_user(int argc, const char **argv) +{ + d_printf("\nnet [] user [misc. options] [targets]"\ + "\n\tList users\n\n"); + d_printf("net [] user DELETE [misc. options] [targets]"\ + "\n\tDelete specified user\n"); + d_printf("\nnet [] user INFO [misc. options] [targets]"\ + "\n\tList the domain groups of the specified user\n"); + d_printf("\nnet [] user ADD [password] [-c container] "\ + "[-F user flags] [misc. options]"\ + " [targets]\n\tAdd specified user\n"); + + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf("\t-C or --comment=\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --container=\tLDAP container, defaults to cn=Users (for add in ADS only)\n"); + return -1; +} + +int net_help_group(int argc, const char **argv) +{ + d_printf("net [] group [misc. options] [targets]"\ + "\n\tList user groups\n\n"); + d_printf("net [] group DELETE "\ + "[misc. options] [targets]"\ + "\n\tDelete specified group\n"); + d_printf("\nnet [] group ADD [-C comment] [-c container]"\ + " [misc. options] [targets]\n\tCreate specified group\n"); + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf("\t-C or --comment=\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --container=\tLDAP container, defaults to cn=Users (for add in ADS only)\n"); + return -1; +} + + +int net_help_join(int argc, const char **argv) +{ + d_printf("\nnet [] join [misc. options]\n" + "\tjoins this server to a domain\n"); + d_printf("Valid methods: (auto-detected if not specified)\n"); + d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n"); + d_printf("\trpc\t\t\t\tDCE-RPC\n"); + net_common_flags_usage(argc, argv); + return -1; +} + +int net_help_share(int argc, const char **argv) +{ + d_printf( + "\nnet [] share [misc. options] [targets] \n" + "\tenumerates all exported resources (network shares) " + "on target server\n\n" + "net [] share ADD [misc. options] [targets]" + "\n\tAdds a share from a server (makes the export active)\n\n" + "net [] share DELETE [misc. options] [targets]\n" + "\n\tDeletes a share from a server (makes the export inactive)\n"); + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf( + "\t-C or --comment=\tdescriptive comment (for add only)\n" + "\t-M or --maxusers=\t\tmax users allowed for share\n"); + return -1; +} + +int net_help_file(int argc, const char **argv) +{ + d_printf("net [] file [misc. options] [targets]\n"\ + "\tlists all open files on file server\n\n"); + d_printf("net [] file USER "\ + "[misc. options] [targets]"\ + "\n\tlists all files opened by username on file server\n\n"); + d_printf("net [] file CLOSE [misc. options] [targets]\n"\ + "\tcloses specified file on target server\n\n"); + d_printf("net [rap] file INFO [misc. options] [targets]\n"\ + "\tdisplays information about the specified open file\n"); + + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + return -1; +} + +static int net_usage(int argc, const char **argv) +{ + d_printf(" net time\t\tto view or set time information\n"\ + " net lookup\t\tto lookup host name or ip address\n"\ + " net user\t\tto manage users\n"\ + " net group\t\tto manage groups\n"\ + " net join\t\tto join a domain\n"\ + " net cache\t\tto operate on cache tdb file\n"\ + " net getlocalsid [NAME]\tto get the SID for local name\n"\ + " net setlocalsid SID\tto set the local domain SID\n"\ + "\n"\ + " net ads \tto run ADS commands\n"\ + " net rap \tto run RAP (pre-RPC) commands\n"\ + " net rpc \tto run RPC commands\n"\ + "\n"\ + "Type \"net help