diff options
Diffstat (limited to 'source4/utils')
39 files changed, 18336 insertions, 0 deletions
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 <PRE></PRE> block. + * + * ------------------------------------------------------------------------ ** + */ + { + switch( new ) + { + case dbg_null: + case dbg_ignore: + return( mode ); + case dbg_message: + if( dbg_message != mode ) + { + /* Switching to message mode. */ + (void)printf( "<PRE>\n" ); + return( dbg_message ); + } + break; + default: + if( dbg_message == mode ) + { + /* Switching out of message mode. */ + (void)printf( "</PRE>\n\n" ); + return( dbg_null ); + } + } + + return( mode ); + } /* modechange */ + +static void newblock( dbg_Token old, dbg_Token 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( ",</B>" ); + break; + case dbg_level: + (void)printf( "</FONT>]</B>\n " ); + break; + case dbg_sourcefile: + (void)printf( ":" ); + break; + case dbg_lineno: + (void)printf( ")" ); + break; + } + + switch( new ) + { + case dbg_timestamp: + (void)printf( "<B>[" ); + break; + case dbg_level: + (void)printf( " <B><FONT COLOR=MAROON>" ); + 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( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2//EN\">\n" ); + (void)printf( "<HTML>\n<HEAD>\n" ); + (void)printf( " <TITLE>Samba Debug Output</TITLE>\n</HEAD>\n\n<BODY>\n" ); + + while( (!feof( stdin )) + && ((len = fread( bufr, 1, DBG_BSIZE, stdin )) > 0) ) + { + for( i = 0; i < len; i++ ) + { + old = 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( "</BODY>\n</HTML>\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 <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> +#include <string.h> +#include <fcntl.h> + +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; i<val_list->val_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; i<vl->val_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; i<key_list->key_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; i<acl->num_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<len)&&(i+1)*3<ascii_max; i++) { + int str_rem = ascii_max - ((int)asciip - (int)ascii); + asciip += snprintf(asciip, str_rem, "%02x", *(unsigned char *)(datap+i)); + if (i < len && str_rem > 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; i<tmp->auths&&i<MAXSUBAUTHS; i++) { + tmp->sub_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; i<num_aces; i++) { + tmp->aces[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, "<No 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; i<count; i++) { + vk_off = IVAL(&vl[i]); + vk_hdr = (VK_HDR *)LOCN(regf->base, 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; i<count; i++) { + NK_HDR *nk_hdr; + + nk_off = IVAL(&lf_hdr->hr[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 + * <value-spec>* + * + * 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:"<No Name>"), + val_to_str(val_type, reg_type_names), data_asc); + return 1; +} + +void usage(void) +{ + fprintf(stderr, "Usage: editreg [-v] [-k] [-c <command-file>] <registryfile>\n"); + fprintf(stderr, "Version: 0.1\n\n"); + fprintf(stderr, "\n\t-v\t sets verbose mode"); + fprintf(stderr, "\n\t-c <command-file>\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<argc; i++) { + if (argv_new[i] == NULL) { + argc_new = i; + break; + } + } + + if (!opt_requester_name) { + opt_requester_name = get_myname(); + } + + if (!opt_user_name && getenv("LOGNAME")) { + opt_user_name = getenv("LOGNAME"); + } + + if (!opt_workgroup) { + opt_workgroup = lp_workgroup(); + } + + if (!opt_target_workgroup) { + opt_target_workgroup = strdup(lp_workgroup()); + } + + if (!init_names()) + exit(1); + + load_interfaces(); + + if (opt_machine_pass) { + char *user; + /* it is very useful to be able to make ads queries as the + machine account for testing purposes and for domain leave */ + + if (!secrets_init()) { + d_printf("ERROR: Unable to open secrets database\n"); + exit(1); + } + + asprintf(&user,"%s$", lp_netbios_name()); + opt_user_name = user; + opt_password = secrets_fetch_machine_password(); + if (!opt_password) { + d_printf("ERROR: Unable to fetch machine password\n"); + exit(1); + } + } + + rc = net_run_function(argc_new-1, argv_new+1, net_func, net_help); + + DEBUG(2,("return code = %d\n", rc)); + return rc; +} diff --git a/source4/utils/net.h b/source4/utils/net.h new file mode 100644 index 0000000000..c1b49a919b --- /dev/null +++ b/source4/utils/net.h @@ -0,0 +1,61 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 "../utils/net_proto.h" + +#define NET_FLAGS_MASTER 1 +#define NET_FLAGS_DMB 2 + +/* Would it be insane to set 'localhost' as the default + remote host for this operation? + + For example, localhost is insane for a 'join' operation. +*/ +#define NET_FLAGS_LOCALHOST_DEFAULT_INSANE 4 + +/* We want to find the PDC only */ +#define NET_FLAGS_PDC 8 + +/* We want an anonymous connection */ +#define NET_FLAGS_ANONYMOUS 16 + + +extern int opt_maxusers; +extern const char *opt_comment; +extern char *opt_container; +extern int opt_flags; + +extern const char *opt_comment; + +extern const char *opt_target_workgroup; +extern const char *opt_workgroup; +extern int opt_long_list_entries; +extern int opt_reboot; +extern int opt_force; +extern int opt_timeout; +extern const char *opt_host; +extern const char *opt_user_name; +extern const char *opt_password; +extern BOOL opt_user_specified; + +extern BOOL opt_have_ip; +extern struct in_addr opt_dest_ip; + +extern const char *share_type[]; + diff --git a/source4/utils/net_ads.c b/source4/utils/net_ads.c new file mode 100644 index 0000000000..6fb6394764 --- /dev/null +++ b/source4/utils/net_ads.c @@ -0,0 +1,1176 @@ +/* + Samba Unix/Linux SMB client library + net ads commands + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Remus Koos (remuskoos@yahoo.com) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + + This program is free software; you can 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 + +int net_ads_usage(int argc, const char **argv) +{ + d_printf( +"\nnet ads join <org_unit>"\ +"\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 <username@realm> -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] <printername> <servername>"\ +"\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 <printer> <server>" +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish <printername>" +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove <printername>" +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n"); + return -1; +} + +static int net_ads_printer_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 <expression> <attributes...>\n"\ + "\nperform a raw LDAP search on a ADS server and dump the results\n"\ + "The expression is a standard LDAP search expression, and the\n"\ + "attributes are a list of LDAP fields to show in the results\n\n"\ + "Example: net ads search '(objectCategory=group)' sAMAccountName\n\n" + ); + net_common_flags_usage(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 <dn> <attributes...>\n"\ + "\nperform a raw LDAP search on a ADS server and dump the results\n"\ + "The DN standard LDAP DN, and the attributes are a list of LDAP fields \n"\ + "to show in the results\n\n"\ + "Example: net ads dn 'CN=administrator,CN=Users,DC=my,DC=domain' sAMAccountName\n\n" + ); + 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 <key string> <data string> <timeout>\n"); + return -1; + } + + keystr = argv[0]; + datastr = argv[1]; + timeout_str = argv[2]; + + /* parse timeout given in command line */ + timeout = parse_timeout(timeout_str); + if (!timeout) { + d_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 <key string> <data string> <timeout>\n"); + return -1; + } + + keystr = argv[0]; + datastr = argv[1]; + timeout_str = argv[2]; + + /* parse timeout given in command line */ + timeout = parse_timeout(timeout_str); + if (!timeout) { + d_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 <key string>\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 <key>\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 <pattern>\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=<server>\t\tserver name\n"); + d_printf("\t-I or --ipaddress=<ipaddr>\taddress of target server\n"); + d_printf("\t-w or --workgroup=<wg>\t\ttarget workgroup or domain\n"); + + d_printf("\n"); + d_printf("Valid miscellaneous options are:\n"); /* misc options */ + d_printf("\t-p or --port=<port>\t\tconnection port on target\n"); + d_printf("\t-W or --myworkgroup=<wg>\tclient workgroup\n"); + d_printf("\t-d or --debug=<level>\t\tdebug level (0-10)\n"); + d_printf("\t-n or --myname=<name>\t\tclient name\n"); + d_printf("\t-U or --user=<name>\t\tuser name\n"); + d_printf("\t-s or --conf=<path>\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 <function>\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 [<method>] user [misc. options] [targets]"\ + "\n\tList users\n\n"); + d_printf("net [<method>] user DELETE <name> [misc. options] [targets]"\ + "\n\tDelete specified user\n"); + d_printf("\nnet [<method>] user INFO <name> [misc. options] [targets]"\ + "\n\tList the domain groups of the specified user\n"); + d_printf("\nnet [<method>] user ADD <name> [password] [-c container] "\ + "[-F user flags] [misc. options]"\ + " [targets]\n\tAdd specified user\n"); + + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n"); + return -1; +} + +int net_help_group(int argc, const char **argv) +{ + d_printf("net [<method>] group [misc. options] [targets]"\ + "\n\tList user groups\n\n"); + d_printf("net [<method>] group DELETE <name> "\ + "[misc. options] [targets]"\ + "\n\tDelete specified group\n"); + d_printf("\nnet [<method>] group ADD <name> [-C comment] [-c container]"\ + " [misc. options] [targets]\n\tCreate specified group\n"); + net_common_methods_usage(argc, argv); + net_common_flags_usage(argc, argv); + d_printf("\t-C or --comment=<comment>\tdescriptive comment (for add only)\n"); + d_printf("\t-c or --container=<container>\tLDAP container, defaults to cn=Users (for add in ADS only)\n"); + return -1; +} + + +int net_help_join(int argc, const char **argv) +{ + d_printf("\nnet [<method>] join [misc. options]\n" + "\tjoins this server to a domain\n"); + d_printf("Valid methods: (auto-detected if not specified)\n"); + d_printf("\tads\t\t\t\tActive Directory (LDAP/Kerberos)\n"); + d_printf("\trpc\t\t\t\tDCE-RPC\n"); + net_common_flags_usage(argc, argv); + return -1; +} + +int net_help_share(int argc, const char **argv) +{ + d_printf( + "\nnet [<method>] share [misc. options] [targets] \n" + "\tenumerates all exported resources (network shares) " + "on target server\n\n" + "net [<method>] share ADD <name=serverpath> [misc. options] [targets]" + "\n\tAdds a share from a server (makes the export active)\n\n" + "net [<method>] share DELETE <sharename> [misc. options] [targets]\n" + "\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=<comment>\tdescriptive comment (for add only)\n" + "\t-M or --maxusers=<num>\t\tmax users allowed for share\n"); + return -1; +} + +int net_help_file(int argc, const char **argv) +{ + d_printf("net [<method>] file [misc. options] [targets]\n"\ + "\tlists all open files on file server\n\n"); + d_printf("net [<method>] file USER <username> "\ + "[misc. options] [targets]"\ + "\n\tlists all files opened by username on file server\n\n"); + d_printf("net [<method>] file CLOSE <id> [misc. options] [targets]\n"\ + "\tcloses specified file on target server\n\n"); + d_printf("net [rap] file INFO <id> [misc. options] [targets]\n"\ + "\tdisplays information about the specified open file\n"); + + net_common_methods_usage(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 <command>\tto run ADS commands\n"\ + " net rap <command>\tto run RAP (pre-RPC) commands\n"\ + " net rpc <command>\tto run RPC commands\n"\ + "\n"\ + "Type \"net help <option>\" to get more information on that option\n"); + net_common_flags_usage(argc, argv); + return -1; +} + +/* + handle "net help *" subcommands +*/ +int net_help(int argc, const char **argv) +{ + struct functable func[] = { + {"ADS", net_ads_help}, + {"RAP", net_rap_help}, + {"RPC", net_rpc_help}, + + {"FILE", net_help_file}, + {"SHARE", net_help_share}, + {"SESSION", net_rap_session_usage}, + {"SERVER", net_rap_server_usage}, + {"DOMAIN", net_rap_domain_usage}, + {"PRINTQ", net_rap_printq_usage}, + {"USER", net_help_user}, + {"GROUP", net_help_group}, + {"JOIN", net_help_join}, + {"VALIDATE", net_rap_validate_usage}, + {"GROUPMEMBER", net_rap_groupmember_usage}, + {"ADMIN", net_rap_admin_usage}, + {"SERVICE", net_rap_service_usage}, + {"PASSWORD", net_rap_password_usage}, + {"TIME", net_time_usage}, + {"LOOKUP", net_lookup_usage}, + + {"HELP", help_usage}, + {NULL, NULL}}; + + return net_run_function(argc, argv, func, net_usage); +} diff --git a/source4/utils/net_lookup.c b/source4/utils/net_lookup.c new file mode 100644 index 0000000000..271094480c --- /dev/null +++ b/source4/utils/net_lookup.c @@ -0,0 +1,234 @@ +/* + Samba Unix/Linux SMB client library + net lookup command + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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_lookup_usage(int argc, const char **argv) +{ + d_printf( +" net lookup host HOSTNAME <type>\n\tgives IP for a hostname\n\n" +" net lookup ldap [domain]\n\tgives IP of domain's ldap server\n\n" +" net lookup kdc [realm]\n\tgives IP of realm's kerberos KDC\n\n" +" net lookup dc [domain]\n\tgives IP of domains Domain Controllers\n\n" +" net lookup master [domain|wg]\n\tgive IP of master browser\n\n" +); + return -1; +} + +/* lookup a hostname giving an IP */ +static int net_lookup_host(int argc, const char **argv) +{ + struct in_addr ip; + int name_type = 0x20; + + if (argc == 0) return net_lookup_usage(argc, argv); + if (argc > 1) name_type = strtol(argv[1], NULL, 0); + + if (!resolve_name(argv[0], &ip, name_type)) { + /* we deliberately use DEBUG() here to send it to stderr + so scripts aren't mucked up */ + DEBUG(0,("Didn't find %s#%02x\n", argv[0], name_type)); + return -1; + } + + d_printf("%s\n", inet_ntoa(ip)); + return 0; +} + +static void print_ldap_srvlist(char *srvlist) +{ + char *cur, *next; + struct in_addr ip; + BOOL printit; + + cur = srvlist; + do { + next = strchr(cur,':'); + if (next) *next++='\0'; + printit = resolve_name(cur, &ip, 0x20); + cur=next; + next=cur ? strchr(cur,' ') :NULL; + if (next) + *next++='\0'; + if (printit) + d_printf("%s:%s\n", inet_ntoa(ip), cur?cur:""); + cur = next; + } while (next); +} + + +static int net_lookup_ldap(int argc, const char **argv) +{ +#ifdef HAVE_LDAP + char *srvlist; + const char *domain; + int rc; + struct in_addr addr; + struct hostent *hostent; + + if (argc > 0) + domain = argv[0]; + else + domain = opt_target_workgroup; + + DEBUG(9, ("Lookup up ldap for domain %s\n", domain)); + rc = ldap_domain2hostlist(domain, &srvlist); + if ((rc == LDAP_SUCCESS) && srvlist) { + print_ldap_srvlist(srvlist); + return 0; + } + + DEBUG(9, ("Looking up DC for domain %s\n", domain)); + if (!get_pdc_ip(domain, &addr)) + return -1; + + hostent = gethostbyaddr((char *) &addr.s_addr, sizeof(addr.s_addr), + AF_INET); + if (!hostent) + return -1; + + DEBUG(9, ("Found DC with DNS name %s\n", hostent->h_name)); + domain = strchr(hostent->h_name, '.'); + if (!domain) + return -1; + domain++; + + DEBUG(9, ("Looking up ldap for domain %s\n", domain)); + rc = ldap_domain2hostlist(domain, &srvlist); + if ((rc == LDAP_SUCCESS) && srvlist) { + print_ldap_srvlist(srvlist); + return 0; + } + return -1; +#endif + DEBUG(1,("No LDAP support\n")); + return -1; +} + +static int net_lookup_dc(int argc, const char **argv) +{ + struct in_addr *ip_list, addr; + char *pdc_str = NULL; + const char *domain=opt_target_workgroup; + int count, i; + BOOL list_ordered; + + if (argc > 0) + domain=argv[0]; + + /* first get PDC */ + if (!get_pdc_ip(domain, &addr)) + return -1; + + asprintf(&pdc_str, "%s", inet_ntoa(addr)); + d_printf("%s\n", pdc_str); + + if (!get_dc_list(domain, &ip_list, &count, &list_ordered)) { + SAFE_FREE(pdc_str); + return 0; + } + for (i=0;i<count;i++) { + char *dc_str = inet_ntoa(ip_list[i]); + if (!strequal(pdc_str, dc_str)) + d_printf("%s\n", dc_str); + } + SAFE_FREE(pdc_str); + return 0; +} + +static int net_lookup_master(int argc, const char **argv) +{ + struct in_addr master_ip; + const char *domain=opt_target_workgroup; + + if (argc > 0) + domain=argv[0]; + + if (!find_master_ip(domain, &master_ip)) + return -1; + d_printf("%s\n", inet_ntoa(master_ip)); + return 0; +} + +static int net_lookup_kdc(int argc, const char **argv) +{ +#ifdef HAVE_KRB5 + krb5_error_code rc; + krb5_context ctx; + struct sockaddr_in *addrs; + int num_kdcs,i; + krb5_data realm; + char **realms; + + rc = krb5_init_context(&ctx); + if (rc) { + DEBUG(1,("krb5_init_context failed (%s)\n", + error_message(rc))); + return -1; + } + + if (argc>0) { + realm.data = (krb5_pointer) argv[0]; + realm.length = strlen(argv[0]); + } else if (lp_realm() && *lp_realm()) { + realm.data = (krb5_pointer) lp_realm(); + realm.length = strlen(realm.data); + } else { + rc = krb5_get_host_realm(ctx, NULL, &realms); + if (rc) { + DEBUG(1,("krb5_gethost_realm failed (%s)\n", + error_message(rc))); + return -1; + } + realm.data = (krb5_pointer) *realms; + realm.length = strlen(realm.data); + } + + rc = krb5_locate_kdc(ctx, &realm, &addrs, &num_kdcs, 0); + if (rc) { + DEBUG(1, ("krb5_locate_kdc failed (%s)\n", error_message(rc))); + return -1; + } + for (i=0;i<num_kdcs;i++) + if (addrs[i].sin_family == AF_INET) + d_printf("%s:%hd\n", inet_ntoa(addrs[i].sin_addr), + ntohs(addrs[i].sin_port)); + return 0; + +#endif + DEBUG(1, ("No kerberos support\n")); + return -1; +} + + +/* lookup hosts or IP addresses using internal samba lookup fns */ +int net_lookup(int argc, const char **argv) +{ + struct functable func[] = { + {"HOST", net_lookup_host}, + {"LDAP", net_lookup_ldap}, + {"DC", net_lookup_dc}, + {"MASTER", net_lookup_master}, + {"KDC", net_lookup_kdc}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_lookup_usage); +} diff --git a/source4/utils/net_rap.c b/source4/utils/net_rap.c new file mode 100644 index 0000000000..8f3dd53fa6 --- /dev/null +++ b/source4/utils/net_rap.c @@ -0,0 +1,1051 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Steve French (sfrench@us.ibm.com) + Copyright (C) 2001 Jim McDonough (jmcd@us.ibm.com) + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + + Originally written by Steve and Jim. Largely rewritten by tridge in + November 2001. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +/* The following messages were for error checking that is not properly + reported at the moment. Which should be reinstated? */ +#define ERRMSG_TARGET_WG_NOT_VALID "\nTarget workgroup option not valid "\ + "except on net rap server command, ignored" +#define ERRMSG_INVALID_HELP_OPTION "\nInvalid help option\n" + +#define ERRMSG_BOTH_SERVER_IPADDRESS "\nTarget server and IP address both "\ + "specified. Do not set both at the same time. The target IP address was used\n" + +const char *share_type[] = { + "Disk", + "Print", + "Dev", + "IPC" +}; + +static int errmsg_not_implemented(void) +{ + d_printf("\nNot implemented\n"); + return 0; +} + +int net_rap_file_usage(int argc, const char **argv) +{ + return net_help_file(argc, argv); +} + +/*************************************************************************** + list info on an open file +***************************************************************************/ +static void file_fn(const char * pPath, const char * pUser, uint16 perms, + uint16 locks, uint32 id) +{ + d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n", + id, pUser, perms, locks, pPath); +} + +static void one_file_fn(const char *pPath, const char *pUser, uint16 perms, + uint16 locks, uint32 id) +{ + d_printf("File ID %d\n"\ + "User name %s\n"\ + "Locks 0x%-4.2x\n"\ + "Path %s\n"\ + "Permissions 0x%x\n", + id, pUser, locks, pPath, perms); +} + + +static int rap_file_close(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0) { + d_printf("\nMissing fileid of file to close\n\n"); + return net_rap_file_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetFileClose(cli, atoi(argv[0])); + cli_shutdown(cli); + return ret; +} + +static int rap_file_info(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0) + return net_rap_file_usage(argc, argv); + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetFileGetInfo(cli, atoi(argv[0]), one_file_fn); + cli_shutdown(cli); + return ret; +} + +static int rap_file_user(int argc, const char **argv) +{ + if (argc == 0) + return net_rap_file_usage(argc, argv); + + d_printf("net rap file user not implemented yet\n"); + return -1; +} + +int net_rap_file(int argc, const char **argv) +{ + struct functable func[] = { + {"CLOSE", rap_file_close}, + {"USER", rap_file_user}, + {"INFO", rap_file_info}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + /* list open files */ + d_printf( + "\nEnumerating open files on remote server:\n\n"\ + "\nFileId Opened by Perms Locks Path \n"\ + "------ --------- ----- ----- ---- \n"); + ret = cli_NetFileEnum(cli, NULL, NULL, file_fn); + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_file_usage); +} + +int net_rap_share_usage(int argc, const char **argv) +{ + return net_help_share(argc, argv); +} + +static void long_share_fn(const char *share_name, uint32 type, + const char *comment, void *state) +{ + d_printf("%-12.12s %-8.8s %-50.50s\n", + share_name, share_type[type], comment); +} + +static void share_fn(const char *share_name, uint32 type, + const char *comment, void *state) +{ + d_printf("%-12.12s\n", share_name); +} + +static int rap_share_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0) { + d_printf("\n\nShare name not specified\n"); + return net_rap_share_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetShareDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_share_add(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + RAP_SHARE_INFO_2 sinfo; + char *p; + char *sharename; + + if (argc == 0) { + d_printf("\n\nShare name not specified\n"); + return net_rap_share_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + sharename = strdup(argv[0]); + p = strchr(sharename, '='); + *p = 0; + strlcpy(sinfo.share_name, sharename, sizeof(sinfo.share_name)); + sinfo.reserved1 = '\0'; + sinfo.share_type = 0; + sinfo.comment = smb_xstrdup(opt_comment); + sinfo.perms = 0; + sinfo.maximum_users = opt_maxusers; + sinfo.active_users = 0; + sinfo.path = p+1; + memset(sinfo.password, '\0', sizeof(sinfo.password)); + sinfo.reserved2 = '\0'; + + ret = cli_NetShareAdd(cli, &sinfo); + cli_shutdown(cli); + return ret; +} + + +int net_rap_share(int argc, const char **argv) +{ + struct functable func[] = { + {"DELETE", rap_share_delete}, + {"CLOSE", rap_share_delete}, + {"ADD", rap_share_add}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + if (opt_long_list_entries) { + d_printf( + "\nEnumerating shared resources (exports) on remote server:\n\n"\ + "\nShare name Type Description\n"\ + "---------- ---- -----------\n"); + ret = cli_RNetShareEnum(cli, long_share_fn, NULL); + } + ret = cli_RNetShareEnum(cli, share_fn, NULL); + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_share_usage); +} + + +int net_rap_session_usage(int argc, const char **argv) +{ + d_printf( + "\nnet rap session [misc. options] [targets]"\ + "\n\tenumerates all active SMB/CIFS sessions on target server\n"); + d_printf( + "\nnet rap session DELETE <client_name> [misc. options] [targets] \n"\ + "\tor"\ + "\nnet rap session CLOSE <client_name> [misc. options] [targets]"\ + "\n\tDeletes (closes) a session from specified client to server\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +static void list_sessions_func(char *wsname, char *username, uint16 conns, + uint16 opens, uint16 users, uint32 sess_time, + uint32 idle_time, uint32 user_flags, char *clitype) +{ + int hrs = idle_time / 3600; + int min = (idle_time / 60) % 60; + int sec = idle_time % 60; + + d_printf("\\\\%-18.18s %-20.20s %-18.18s %5d %2.2d:%2.2d:%2.2d\n", + wsname, username, clitype, opens, hrs, min, sec); +} + +static void display_session_func(const char *wsname, const char *username, + uint16 conns, uint16 opens, uint16 users, + uint32 sess_time, uint32 idle_time, + uint32 user_flags, const char *clitype) +{ + int ihrs = idle_time / 3600; + int imin = (idle_time / 60) % 60; + int isec = idle_time % 60; + int shrs = sess_time / 3600; + int smin = (sess_time / 60) % 60; + int ssec = sess_time % 60; + d_printf("User name %-20.20s\n"\ + "Computer %-20.20s\n"\ + "Guest logon %-20.20s\n"\ + "Client Type %-40.40s\n"\ + "Sess time %2.2d:%2.2d:%2.2d\n"\ + "Idle time %2.2d:%2.2d:%2.2d\n", + username, wsname, + (user_flags&0x0)?"yes":"no", clitype, + shrs, smin, ssec, ihrs, imin, isec); +} + +static void display_conns_func(uint16 conn_id, uint16 conn_type, uint16 opens, + uint16 users, uint32 conn_time, + const char *username, const char *netname) +{ + d_printf("%-14.14s %-8.8s %5d\n", + netname, share_type[conn_type], opens); +} + +static int rap_session_info(int argc, const char **argv) +{ + const char *sessname; + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + if (argc == 0) + return net_rap_session_usage(argc, argv); + + sessname = argv[0]; + + ret = cli_NetSessionGetInfo(cli, sessname, display_session_func); + if (ret < 0) { + cli_shutdown(cli); + return ret; + } + + d_printf("Share name Type # Opens\n-------------------------"\ + "-----------------------------------------------------\n"); + ret = cli_NetConnectionEnum(cli, sessname, display_conns_func); + cli_shutdown(cli); + return ret; +} + +static int rap_session_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + if (argc == 0) + return net_rap_session_usage(argc, argv); + + ret = cli_NetSessionDel(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +int net_rap_session(int argc, const char **argv) +{ + struct functable func[] = { + {"INFO", rap_session_info}, + {"DELETE", rap_session_delete}, + {"CLOSE", rap_session_delete}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + d_printf("Computer User name "\ + "Client Type Opens Idle time\n"\ + "------------------------------------------"\ + "------------------------------------\n"); + ret = cli_NetSessionEnum(cli, list_sessions_func); + + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_session_usage); +} + +/**************************************************************************** +list a server name +****************************************************************************/ +static void display_server_func(const char *name, uint32 m, + const char *comment, void * reserved) +{ + d_printf("\t%-16.16s %s\n", name, comment); +} + + +int net_rap_server_usage(int argc, const char **argv) +{ + d_printf("net rap server [misc. options] [target]\n\t"\ + "lists the servers in the specified domain or workgroup.\n"); + d_printf("\n\tIf domain is not specified, it uses the current"\ + " domain or workgroup as\n\tthe default.\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +int net_rap_server(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + d_printf("\nEnumerating servers in this domain or workgroup: \n\n"\ + "\tServer name Server description\n"\ + "\t------------- ----------------------------\n"); + + ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_ALL, + display_server_func,NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_domain_usage(int argc, const char **argv) +{ + d_printf("net rap domain [misc. options] [target]\n\tlists the"\ + " domains or workgroups visible on the current network\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + + +int net_rap_domain(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + d_printf("\nEnumerating domains:\n\n"\ + "\tDomain name Server name of Browse Master\n"\ + "\t------------- ----------------------------\n"); + + ret = cli_NetServerEnum(cli, cli->server_domain, SV_TYPE_DOMAIN_ENUM, + display_server_func,NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_printq_usage(int argc, const char **argv) +{ + d_printf( + "net rap printq [misc. options] [targets]\n"\ + "\tor\n"\ + "net rap printq list [<queue_name>] [misc. options] [targets]\n"\ + "\tlists the specified queue and jobs on the target server.\n"\ + "\tIf the queue name is not specified, all queues are listed.\n\n"); + d_printf( + "net rap printq delete [<queue name>] [misc. options] [targets]\n"\ + "\tdeletes the specified job number on the target server, or the\n"\ + "\tprinter queue if no job number is specified\n"); + + net_common_flags_usage(argc, argv); + d_printf("\t-j or --jobid=<job id>\t\tjob id\n"); + + return -1; +} + +static void enum_queue(const char *queuename, uint16 pri, uint16 start, + uint16 until, const char *sep, const char *pproc, + const char *dest, const char *qparms, + const char *qcomment, uint16 status, uint16 jobcount) +{ + d_printf("%-17.17s Queue %5d jobs ", + queuename, jobcount); + + switch (status) { + case 0: + d_printf("*Printer Active*\n"); + break; + case 1: + d_printf("*Printer Paused*\n"); + break; + case 2: + d_printf("*Printer error*\n"); + break; + case 3: + d_printf("*Delete Pending*\n"); + break; + default: + d_printf("**UNKNOWN STATUS**\n"); + } +} + +static void enum_jobs(uint16 jobid, const char *ownername, + const char *notifyname, const char *datatype, + const char *jparms, uint16 pos, uint16 status, + const char *jstatus, unsigned int submitted, unsigned int jobsize, + const char *comment) +{ + d_printf(" %-23.23s %5d %9d ", + ownername, jobid, jobsize); + switch (status) { + case 0: + d_printf("Waiting\n"); + break; + case 1: + d_printf("Held in queue\n"); + break; + case 2: + d_printf("Spooling\n"); + break; + case 3: + d_printf("Printing\n"); + break; + default: + d_printf("**UNKNOWN STATUS**\n"); + } +} + +#define PRINTQ_ENUM_DISPLAY \ + "Print queues at \\\\%s\n\n"\ + "Name Job # Size Status\n\n"\ + "------------------------------------------------------------------"\ + "-------------\n" + +static int rap_printq_info(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0) + return net_rap_printq_usage(argc, argv); + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */ + ret = cli_NetPrintQGetInfo(cli, argv[0], enum_queue, enum_jobs); + cli_shutdown(cli); + return ret; +} + +static int rap_printq_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0) + return net_rap_printq_usage(argc, argv); + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_printjob_del(cli, atoi(argv[0])); + cli_shutdown(cli); + return ret; +} + +int net_rap_printq(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + struct functable func[] = { + {"INFO", rap_printq_info}, + {"DELETE", rap_printq_delete}, + {NULL, NULL} + }; + + if (argc == 0) { + if (!(cli = net_make_ipc_connection(0))) + return -1; + + d_printf(PRINTQ_ENUM_DISPLAY, cli->desthost); /* list header */ + ret = cli_NetPrintQEnum(cli, enum_queue, enum_jobs); + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_printq_usage); +} + + +static int net_rap_user_usage(int argc, const char **argv) +{ + return net_help_user(argc, argv); +} + +static void user_fn(const char *user_name, const char *comment, + const char * home_dir, const char * logon_script, + void *state) +{ + d_printf("%-21.21s\n", user_name); +} + +static void long_user_fn(const char *user_name, const char *comment, + const char * home_dir, const char * logon_script, + void *state) +{ + d_printf("%-21.21s %-50.50s\n", + user_name, comment); +} + +static void group_member_fn(const char *user_name, void *state) +{ + d_printf("%-21.21s\n", user_name); +} + +static int rap_user_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc == 0) { + d_printf("\n\nUser name not specified\n"); + return net_rap_user_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetUserDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_user_add(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + RAP_USER_INFO_1 userinfo; + + if (argc == 0) { + d_printf("\n\nUser name not specified\n"); + return net_rap_user_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + safe_strcpy(userinfo.user_name, argv[0], sizeof(userinfo.user_name)); + if (opt_flags == -1) + opt_flags = 0x21; + + userinfo.userflags = opt_flags; + userinfo.reserved1 = '\0'; + userinfo.comment = smb_xstrdup(opt_comment); + userinfo.priv = 1; + userinfo.home_dir = NULL; + userinfo.logon_script = NULL; + + ret = cli_NetUserAdd(cli, &userinfo); + + cli_shutdown(cli); + return ret; +} + +static int rap_user_info(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0) { + d_printf("\n\nUser name not specified\n"); + return net_rap_user_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetUserGetGroups(cli, argv[0], group_member_fn, NULL); + cli_shutdown(cli); + return ret; +} + +int net_rap_user(int argc, const char **argv) +{ + int ret = -1; + struct functable func[] = { + {"ADD", rap_user_add}, + {"INFO", rap_user_info}, + {"DELETE", rap_user_delete}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + if (!(cli = net_make_ipc_connection(0))) + goto done; + if (opt_long_list_entries) { + d_printf("\nUser name Comment"\ + "\n-----------------------------\n"); + ret = cli_RNetUserEnum(cli, long_user_fn, NULL); + cli_shutdown(cli); + goto done; + } + ret = cli_RNetUserEnum(cli, user_fn, NULL); + cli_shutdown(cli); + goto done; + } + + ret = net_run_function(argc, argv, func, net_rap_user_usage); + done: + if (ret != 0) { + DEBUG(1, ("Net user returned: %d\n", ret)); + } + return ret; +} + + +int net_rap_group_usage(int argc, const char **argv) +{ + return net_help_group(argc, argv); +} + +static void long_group_fn(const char *group_name, const char *comment, + void *state) +{ + d_printf("%-21.21s %-50.50s\n", group_name, comment); +} + +static void group_fn(const char *group_name, const char *comment, void *state) +{ + d_printf("%-21.21s\n", group_name); +} + +static int rap_group_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0) { + d_printf("\n\nGroup name not specified\n"); + return net_rap_group_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetGroupDelete(cli, argv[0]); + cli_shutdown(cli); + return ret; +} + +static int rap_group_add(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + RAP_GROUP_INFO_1 grinfo; + + if (argc == 0) { + d_printf("\n\nGroup name not specified\n"); + return net_rap_group_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + /* BB check for length 21 or smaller explicitly ? BB */ + safe_strcpy(grinfo.group_name, argv[0], sizeof(grinfo.group_name)); + grinfo.reserved1 = '\0'; + grinfo.comment = smb_xstrdup(opt_comment); + + ret = cli_NetGroupAdd(cli, &grinfo); + cli_shutdown(cli); + return ret; +} + +int net_rap_group(int argc, const char **argv) +{ + struct functable func[] = { + {"ADD", rap_group_add}, + {"DELETE", rap_group_delete}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + if (!(cli = net_make_ipc_connection(0))) + return -1; + if (opt_long_list_entries) { + d_printf("Group name Comment\n"); + d_printf("-----------------------------\n"); + ret = cli_RNetGroupEnum(cli, long_group_fn, NULL); + cli_shutdown(cli); + return ret; + } + ret = cli_RNetGroupEnum(cli, group_fn, NULL); + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_group_usage); +} + +int net_rap_groupmember_usage(int argc, const char **argv) +{ + d_printf( + "net rap groupmember LIST <group> [misc. options] [targets]"\ + "\n\t Enumerate users in a group\n"\ + "\nnet rap groupmember DELETE <group> <user> [misc. options] "\ + "[targets]\n\t Delete sepcified user from specified group\n"\ + "\nnet rap groupmember ADD <group> <user> [misc. options] [targets]"\ + "\n\t Add specified user to specified group\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + + +static int rap_groupmember_add(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc != 2) { + d_printf("\n\nGroup or user name not specified\n"); + return net_rap_groupmember_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetGroupAddUser(cli, argv[0], argv[1]); + cli_shutdown(cli); + return ret; +} + +static int rap_groupmember_delete(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc != 2) { + d_printf("\n\nGroup or user name not specified\n"); + return net_rap_groupmember_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetGroupDelUser(cli, argv[0], argv[1]); + cli_shutdown(cli); + return ret; +} + +static int rap_groupmember_list(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + if (argc == 0) { + d_printf("\n\nGroup name not specified\n"); + return net_rap_groupmember_usage(argc, argv); + } + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + ret = cli_NetGroupGetUsers(cli, argv[0], group_member_fn, NULL ); + cli_shutdown(cli); + return ret; +} + +int net_rap_groupmember(int argc, const char **argv) +{ + struct functable func[] = { + {"ADD", rap_groupmember_add}, + {"LIST", rap_groupmember_list}, + {"DELETE", rap_groupmember_delete}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_rap_groupmember_usage); +} + +int net_rap_validate_usage(int argc, const char **argv) +{ + d_printf("net rap validate <username> [password]\n"\ + "\tValidate user and password to check whether they"\ + " can access target server or domain\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +int net_rap_validate(int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +int net_rap_service_usage(int argc, const char **argv) +{ + d_printf("net rap service [misc. options] [targets] \n"\ + "\tlists all running service daemons on target server\n"); + d_printf("\nnet rap service START <name> [service startup arguments]"\ + " [misc. options] [targets]"\ + "\n\tStart named service on remote server\n"); + d_printf("\nnet rap service STOP <name> [misc. options] [targets]\n"\ + "\n\tStop named service on remote server\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +static int rap_service_start(int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +static int rap_service_stop(int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +int net_rap_service(int argc, const char **argv) +{ + struct functable func[] = { + {"START", rap_service_start}, + {"STOP", rap_service_stop}, + {NULL, NULL} + }; + + if (argc == 0) { + struct cli_state *cli; + int ret; + if (!(cli = net_make_ipc_connection(0))) + return -1; + + if (opt_long_list_entries) { + d_printf("Service name Comment\n"); + d_printf("-----------------------------\n"); + ret = cli_RNetServiceEnum(cli, long_group_fn, NULL); + } + ret = cli_RNetServiceEnum(cli, group_fn, NULL); + cli_shutdown(cli); + return ret; + } + + return net_run_function(argc, argv, func, net_rap_service_usage); +} + +int net_rap_password_usage(int argc, const char **argv) +{ + d_printf( + "net rap password <user> <oldpwo> <newpw> [misc. options] [target]\n"\ + "\tchanges the password for the specified user at target\n"); + + return -1; +} + + +int net_rap_password(int argc, const char **argv) +{ + struct cli_state *cli; + int ret; + + if (argc < 3) + return net_rap_password_usage(argc, argv); + + if (!(cli = net_make_ipc_connection(0))) + return -1; + + /* BB Add check for password lengths? */ + ret = cli_oem_change_password(cli, argv[0], argv[2], argv[1]); + cli_shutdown(cli); + return ret; +} + +int net_rap_admin_usage(int argc, const char **argv) +{ + d_printf( + "net rap admin <remote command> [cmd args [env]] [misc. options] [targets]"\ + "\n\texecutes a remote command on an os/2 target server\n"); + + return -1; +} + + +int net_rap_admin(int argc, const char **argv) +{ + return errmsg_not_implemented(); +} + +/* The help subsystem for the RAP subcommand */ + +int net_rap_usage(int argc, const char **argv) +{ + d_printf(" net rap domain \tto list domains \n"\ + " net rap file \t\tto list open files on a server \n"\ + " net rap group \tto list user groups \n"\ + " net rap groupmember \tto list users in a group \n"\ + " net rap password \tto change the password of a user\n"\ + " net rap printq \tto list the print queues on a server\n"\ + " net rap server \tto list servers in a domain\n"\ + " net rap session \tto list clients with open sessions to a server\n"\ + " net rap share \tto list shares exported by a server\n"\ + " net rap user \t\tto list users\n"\ + " net rap validate \tto check whether a user and the corresponding password are valid\n"\ + " net rap help\n"\ + "\nType \"net help <option>\" to get more information on that option\n\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +/* + handle "net rap help *" subcommands +*/ +int net_rap_help(int argc, const char **argv) +{ + struct functable func[] = { + {"FILE", net_rap_file_usage}, + {"SHARE", net_rap_share_usage}, + {"SESSION", net_rap_session_usage}, + {"SERVER", net_rap_server_usage}, + {"DOMAIN", net_rap_domain_usage}, + {"PRINTQ", net_rap_printq_usage}, + {"USER", net_rap_user_usage}, + {"GROUP", net_rap_group_usage}, + {"VALIDATE", net_rap_validate_usage}, + {"GROUPMEMBER", net_rap_groupmember_usage}, + {"ADMIN", net_rap_admin_usage}, + {"SERVICE", net_rap_service_usage}, + {"PASSWORD", net_rap_password_usage}, + {NULL, NULL}}; + + return net_run_function(argc, argv, func, net_rap_usage); +} + +/* Entry-point for all the RAP functions. */ + +int net_rap(int argc, const char **argv) +{ + struct functable func[] = { + {"FILE", net_rap_file}, + {"SHARE", net_rap_share}, + {"SESSION", net_rap_session}, + {"SERVER", net_rap_server}, + {"DOMAIN", net_rap_domain}, + {"PRINTQ", net_rap_printq}, + {"USER", net_rap_user}, + {"GROUP", net_rap_group}, + {"VALIDATE", net_rap_validate}, + {"GROUPMEMBER", net_rap_groupmember}, + {"ADMIN", net_rap_admin}, + {"SERVICE", net_rap_service}, + {"PASSWORD", net_rap_password}, + {"HELP", net_rap_help}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_rap_usage); +} + diff --git a/source4/utils/net_rpc.c b/source4/utils/net_rpc.c new file mode 100644 index 0000000000..89ee34ac01 --- /dev/null +++ b/source4/utils/net_rpc.c @@ -0,0 +1,2262 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + Copyright (C) 2002 Jim McDonough (jmcd@us.ibm.com) + + This program is free software; you can 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" + +/** + * @file net_rpc.c + * + * @brief RPC based subcommands for the 'net' utility. + * + * This file should contain much of the functionality that used to + * be found in rpcclient, execpt that the commands should change + * less often, and the fucntionality should be sane (the user is not + * expected to know a rid/sid before they conduct an operation etc.) + * + * @todo Perhaps eventually these should be split out into a number + * of files, as this could get quite big. + **/ + + +/* A function of this type is passed to the 'run_rpc_command' wrapper */ +typedef NTSTATUS (*rpc_command_fn)(const DOM_SID *, struct cli_state *, TALLOC_CTX *, int, const char **); + +/** + * Many of the RPC functions need the domain sid. This function gets + * it at the start of every run + * + * @param cli A cli_state already connected to the remote machine + * + * @return The Domain SID of the remote machine. + **/ + +static DOM_SID *net_get_remote_domain_sid(struct cli_state *cli) +{ + DOM_SID *domain_sid; + POLICY_HND pol; + NTSTATUS result = NT_STATUS_OK; + uint32 info_class = 5; + fstring domain_name; + TALLOC_CTX *mem_ctx; + + if (!(domain_sid = malloc(sizeof(DOM_SID)))){ + DEBUG(0,("net_get_remote_domain_sid: malloc returned NULL!\n")); + goto error; + } + + if (!(mem_ctx=talloc_init("net_get_remote_domain_sid"))) + { + DEBUG(0,("net_get_remote_domain_sid: talloc_init returned NULL!\n")); + goto error; + } + + + if (!cli_nt_session_open (cli, PI_LSARPC)) { + fprintf(stderr, "could not initialise lsa pipe\n"); + goto error; + } + + result = cli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &pol); + if (!NT_STATUS_IS_OK(result)) { + goto error; + } + + result = cli_lsa_query_info_policy(cli, mem_ctx, &pol, info_class, + domain_name, domain_sid); + if (!NT_STATUS_IS_OK(result)) { + goto error; + } + + cli_lsa_close(cli, mem_ctx, &pol); + cli_nt_session_close(cli); + talloc_destroy(mem_ctx); + + return domain_sid; + + error: + fprintf(stderr, "could not obtain sid for domain %s\n", cli->domain); + + if (!NT_STATUS_IS_OK(result)) { + fprintf(stderr, "error: %s\n", nt_errstr(result)); + } + + exit(1); +} + +/** + * Run a single RPC command, from start to finish. + * + * @param pipe_name the pipe to connect to (usually a PIPE_ constant) + * @param conn_flag a NET_FLAG_ combination. Passed to + * net_make_ipc_connection. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * @return A shell status integer (0 for success) + */ + +static int run_rpc_command(struct cli_state *cli_arg, const int pipe_idx, int conn_flags, + rpc_command_fn fn, + int argc, const char **argv) +{ + struct cli_state *cli = NULL; + TALLOC_CTX *mem_ctx; + NTSTATUS nt_status; + DOM_SID *domain_sid; + + /* make use of cli_state handed over as an argument, if possible */ + if (!cli_arg) + cli = net_make_ipc_connection(conn_flags); + else + cli = cli_arg; + + if (!cli) { + return -1; + } + + domain_sid = net_get_remote_domain_sid(cli); + + /* Create mem_ctx */ + + if (!(mem_ctx = talloc_init("run_rpc_command"))) { + DEBUG(0, ("talloc_init() failed\n")); + cli_shutdown(cli); + return -1; + } + + if (!cli_nt_session_open(cli, pipe_idx)) { + DEBUG(0, ("Could not initialise pipe\n")); + } + + nt_status = fn(domain_sid, cli, mem_ctx, argc, argv); + + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(1, ("rpc command function failed! (%s)\n", nt_errstr(nt_status))); + } else { + DEBUG(5, ("rpc command function succedded\n")); + } + + + if (cli->nt_pipe_fnum) + cli_nt_session_close(cli); + + /* close the connection only if it was opened here */ + if (!cli_arg) + cli_shutdown(cli); + + talloc_destroy(mem_ctx); + + return (!NT_STATUS_IS_OK(nt_status)); +} + + +/****************************************************************************/ + + +/** + * Force a change of the trust acccount password. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid aquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on compleation of the function. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_changetrustpw_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) { + + return trust_pw_find_change_and_store_it(cli, mem_ctx, opt_target_workgroup); +} + +/** + * Force a change of the trust acccount password. + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_changetrustpw(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, rpc_changetrustpw_internals, + argc, argv); +} + + +/****************************************************************************/ + + +/** + * Join a domain, the old way. + * + * This uses 'machinename' as the inital password, and changes it. + * + * The password should be created with 'server manager' or eqiv first. + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid aquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on compleation of the function. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_join_oldstyle_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) { + + fstring trust_passwd; + unsigned char orig_trust_passwd_hash[16]; + NTSTATUS result; + + fstrcpy(trust_passwd, lp_netbios_name()); + strlower(trust_passwd); + + /* + * Machine names can be 15 characters, but the max length on + * a password is 14. --jerry + */ + + trust_passwd[14] = '\0'; + + E_md4hash(trust_passwd, orig_trust_passwd_hash); + + result = trust_pw_change_and_store_it(cli, mem_ctx, orig_trust_passwd_hash); + + if (NT_STATUS_IS_OK(result)) + printf("Joined domain %s.\n",lp_workgroup()); + + return result; +} + +/** + * Join a domain, the old way. + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int net_rpc_join_oldstyle(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, rpc_join_oldstyle_internals, + argc, argv); +} + +/** + * Basic usage function for 'net rpc join' + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +static int rpc_join_usage(int argc, const char **argv) +{ + d_printf("net rpc join -U <username>[%%password] [options]\n"\ + "\t to join a domain with admin username & password\n"\ + "\t\t password will be prompted if none is specified\n"); + d_printf("net rpc join [options except -U]\n"\ + "\t to join a domain created in server manager\n\n\n"); + + net_common_flags_usage(argc, argv); + return -1; +} + +/** + * 'net rpc join' entrypoint. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * Main 'net_rpc_join()' (where the admain username/password is used) is + * in net_rpc_join.c + * Assume if a -U is specified, it's the new style, otherwise it's the + * old style. If 'oldstyle' is specfied explicity, do it and don't prompt. + **/ + +int net_rpc_join(int argc, const char **argv) +{ + struct functable func[] = { + {"oldstyle", net_rpc_join_oldstyle}, + {NULL, NULL} + }; + + if (argc == 0) { + if ((net_rpc_join_oldstyle(argc, argv) == 0)) + return 0; + + return net_rpc_join_newstyle(argc, argv); + } + + return net_run_function(argc, argv, func, rpc_join_usage); +} + + + +/** + * display info about a rpc domain + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_info_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + SAM_UNK_CTR ctr; + fstring sid_str; + + sid_to_string(sid_str, domain_sid); + + /* Get sam policy handle */ + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get domain policy handle */ + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + ZERO_STRUCT(ctr); + result = cli_samr_query_dom_info(cli, mem_ctx, &domain_pol, + 2, &ctr); + if (NT_STATUS_IS_OK(result)) { + TALLOC_CTX *ctx = talloc_init("rpc_info_internals"); + d_printf("Domain Name: %s\n", unistr2_tdup(ctx, &ctr.info.inf2.uni_domain)); + d_printf("Domain SID: %s\n", sid_str); + d_printf("Sequence number: %u\n", ctr.info.inf2.seq_num); + d_printf("Num users: %u\n", ctr.info.inf2.num_domain_usrs); + d_printf("Num domain groups: %u\n", ctr.info.inf2.num_domain_grps); + d_printf("Num local groups: %u\n", ctr.info.inf2.num_local_grps); + talloc_destroy(ctx); + } + + done: + return result; +} + + +/** + * 'net rpc info' entrypoint. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ +int net_rpc_info(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, + rpc_info_internals, + argc, argv); +} + + +/** + * Fetch domain SID into the local secrets.tdb + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_getsid_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + fstring sid_str; + + sid_to_string(sid_str, domain_sid); + d_printf("Storing SID %s for Domain %s in secrets.tdb\n", + sid_str, lp_workgroup()); + + if (!secrets_store_domain_sid(lp_netbios_name(), domain_sid)) { + DEBUG(0,("Can't store domain SID\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + + +/** + * 'net rpc getsid' entrypoint. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ +int net_rpc_getsid(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, + rpc_getsid_internals, + argc, argv); +} + + +/****************************************************************************/ + +/** + * Basic usage function for 'net rpc user' + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +static int rpc_user_usage(int argc, const char **argv) +{ + return net_help_user(argc, argv); +} + +/** + * Add a new user to a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_user_add_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) { + + POLICY_HND connect_pol, domain_pol, user_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + const char *acct_name; + uint16 acb_info; + uint32 unknown, user_rid; + + if (argc != 1) { + d_printf("User must be specified\n"); + rpc_user_usage(argc, argv); + return NT_STATUS_OK; + } + + acct_name = argv[0]; + + /* Get sam policy handle */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get domain policy handle */ + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Create domain user */ + + acb_info = ACB_NORMAL; + unknown = 0xe005000b; /* No idea what this is - a permission mask? */ + + result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol, + acct_name, acb_info, unknown, + &user_pol, &user_rid); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + done: + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to add user %s - %s\n", acct_name, + nt_errstr(result)); + } else { + d_printf("Added user %s\n", acct_name); + } + return result; +} + +/** + * Add a new user to a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_user_add(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_add_internals, + argc, argv); +} + +/** + * Delete a user from a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_user_del_internals(const DOM_SID *domain_sid, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + POLICY_HND connect_pol, domain_pol, user_pol; + + if (argc < 1) { + d_printf("User must be specified\n"); + rpc_user_usage(argc, argv); + return NT_STATUS_OK; + } + /* Get sam policy and domain handles */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get handle on user */ + + { + uint32 *user_rids, num_rids, *name_types; + uint32 flags = 0x000003e8; /* Unknown */ + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, + flags, 1, &argv[0], + &num_rids, &user_rids, + &name_types); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = cli_samr_open_user(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + user_rids[0], &user_pol); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + } + + /* Delete user */ + + result = cli_samr_delete_dom_user(cli, mem_ctx, &user_pol); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Display results */ + + done: + return result; + +} + +/** + * Delete a user from a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_user_delete(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_del_internals, + argc, argv); +} + +/** + * List user's groups on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_user_info_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol, user_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 *rids, num_rids, *name_types, num_names; + uint32 flags = 0x000003e8; /* Unknown */ + int i; + char **names; + DOM_GID *user_gids; + + if (argc < 1) { + d_printf("User must be specified\n"); + rpc_user_usage(argc, argv); + return NT_STATUS_OK; + } + /* Get sam policy handle */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) goto done; + + /* Get domain policy handle */ + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) goto done; + + /* Get handle on user */ + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, + flags, 1, &argv[0], + &num_rids, &rids, &name_types); + + if (!NT_STATUS_IS_OK(result)) goto done; + + result = cli_samr_open_user(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rids[0], &user_pol); + if (!NT_STATUS_IS_OK(result)) goto done; + + result = cli_samr_query_usergroups(cli, mem_ctx, &user_pol, + &num_rids, &user_gids); + + /* Look up rids */ + + rids = (uint32 *)talloc(mem_ctx, sizeof(uint32) * num_rids); + + for (i = 0; i < num_rids; i++) + rids[i] = user_gids[i].g_rid; + + result = cli_samr_lookup_rids(cli, mem_ctx, &domain_pol, + flags, num_rids, rids, + &num_names, &names, &name_types); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Display results */ + + for (i = 0; i < num_names; i++) + printf("%s\n", names[i]); + + done: + return result; +} + +/** + * List a user's groups from a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_user_info(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_info_internals, + argc, argv); +} + +/** + * List users on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_user_list_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 start_idx=0, num_entries, i, loop_count = 0; + SAM_DISPINFO_CTR ctr; + SAM_DISPINFO_1 info1; + + /* Get sam policy handle */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get domain policy handle */ + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Query domain users */ + ZERO_STRUCT(ctr); + ZERO_STRUCT(info1); + ctr.sam.info1 = &info1; + if (opt_long_list_entries) + d_printf("\nUser name Comment"\ + "\n-----------------------------\n"); + do { + fstring user, desc; + uint32 max_entries, max_size; + + get_query_dispinfo_params( + loop_count, &max_entries, &max_size); + + result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol, + &start_idx, 1, &num_entries, + max_entries, max_size, &ctr); + loop_count++; + + for (i = 0; i < num_entries; i++) { + unistr2_to_ascii(user, &(&ctr.sam.info1->str[i])->uni_acct_name, sizeof(user)-1); + if (opt_long_list_entries) + unistr2_to_ascii(desc, &(&ctr.sam.info1->str[i])->uni_acct_desc, sizeof(desc)-1); + + if (opt_long_list_entries) + printf("%-21.21s %-50.50s\n", user, desc); + else + printf("%s\n", user); + } + } while (!NT_STATUS_IS_OK(result)); + + done: + return result; +} + +/** + * 'net rpc user' entrypoint. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_user(int argc, const char **argv) +{ + struct functable func[] = { + {"add", rpc_user_add}, + {"info", rpc_user_info}, + {"delete", rpc_user_delete}, + {NULL, NULL} + }; + + if (argc == 0) { + if (opt_long_list_entries) { + } else { + } + return run_rpc_command(NULL,PI_SAMR, 0, + rpc_user_list_internals, + argc, argv); + } + + return net_run_function(argc, argv, func, rpc_user_usage); +} + + +/****************************************************************************/ + +/** + * Basic usage function for 'net rpc group' + * @param argc Standard main() style argc. + * @param argv Standard main() style argv. Initial components are already + * stripped. + **/ + +static int rpc_group_usage(int argc, const char **argv) +{ + return net_help_group(argc, argv); +} + +/** + * List groups on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_group_list_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + uint32 start_idx=0, max_entries=250, num_entries, i; + struct acct_info *groups; + DOM_SID global_sid_Builtin; + + string_to_sid(&global_sid_Builtin, "S-1-5-32"); + + /* Get sam policy handle */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get domain policy handle */ + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Query domain groups */ + if (opt_long_list_entries) + d_printf("\nGroup name Comment"\ + "\n-----------------------------\n"); + do { + result = cli_samr_enum_dom_groups(cli, mem_ctx, &domain_pol, + &start_idx, max_entries, + &groups, &num_entries); + + for (i = 0; i < num_entries; i++) { + if (opt_long_list_entries) + printf("%-21.21s %-50.50s\n", + groups[i].acct_name, + groups[i].acct_desc); + else + printf("%-21.21s\n", groups[i].acct_name); + } + } while (!NT_STATUS_IS_OK(result)); + /* query domain aliases */ + do { + result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol, + &start_idx, max_entries, + &groups, &num_entries); + + for (i = 0; i < num_entries; i++) { + if (opt_long_list_entries) + printf("%-21.21s %-50.50s\n", + groups[i].acct_name, + groups[i].acct_desc); + else + printf("%-21.21s\n", groups[i].acct_name); + } + } while (!NT_STATUS_IS_OK(result)); + cli_samr_close(cli, mem_ctx, &domain_pol); + /* Get builtin policy handle */ + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &global_sid_Builtin, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + /* query builtin aliases */ + do { + result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol, + &start_idx, max_entries, + &groups, &num_entries); + + for (i = 0; i < num_entries; i++) { + if (opt_long_list_entries) + printf("%-21.21s %-50.50s\n", + groups[i].acct_name, + groups[i].acct_desc); + else + printf("%s\n", groups[i].acct_name); + } + } while (!NT_STATUS_IS_OK(result)); + + done: + return result; +} + +/** + * 'net rpc group' entrypoint. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_group(int argc, const char **argv) +{ + struct functable func[] = { +#if 0 + {"add", rpc_group_add}, + {"delete", rpc_group_delete}, +#endif + {NULL, NULL} + }; + + if (argc == 0) { + if (opt_long_list_entries) { + } else { + } + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_list_internals, + argc, argv); + } + + return net_run_function(argc, argv, func, rpc_group_usage); +} + +/****************************************************************************/ + +static int rpc_share_usage(int argc, const char **argv) +{ + return net_help_share(argc, argv); +} + +/** + * Add a share on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ +static NTSTATUS +rpc_share_add_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx,int argc, const char **argv) +{ + WERROR result; + char *sharename=talloc_strdup(mem_ctx, argv[0]); + char *path; + uint32 type=0; /* only allow disk shares to be added */ + uint32 num_users=0, perms=0; + char *password=NULL; /* don't allow a share password */ + + path = strchr(sharename, '='); + if (!path) + return NT_STATUS_UNSUCCESSFUL; + *path++ = '\0'; + + result = cli_srvsvc_net_share_add(cli, mem_ctx, sharename, type, + opt_comment, perms, opt_maxusers, + num_users, path, password); + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +static int rpc_share_add(int argc, const char **argv) +{ + if ((argc < 1) || !strchr(argv[0], '=')) { + DEBUG(1,("Sharename or path not specified on add\n")); + return rpc_share_usage(argc, argv); + } + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_share_add_internals, + argc, argv); +} + +/** + * Delete a share on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ +static NTSTATUS +rpc_share_del_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx,int argc, const char **argv) +{ + WERROR result; + + result = cli_srvsvc_net_share_del(cli, mem_ctx, argv[0]); + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/** + * Delete a share on a remote RPC server + * + * @param domain_sid The domain sid acquired from the remote server + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ +static int rpc_share_delete(int argc, const char **argv) +{ + if (argc < 1) { + DEBUG(1,("Sharename not specified on delete\n")); + return rpc_share_usage(argc, argv); + } + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_share_del_internals, + argc, argv); +} + +/** + * Formatted print of share info + * + * @param info1 pointer to SRV_SHARE_INFO_1 to format + **/ + +static void display_share_info_1(SRV_SHARE_INFO_1 *info1) +{ + fstring netname = "", remark = ""; + + rpcstr_pull_unistr2_fstring(netname, &info1->info_1_str.uni_netname); + rpcstr_pull_unistr2_fstring(remark, &info1->info_1_str.uni_remark); + + if (opt_long_list_entries) { + d_printf("%-12.12s %-8.8s %-50.50s\n", + netname, share_type[info1->info_1.type], remark); + } else { + d_printf("%-12.12s\n", netname); + } + +} + +/** + * List shares on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_share_list_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + SRV_SHARE_INFO_CTR ctr; + WERROR result; + ENUM_HND hnd; + uint32 preferred_len = 0xffffffff, i; + + init_enum_hnd(&hnd, 0); + + result = cli_srvsvc_net_share_enum( + cli, mem_ctx, 1, &ctr, preferred_len, &hnd); + + if (!W_ERROR_IS_OK(result)) + goto done; + + /* Display results */ + + if (opt_long_list_entries) { + d_printf( + "\nEnumerating shared resources (exports) on remote server:\n\n"\ + "\nShare name Type Description\n"\ + "---------- ---- -----------\n"); + } + for (i = 0; i < ctr.num_entries; i++) + display_share_info_1(&ctr.share.info1[i]); + done: + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/** + * 'net rpc share' entrypoint. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_share(int argc, const char **argv) +{ + struct functable func[] = { + {"add", rpc_share_add}, + {"delete", rpc_share_delete}, + {NULL, NULL} + }; + + if (argc == 0) + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_share_list_internals, + argc, argv); + + return net_run_function(argc, argv, func, rpc_share_usage); +} + +/****************************************************************************/ + +static int rpc_file_usage(int argc, const char **argv) +{ + return net_help_file(argc, argv); +} + +/** + * Close a file on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ +static NTSTATUS +rpc_file_close_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + WERROR result; + result = cli_srvsvc_net_file_close(cli, mem_ctx, atoi(argv[0])); + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + +/** + * Close a file on a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ +static int rpc_file_close(int argc, const char **argv) +{ + if (argc < 1) { + DEBUG(1, ("No fileid given on close\n")); + return(rpc_file_usage(argc, argv)); + } + + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_file_close_internals, + argc, argv); +} + +/** + * Formatted print of open file info + * + * @param info3 FILE_INFO_3 contents + * @param str3 strings for FILE_INFO_3 + **/ + +static void display_file_info_3(FILE_INFO_3 *info3, FILE_INFO_3_STR *str3) +{ + fstring user = "", path = ""; + + rpcstr_pull_unistr2_fstring(user, &str3->uni_user_name); + rpcstr_pull_unistr2_fstring(path, &str3->uni_path_name); + + d_printf("%-7.1d %-20.20s 0x%-4.2x %-6.1d %s\n", + info3->id, user, info3->perms, info3->num_locks, path); +} + +/** + * List open files on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS +rpc_file_list_internals(const DOM_SID *domain_sid, struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + SRV_FILE_INFO_CTR ctr; + WERROR result; + ENUM_HND hnd; + uint32 preferred_len = 0xffffffff, i; + const char *username=NULL; + + init_enum_hnd(&hnd, 0); + + /* if argc > 0, must be user command */ + if (argc > 0) + username = smb_xstrdup(argv[0]); + + result = cli_srvsvc_net_file_enum( + cli, mem_ctx, 3, username, &ctr, preferred_len, &hnd); + + if (!W_ERROR_IS_OK(result)) + goto done; + + /* Display results */ + + d_printf( + "\nEnumerating open files on remote server:\n\n"\ + "\nFileId Opened by Perms Locks Path"\ + "\n------ --------- ----- ----- ---- \n"); + for (i = 0; i < ctr.num_entries; i++) + display_file_info_3(&ctr.file.info3[i].info_3, + &ctr.file.info3[i].info_3_str); + done: + return W_ERROR_IS_OK(result) ? NT_STATUS_OK : NT_STATUS_UNSUCCESSFUL; +} + + +/** + * List files for a user on a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ +static int rpc_file_user(int argc, const char **argv) +{ + if (argc < 1) { + DEBUG(1, ("No username given\n")); + return(rpc_file_usage(argc, argv)); + } + + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_file_list_internals, + argc, argv); +} + + +/** + * 'net rpc file' entrypoint. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_file(int argc, const char **argv) +{ + struct functable func[] = { + {"close", rpc_file_close}, + {"user", rpc_file_user}, +#if 0 + {"info", rpc_file_info}, +#endif + {NULL, NULL} + }; + + if (argc == 0) + return run_rpc_command(NULL, PI_SRVSVC, 0, + rpc_file_list_internals, + argc, argv); + + return net_run_function(argc, argv, func, rpc_file_usage); +} + +/****************************************************************************/ + + + +/** + * ABORT the shutdown of a remote RPC Server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid aquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on compleation of the function. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_shutdown_abort_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + result = cli_reg_abort_shutdown(cli, mem_ctx); + + if (NT_STATUS_IS_OK(result)) + DEBUG(5,("cmd_reg_abort_shutdown: query succeeded\n")); + else + DEBUG(5,("cmd_reg_abort_shutdown: query failed\n")); + + return result; +} + + +/** + * ABORT the Shut down of a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_shutdown_abort(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_abort_internals, + argc, argv); +} + +/** + * Shut down a remote RPC Server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid aquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on compleation of the function. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return Normal NTSTATUS return. + **/ + +static NTSTATUS rpc_shutdown_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + const char *msg = "This machine will be shutdown shortly"; + uint32 timeout = 20; +#if 0 + poptContext pc; + int rc; + + struct poptOption long_options[] = { + {"message", 'm', POPT_ARG_STRING, &msg}, + {"timeout", 't', POPT_ARG_INT, &timeout}, + {"reboot", 'r', POPT_ARG_NONE, &reboot}, + {"force", 'f', POPT_ARG_NONE, &force}, + { 0, 0, 0, 0} + }; + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + rc = poptGetNextOpt(pc); + + if (rc < -1) { + /* an error occurred during option processing */ + DEBUG(0, ("%s: %s\n", + poptBadOption(pc, POPT_BADOPTION_NOALIAS), + poptStrerror(rc))); + return NT_STATUS_INVALID_PARAMETER; + } +#endif + if (opt_comment) { + msg = opt_comment; + } + if (opt_timeout) { + timeout = opt_timeout; + } + + /* create an entry */ + result = cli_reg_shutdown(cli, mem_ctx, msg, timeout, opt_reboot, opt_force); + + if (NT_STATUS_IS_OK(result)) + DEBUG(5,("Shutdown of remote machine succeeded\n")); + else + DEBUG(0,("Shutdown of remote machine failed!\n")); + + return result; +} + +/** + * Shut down a remote RPC server + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_shutdown(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_WINREG, 0, rpc_shutdown_internals, + argc, argv); +} + +/*************************************************************************** + NT Domain trusts code (i.e. 'net rpc trustdom' functionality) + + ***************************************************************************/ + +/** + * Add interdomain trust account to the RPC server. + * All parameters (except for argc and argv) are passed by run_rpc_command + * function. + * + * @param domain_sid The domain sid acquired from the server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, destoyed on completion of the function. + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return normal NTSTATUS return code + */ + +static NTSTATUS rpc_trustdom_add_internals(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) { + + POLICY_HND connect_pol, domain_pol, user_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + char *acct_name; + uint16 acb_info; + uint32 unknown, user_rid; + + if (argc != 1) { + d_printf("Usage: net rpc trustdom add <domain_name>\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Make valid trusting domain account (ie. uppercased and with '$' appended) + */ + + if (asprintf(&acct_name, "%s$", argv[0]) < 0) { + return NT_STATUS_NO_MEMORY; + } + + strupper(acct_name); + + /* Get samr policy handle */ + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Get domain policy handle */ + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + domain_sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Create trusting domain's account */ + acb_info = ACB_DOMTRUST; + unknown = 0xe005000b; /* No idea what this is - a permission mask? + mimir: yes, most probably it is */ + + result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol, + acct_name, acb_info, unknown, + &user_pol, &user_rid); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + done: + SAFE_FREE(acct_name); + return result; +} + +/** + * Create interdomain trust account for a remote domain. + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + **/ + +static int rpc_trustdom_add(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, rpc_trustdom_add_internals, + argc, argv); +} + + +/** + * Delete interdomain trust account for a remote domain. + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + **/ + +static int rpc_trustdom_del(int argc, const char **argv) +{ + d_printf("Sorry, not yet implemented.\n"); + return -1; +} + + +/** + * Establish trust relationship to a trusting domain. + * Interdomain account must already be created on remote PDC. + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + **/ + +static int rpc_trustdom_establish(int argc, const char **argv) +{ + struct cli_state *cli; + struct in_addr server_ip; + POLICY_HND connect_hnd; + TALLOC_CTX *mem_ctx; + NTSTATUS nt_status; + DOM_SID domain_sid; + WKS_INFO_100 wks_info; + + char* domain_name; + char* acct_name; + fstring pdc_name; + + /* + * Connect to \\server\ipc$ as 'our domain' account with password + */ + + if (argc != 1) { + d_printf("Usage: net rpc trustdom establish <domain_name>\n"); + return -1; + } + + domain_name = smb_xstrdup(argv[0]); + strupper(domain_name); + + /* account name used at first is our domain's name with '$' */ + asprintf(&acct_name, "%s$", lp_workgroup()); + strupper(acct_name); + + /* + * opt_workgroup will be used by connection functions further, + * hence it should be set to remote domain name instead of ours + */ + if (opt_workgroup) { + opt_workgroup = smb_xstrdup(domain_name); + }; + + opt_user_name = acct_name; + + /* find the domain controller */ + if (!net_find_dc(&server_ip, pdc_name, domain_name)) { + DEBUG(0, ("Coulnd find domain controller for domain %s\n", domain_name)); + return -1; + } + + /* connect to ipc$ as username/password */ + nt_status = connect_to_ipc(&cli, &server_ip, pdc_name); + if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT)) { + + /* Is it trusting domain account for sure ? */ + DEBUG(0, ("Couldn't verify trusting domain account. Error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + /* + * Connect to \\server\ipc$ again (this time anonymously) + */ + + nt_status = connect_to_ipc_anonymous(&cli, &server_ip, (char*)pdc_name); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't connect to domain %s controller. Error was %s.\n", + domain_name, nt_errstr(nt_status))); + } + + /* + * Use NetServerEnum2 to make sure we're talking to a proper server + */ + + if (!cli_get_pdc_name(cli, domain_name, (char*)pdc_name)) { + DEBUG(0, ("NetServerEnum2 error: Couldn't find primary domain controller\ + for domain %s\n", domain_name)); + } + + /* + * Call WksQueryInfo to check remote server's capabilities + * note: It is now used only to get unicode domain name + */ + + if (!cli_nt_session_open(cli, PI_WKSSVC)) { + DEBUG(0, ("Couldn't not initialise wkssvc pipe\n")); + return -1; + } + + if (!(mem_ctx = talloc_init("establishing trust relationship to domain %s", + domain_name))) { + DEBUG(0, ("talloc_init() failed\n")); + cli_shutdown(cli); + return -1; + } + + nt_status = cli_wks_query_info(cli, mem_ctx, &wks_info); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("WksQueryInfo call failed.\n")); + return -1; + } + + if (cli->nt_pipe_fnum) + cli_nt_session_close(cli); + + + /* + * Call LsaOpenPolicy and LsaQueryInfo + */ + + if (!(mem_ctx = talloc_init("rpc_trustdom_establish"))) { + DEBUG(0, ("talloc_init() failed\n")); + cli_shutdown(cli); + return -1; + } + + if (!cli_nt_session_open(cli, PI_LSARPC)) { + DEBUG(0, ("Could not initialise lsa pipe\n")); + cli_shutdown(cli); + return -1; + } + + nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, + &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't open policy handle. Error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + /* Querying info level 5 */ + + nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd, + 5 /* info level */, domain_name, + &domain_sid); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + + + + /* There should be actually query info level 3 (following nt serv behaviour), + but I still don't know if it's _really_ necessary */ + + /* + * Store the password in secrets db + */ + + if (!secrets_store_trusted_domain_password(domain_name, wks_info.uni_lan_grp.buffer, + wks_info.uni_lan_grp.uni_str_len, opt_password, + domain_sid)) { + DEBUG(0, ("Storing password for trusted domain failed.\n")); + return -1; + } + + /* + * Close the pipes and clean up + */ + + nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't close LSA pipe. Error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + if (cli->nt_pipe_fnum) + cli_nt_session_close(cli); + + talloc_destroy(mem_ctx); + + DEBUG(0, ("Success!\n")); + return 0; +} + +/** + * Revoke trust relationship to the remote domain + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + **/ + +static int rpc_trustdom_revoke(int argc, const char **argv) +{ + char* domain_name; + + if (argc < 1) return -1; + + /* generate upper cased domain name */ + domain_name = smb_xstrdup(argv[0]); + strupper(domain_name); + + /* delete password of the trust */ + if (!trusted_domain_password_delete(domain_name)) { + DEBUG(0, ("Failed to revoke relationship to the trusted domain %s\n", + domain_name)); + return -1; + }; + + return 0; +} + +/** + * Usage for 'net rpc trustdom' command + * + * @param argc standard argc + * @param argv standard argv without inital components + * + * @return Integer status returned to shell + **/ + +static int rpc_trustdom_usage(int argc, const char **argv) +{ + d_printf(" net rpc trustdom add \t\t add trusting domain's account\n"); + d_printf(" net rpc trustdom del \t\t delete trusting domain's account\n"); + d_printf(" net rpc trustdom establish \t establish relationship to trusted domain\n"); + d_printf(" net rpc trustdom revoke \t abandon relationship to trusted domain\n"); + d_printf(" net rpc trustdom list \t show current interdomain trust relationships\n"); + return -1; +} + + +static NTSTATUS rpc_query_domain_sid(const DOM_SID *domain_sid, struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + fstring str_sid; + sid_to_string(str_sid, domain_sid); + d_printf("%s\n", str_sid); + return NT_STATUS_OK; +}; + + +static int rpc_trustdom_list(int argc, const char **argv) +{ + /* common variables */ + TALLOC_CTX* mem_ctx; + struct cli_state *cli, *remote_cli; + NTSTATUS nt_status; + const char *domain_name = NULL; + DOM_SID queried_dom_sid; + fstring ascii_sid, padding; + int ascii_dom_name_len; + POLICY_HND connect_hnd; + + /* trusted domains listing variables */ + int enum_ctx = 0; + int num_domains, i, pad_len, col_len = 20; + DOM_SID *domain_sids; + char **trusted_dom_names; + fstring pdc_name; + + /* trusting domains listing variables */ + POLICY_HND domain_hnd; + char **trusting_dom_names; + uint32 *trusting_dom_rids; + + /* + * Listing trusted domains (stored in secrets.tdb, if local) + */ + + mem_ctx = talloc_init("trust relationships listing"); + + /* + * set domain and pdc name to local samba server (default) + * or to remote one given in command line + */ + + if (StrCaseCmp(opt_workgroup, lp_workgroup())) { + domain_name = opt_workgroup; + opt_target_workgroup = opt_workgroup; + } else { + fstrcpy(pdc_name, lp_netbios_name()); + domain_name = talloc_strdup(mem_ctx, lp_workgroup()); + opt_target_workgroup = domain_name; + }; + + /* open \PIPE\lsarpc and open policy handle */ + if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) { + DEBUG(0, ("Couldn't connect to domain controller\n")); + return -1; + }; + + if (!cli_nt_session_open(cli, PI_LSARPC)) { + DEBUG(0, ("Could not initialise lsa pipe\n")); + return -1; + }; + + nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, + &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't open policy handle. Error was %s\n", + nt_errstr(nt_status))); + return -1; + }; + + /* query info level 5 to obtain sid of a domain being queried */ + nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd, + 5 /* info level */, domain_name, &queried_dom_sid); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("LSA Query Info failed. Returned error was %s\n", + nt_errstr(nt_status))); + return -1; + } + + /* + * Keep calling LsaEnumTrustdom over opened pipe until + * the end of enumeration is reached + */ + + d_printf("Trusted domains list:\n\n"); + + do { + nt_status = cli_lsa_enum_trust_dom(cli, mem_ctx, &connect_hnd, &enum_ctx, + &num_domains, + &trusted_dom_names, &domain_sids); + + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't enumerate trusted domains. Error was %s\n", + nt_errstr(nt_status))); + return -1; + }; + + for (i = 0; i < num_domains; i++) { + /* convert sid into ascii string */ + sid_to_string(ascii_sid, &(domain_sids[i])); + + /* calculate padding space for d_printf to look nicer */ + pad_len = col_len - strlen(trusted_dom_names[i]); + padding[pad_len] = 0; + do padding[--pad_len] = ' '; while (pad_len); + + d_printf("%s%s%s\n", trusted_dom_names[i], padding, ascii_sid); + }; + + /* + * in case of no trusted domains say something rather + * than just display blank line + */ + if (!num_domains) d_printf("none\n"); + + } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); + + /* close this connection before doing next one */ + nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't properly close lsa policy handle. Error was %s\n", + nt_errstr(nt_status))); + return -1; + }; + + cli_nt_session_close(cli); + + /* + * Listing trusting domains (stored in passdb backend, if local) + */ + + d_printf("\nTrusting domains list:\n\n"); + + /* + * Open \PIPE\samr and get needed policy handles + */ + if (!cli_nt_session_open(cli, PI_SAMR)) { + DEBUG(0, ("Could not initialise samr pipe\n")); + return -1; + }; + + /* SamrConnect */ + nt_status = cli_samr_connect(cli, mem_ctx, SA_RIGHT_SAM_OPEN_DOMAIN, + &connect_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't open SAMR policy handle. Error was %s\n", + nt_errstr(nt_status))); + return -1; + }; + + /* SamrOpenDomain - we have to open domain policy handle in order to be + able to enumerate accounts*/ + nt_status = cli_samr_open_domain(cli, mem_ctx, &connect_hnd, + SA_RIGHT_DOMAIN_ENUM_ACCOUNTS, + &queried_dom_sid, &domain_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't open domain object. Error was %s\n", + nt_errstr(nt_status))); + return -1; + }; + + /* + * perform actual enumeration + */ + + enum_ctx = 0; /* reset enumeration context from last enumeration */ + do { + + nt_status = cli_samr_enum_dom_users(cli, mem_ctx, &domain_hnd, + &enum_ctx, ACB_DOMTRUST, 0xffff, + &trusting_dom_names, &trusting_dom_rids, + &num_domains); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't enumerate accounts. Error was: %s\n", + nt_errstr(nt_status))); + return -1; + }; + + for (i = 0; i < num_domains; i++) { + + /* + * get each single domain's sid (do we _really_ need this ?): + * 1) connect to domain's pdc + * 2) query the pdc for domain's sid + */ + + /* get rid of '$' tail */ + ascii_dom_name_len = strlen(trusting_dom_names[i]); + if (ascii_dom_name_len && ascii_dom_name_len < FSTRING_LEN) + trusting_dom_names[i][ascii_dom_name_len - 1] = '\0'; + + /* calculate padding space for d_printf to look nicer */ + pad_len = col_len - strlen(trusting_dom_names[i]); + padding[pad_len] = 0; + do padding[--pad_len] = ' '; while (pad_len); + + /* set opt_* variables to remote domain */ + strupper(trusting_dom_names[i]); + opt_workgroup = talloc_strdup(mem_ctx, trusting_dom_names[i]); + opt_target_workgroup = opt_workgroup; + + d_printf("%s%s", trusting_dom_names[i], padding); + + /* connect to remote domain controller */ + remote_cli = net_make_ipc_connection(NET_FLAGS_PDC | NET_FLAGS_ANONYMOUS); + if (remote_cli) { + /* query for domain's sid */ + if (run_rpc_command(remote_cli, PI_LSARPC, 0, rpc_query_domain_sid, argc, argv)) + d_printf("couldn't get domain's sid\n"); + + cli_shutdown(remote_cli); + + } else { + d_printf("domain controller is not responding\n"); + }; + }; + + if (!num_domains) d_printf("none\n"); + + } while (NT_STATUS_EQUAL(nt_status, STATUS_MORE_ENTRIES)); + + /* close opened samr and domain policy handles */ + nt_status = cli_samr_close(cli, mem_ctx, &domain_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't properly close domain policy handle for domain %s\n", domain_name)); + }; + + nt_status = cli_samr_close(cli, mem_ctx, &connect_hnd); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0, ("Couldn't properly close samr policy handle for domain %s\n", domain_name)); + }; + + /* close samr pipe and connection to IPC$ */ + cli_nt_session_close(cli); + cli_shutdown(cli); + + talloc_destroy(mem_ctx); + return 0; +} + +/** + * Entrypoint for 'net rpc trustdom' code + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + */ + +static int rpc_trustdom(int argc, const char **argv) +{ + struct functable func[] = { + {"add", rpc_trustdom_add}, + {"del", rpc_trustdom_del}, + {"establish", rpc_trustdom_establish}, + {"revoke", rpc_trustdom_revoke}, + {"help", rpc_trustdom_usage}, + {"list", rpc_trustdom_list}, + {NULL, NULL} + }; + + if (argc == 0) { + rpc_trustdom_usage(argc, argv); + return -1; + } + + return (net_run_function(argc, argv, func, rpc_user_usage)); +} + +/** + * Check if a server will take rpc commands + * @param flags Type of server to connect to (PDC, DMB, localhost) + * if the host is not explicitly specified + * @return BOOL (true means rpc supported) + */ +BOOL net_rpc_check(unsigned flags) +{ + struct cli_state cli; + BOOL ret = False; + struct in_addr server_ip; + char *server_name = NULL; + + /* flags (i.e. server type) may depend on command */ + if (!net_find_server(flags, &server_ip, &server_name)) + return False; + + ZERO_STRUCT(cli); + if (cli_initialise(&cli) == False) + return False; + + if (!cli_connect(&cli, server_name, &server_ip)) + goto done; + if (!attempt_netbios_session_request(&cli, lp_netbios_name(), + server_name, &server_ip)) + goto done; + if (!cli_negprot(&cli)) + goto done; + if (cli.protocol < PROTOCOL_NT1) + goto done; + + ret = True; + done: + cli_shutdown(&cli); + return ret; +} + + +/****************************************************************************/ + + +/** + * Basic usage function for 'net rpc' + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_usage(int argc, const char **argv) +{ + d_printf(" net rpc info \t\t\tshow basic info about a domain \n"); + d_printf(" net rpc join \t\t\tto join a domain \n"); + d_printf(" net rpc testjoin \t\ttests that a join is valid\n"); + d_printf(" net rpc user \t\t\tto add, delete and list users\n"); + d_printf(" net rpc group \t\tto list groups\n"); + d_printf(" net rpc share \t\tto add, delete, and list shares\n"); + d_printf(" net rpc file \t\t\tto list open files\n"); + d_printf(" net rpc changetrustpw \tto change the trust account password\n"); + d_printf(" net rpc getsid \t\tfetch the domain sid into the local secrets.tdb\n"); + d_printf(" net rpc vampire \t\tsyncronise an NT PDC's users and groups into the local passdb\n"); + d_printf(" net rpc samdump \t\tdiplay an NT PDC's users, groups and other data\n"); + d_printf(" net rpc trustdom \t\tto create trusting domain's account\n" + "\t\t\t\t\tor establish trust\n"); + d_printf(" net rpc abortshutdown \tto abort the shutdown of a remote server\n"); + d_printf(" net rpc shutdown \t\tto shutdown a remote server\n"); + d_printf("\n"); + d_printf("'net rpc shutdown' also accepts the following miscellaneous options:\n"); /* misc options */ + d_printf("\t-r or --reboot\trequest remote server reboot on shutdown\n"); + d_printf("\t-f or --force\trequest the remote server force its shutdown\n"); + d_printf("\t-t or --timeout=<timeout>\tnumber of seconds before shutdown\n"); + d_printf("\t-c or --comment=<message>\ttext message to display on impending shutdown\n"); + return -1; +} + + +/** + * Help function for 'net rpc'. Calls command specific help if requested + * or displays usage of net rpc + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc_help(int argc, const char **argv) +{ + struct functable func[] = { + {"join", rpc_join_usage}, + {"user", rpc_user_usage}, + {"group", rpc_group_usage}, + {"share", rpc_share_usage}, + /*{"changetrustpw", rpc_changetrustpw_usage}, */ + {"trustdom", rpc_trustdom_usage}, + /*{"abortshutdown", rpc_shutdown_abort_usage},*/ + /*{"shutdown", rpc_shutdown_usage}, */ + {NULL, NULL} + }; + + if (argc == 0) { + net_rpc_usage(argc, argv); + return -1; + } + + return (net_run_function(argc, argv, func, rpc_user_usage)); +} + + +/** + * 'net rpc' entrypoint. + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + **/ + +int net_rpc(int argc, const char **argv) +{ + struct functable func[] = { + {"info", net_rpc_info}, + {"join", net_rpc_join}, + {"testjoin", net_rpc_testjoin}, + {"user", net_rpc_user}, + {"group", net_rpc_group}, + {"share", net_rpc_share}, + {"file", net_rpc_file}, + {"changetrustpw", rpc_changetrustpw}, + {"trustdom", rpc_trustdom}, + {"abortshutdown", rpc_shutdown_abort}, + {"shutdown", rpc_shutdown}, + {"samdump", rpc_samdump}, + {"vampire", rpc_vampire}, + {"getsid", net_rpc_getsid}, + {"help", net_rpc_help}, + {NULL, NULL} + }; + return net_run_function(argc, argv, func, net_rpc_usage); +} diff --git a/source4/utils/net_rpc_join.c b/source4/utils/net_rpc_join.c new file mode 100644 index 0000000000..4f82d0b800 --- /dev/null +++ b/source4/utils/net_rpc_join.c @@ -0,0 +1,354 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2001 Andrew Bartlett (abartlet@samba.org) + Copyright (C) Tim Potter 2001 + + This program is free software; you can 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" + +/* Macro for checking RPC error codes to make things more readable */ + +#define CHECK_RPC_ERR(rpc, msg) \ + if (!NT_STATUS_IS_OK(result = rpc)) { \ + DEBUG(0, (msg ": %s\n", nt_errstr(result))); \ + goto done; \ + } + +#define CHECK_RPC_ERR_DEBUG(rpc, debug_args) \ + if (!NT_STATUS_IS_OK(result = rpc)) { \ + DEBUG(0, debug_args); \ + goto done; \ + } + + +/** + * confirm that a domain join is still valid + * + * @return A shell status integer (0 for success) + * + **/ +int net_rpc_join_ok(const char *domain) +{ + struct cli_state *cli; + uchar stored_md4_trust_password[16]; + int retval = 1; + uint32 channel; + NTSTATUS result; + uint32 neg_flags = 0x000001ff; + + /* Connect to remote machine */ + if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) { + return 1; + } + + if (!cli_nt_session_open(cli, PI_NETLOGON)) { + DEBUG(0,("Error connecting to NETLOGON pipe\n")); + goto done; + } + + if (!secrets_fetch_trust_account_password(domain, + stored_md4_trust_password, NULL)) { + DEBUG(0,("Could not reterive domain trust secret")); + goto done; + } + + if (lp_server_role() == ROLE_DOMAIN_BDC || + lp_server_role() == ROLE_DOMAIN_PDC) { + channel = SEC_CHAN_BDC; + } else { + channel = SEC_CHAN_WKSTA; + } + + CHECK_RPC_ERR(cli_nt_setup_creds(cli, + channel, + stored_md4_trust_password, &neg_flags, 2), + "error in domain join verification"); + + retval = 0; /* Success! */ + +done: + /* Close down pipe - this will clean up open policy handles */ + if (cli->nt_pipe_fnum) + cli_nt_session_close(cli); + + cli_shutdown(cli); + + return retval; +} + +/** + * Join a domain using the administrator username and password + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped. Currently not used. + * @return A shell status integer (0 for success) + * + **/ + +int net_rpc_join_newstyle(int argc, const char **argv) +{ + + /* libsmb variables */ + + struct cli_state *cli; + TALLOC_CTX *mem_ctx; + uint32 acb_info; + + /* rpc variables */ + + POLICY_HND lsa_pol, sam_pol, domain_pol, user_pol; + DOM_SID domain_sid; + uint32 user_rid; + + /* Password stuff */ + + char *clear_trust_password = NULL; + fstring ucs2_trust_password; + int ucs2_pw_len; + uchar pwbuf[516], sess_key[16]; + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + SAM_USER_INFO_10 p10; + + /* Misc */ + + NTSTATUS result; + int retval = 1; + fstring domain; + uint32 num_rids, *name_types, *user_rids; + uint32 flags = 0x3e8; + char *acct_name; + const char *const_acct_name; + + /* Connect to remote machine */ + + if (!(cli = net_make_ipc_connection(NET_FLAGS_PDC))) + return 1; + + if (!(mem_ctx = talloc_init("net_rpc_join_newstyle"))) { + DEBUG(0, ("Could not initialise talloc context\n")); + goto done; + } + + /* Fetch domain sid */ + + if (!cli_nt_session_open(cli, PI_LSARPC)) { + DEBUG(0, ("Error connecting to SAM pipe\n")); + goto done; + } + + + CHECK_RPC_ERR(cli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &lsa_pol), + "error opening lsa policy handle"); + + CHECK_RPC_ERR(cli_lsa_query_info_policy(cli, mem_ctx, &lsa_pol, + 5, domain, &domain_sid), + "error querying info policy"); + + cli_lsa_close(cli, mem_ctx, &lsa_pol); + + cli_nt_session_close(cli); /* Done with this pipe */ + + /* Create domain user */ + if (!cli_nt_session_open(cli, PI_SAMR)) { + DEBUG(0, ("Error connecting to SAM pipe\n")); + goto done; + } + + CHECK_RPC_ERR(cli_samr_connect(cli, mem_ctx, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &sam_pol), + "could not connect to SAM database"); + + + CHECK_RPC_ERR(cli_samr_open_domain(cli, mem_ctx, &sam_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, + &domain_sid, &domain_pol), + "could not open domain"); + + /* Create domain user */ + acct_name = talloc_asprintf(mem_ctx, "%s$", lp_netbios_name()); + strlower(acct_name); + const_acct_name = acct_name; + + acb_info = ((lp_server_role() == ROLE_DOMAIN_BDC) || lp_server_role() == ROLE_DOMAIN_PDC) ? ACB_SVRTRUST : ACB_WSTRUST; + + result = cli_samr_create_dom_user(cli, mem_ctx, &domain_pol, + acct_name, acb_info, + 0xe005000b, &user_pol, + &user_rid); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, NT_STATUS_USER_EXISTS)) { + d_printf("Create of workstation account failed\n"); + + /* If NT_STATUS_ACCESS_DENIED then we have a valid + username/password combo but the user does not have + administrator access. */ + + if (NT_STATUS_V(result) == NT_STATUS_V(NT_STATUS_ACCESS_DENIED)) + d_printf("User specified does not have administrator privileges\n"); + + goto done; + } + + /* We *must* do this.... don't ask... */ + + if (NT_STATUS_IS_OK(result)) + cli_samr_close(cli, mem_ctx, &user_pol); + + CHECK_RPC_ERR_DEBUG(cli_samr_lookup_names(cli, mem_ctx, + &domain_pol, flags, + 1, &const_acct_name, + &num_rids, + &user_rids, &name_types), + ("error looking up rid for user %s: %s\n", + acct_name, nt_errstr(result))); + + if (name_types[0] != SID_NAME_USER) { + DEBUG(0, ("%s is not a user account\n", acct_name)); + goto done; + } + + user_rid = user_rids[0]; + + /* Open handle on user */ + + CHECK_RPC_ERR_DEBUG( + cli_samr_open_user(cli, mem_ctx, &domain_pol, + SEC_RIGHTS_MAXIMUM_ALLOWED, + user_rid, &user_pol), + ("could not re-open existing user %s: %s\n", + acct_name, nt_errstr(result))); + + /* Create a random machine account password */ + + { + char *str; + str = generate_random_str(DEFAULT_TRUST_ACCOUNT_PASSWORD_LENGTH); + clear_trust_password = strdup(str); + } + + ucs2_pw_len = push_ucs2(NULL, ucs2_trust_password, + clear_trust_password, + sizeof(ucs2_trust_password), 0); + + encode_pw_buffer((char *)pwbuf, ucs2_trust_password, + ucs2_pw_len); + + /* Set password on machine account */ + + ZERO_STRUCT(ctr); + ZERO_STRUCT(p24); + + init_sam_user_info24(&p24, (char *)pwbuf,24); + + ctr.switch_value = 24; + ctr.info.id24 = &p24; + + CHECK_RPC_ERR(cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24, + cli->user_session_key, &ctr), + "error setting trust account password"); + + /* Why do we have to try to (re-)set the ACB to be the same as what + we passed in the samr_create_dom_user() call? When a NT + workstation is joined to a domain by an administrator the + acb_info is set to 0x80. For a normal user with "Add + workstations to the domain" rights the acb_info is 0x84. I'm + not sure whether it is supposed to make a difference or not. NT + seems to cope with either value so don't bomb out if the set + userinfo2 level 0x10 fails. -tpot */ + + ZERO_STRUCT(ctr); + ctr.switch_value = 0x10; + ctr.info.id10 = &p10; + + init_sam_user_info10(&p10, acb_info); + + /* Ignoring the return value is necessary for joining a domain + as a normal user with "Add workstation to domain" privilege. */ + + result = cli_samr_set_userinfo2(cli, mem_ctx, &user_pol, 0x10, + sess_key, &ctr); + + /* Now store the secret in the secrets database */ + + strupper(domain); + + if (!secrets_store_domain_sid(domain, &domain_sid)) { + DEBUG(0, ("error storing domain sid for %s\n", domain)); + goto done; + } + + if (!secrets_store_machine_password(clear_trust_password)) { + DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain)); + } + + /* Now check the whole process from top-to-bottom */ + cli_samr_close(cli, mem_ctx, &user_pol); + cli_nt_session_close(cli); /* Done with this pipe */ + + retval = net_rpc_join_ok(domain); + +done: + /* Close down pipe - this will clean up open policy handles */ + + if (cli->nt_pipe_fnum) + cli_nt_session_close(cli); + + /* Display success or failure */ + + if (retval != 0) { + trust_password_delete(domain); + fprintf(stderr,"Unable to join domain %s.\n",domain); + } else { + printf("Joined domain %s.\n",domain); + } + + cli_shutdown(cli); + + SAFE_FREE(clear_trust_password); + + return retval; +} + + +/** + * check that a join is OK + * + * @return A shell status integer (0 for success) + * + **/ +int net_rpc_testjoin(int argc, const char **argv) +{ + char *domain = smb_xstrdup(lp_workgroup()); + + /* Display success or failure */ + if (net_rpc_join_ok(domain) != 0) { + fprintf(stderr,"Join to domain '%s' is not valid\n",domain); + free(domain); + return -1; + } + + printf("Join to '%s' is OK\n",domain); + free(domain); + return 0; +} diff --git a/source4/utils/net_rpc_samsync.c b/source4/utils/net_rpc_samsync.c new file mode 100644 index 0000000000..7d5c8681ad --- /dev/null +++ b/source4/utils/net_rpc_samsync.c @@ -0,0 +1,725 @@ +/* + Unix SMB/CIFS implementation. + dump the remote SAM using rpc samsync operations + + Copyright (C) Andrew Tridgell 2002 + Copyright (C) Tim Potter 2001,2002 + Modified by Volker Lendecke 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 "../utils/net.h" + +extern DOM_SID global_sid_Builtin; + +static void display_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *g) +{ + int i; + d_printf("Group mem %u: ", rid); + for (i=0;i<g->num_members;i++) { + d_printf("%u ", g->rids[i]); + } + d_printf("\n"); +} + +static void display_alias_info(uint32 rid, SAM_ALIAS_INFO *a) +{ + d_printf("Alias '%s' ", unistr2_static(&a->uni_als_name)); + d_printf("desc='%s' rid=%u\n", unistr2_static(&a->uni_als_desc), a->als_rid); +} + +static void display_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *a) +{ + int i; + d_printf("Alias rid %u: ", rid); + for (i=0;i<a->num_members;i++) { + d_printf("%s ", sid_string_static(&a->sids[i].sid)); + } + d_printf("\n"); +} + +static void display_account_info(uint32 rid, SAM_ACCOUNT_INFO *a) +{ + fstring hex_nt_passwd, hex_lm_passwd; + uchar lm_passwd[16], nt_passwd[16]; + static uchar zero_buf[16]; + + /* Decode hashes from password hash (if they are not NULL) */ + + if (memcmp(a->pass.buf_lm_pwd, zero_buf, 16) != 0) { + sam_pwd_hash(a->user_rid, a->pass.buf_lm_pwd, lm_passwd, 0); + smbpasswd_sethexpwd(hex_lm_passwd, lm_passwd, a->acb_info); + } else { + smbpasswd_sethexpwd(hex_lm_passwd, NULL, 0); + } + + if (memcmp(a->pass.buf_nt_pwd, zero_buf, 16) != 0) { + sam_pwd_hash(a->user_rid, a->pass.buf_nt_pwd, nt_passwd, 0); + smbpasswd_sethexpwd(hex_nt_passwd, nt_passwd, a->acb_info); + } else { + smbpasswd_sethexpwd(hex_nt_passwd, NULL, 0); + } + + printf("%s:%d:%s:%s:%s:LCT-0\n", unistr2_static(&a->uni_acct_name), + a->user_rid, hex_lm_passwd, hex_nt_passwd, + smbpasswd_encode_acb_info(a->acb_info)); +} + +static void display_domain_info(SAM_DOMAIN_INFO *a) +{ + d_printf("Domain name: %s\n", unistr2_static(&a->uni_dom_name)); +} + +static void display_group_info(uint32 rid, SAM_GROUP_INFO *a) +{ + d_printf("Group '%s' ", unistr2_static(&a->uni_grp_name)); + d_printf("desc='%s', rid=%u\n", unistr2_static(&a->uni_grp_desc), rid); +} + +static void display_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta) +{ + switch (hdr_delta->type) { + case SAM_DELTA_ACCOUNT_INFO: + display_account_info(hdr_delta->target_rid, &delta->account_info); + break; + case SAM_DELTA_GROUP_MEM: + display_group_mem_info(hdr_delta->target_rid, &delta->grp_mem_info); + break; + case SAM_DELTA_ALIAS_INFO: + display_alias_info(hdr_delta->target_rid, &delta->alias_info); + break; + case SAM_DELTA_ALIAS_MEM: + display_alias_mem(hdr_delta->target_rid, &delta->als_mem_info); + break; + case SAM_DELTA_DOMAIN_INFO: + display_domain_info(&delta->domain_info); + break; + case SAM_DELTA_GROUP_INFO: + display_group_info(hdr_delta->target_rid, &delta->group_info); + break; + default: + d_printf("Unknown delta record type %d\n", hdr_delta->type); + break; + } +} + + +static void dump_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds) +{ + unsigned sync_context = 0; + NTSTATUS result; + int i; + TALLOC_CTX *mem_ctx; + SAM_DELTA_HDR *hdr_deltas; + SAM_DELTA_CTR *deltas; + uint32 num_deltas; + + if (!(mem_ctx = talloc_init("dump_database"))) { + return; + } + + d_printf("Dumping database %u\n", db_type); + + do { + result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, db_type, + sync_context, + &num_deltas, &hdr_deltas, &deltas); + clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), ret_creds); + for (i = 0; i < num_deltas; i++) { + display_sam_entry(&hdr_deltas[i], &deltas[i]); + } + sync_context += 1; + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + talloc_destroy(mem_ctx); +} + +/* dump sam database via samsync rpc calls */ +int rpc_samdump(int argc, const char **argv) +{ + NTSTATUS result; + struct cli_state *cli = NULL; + uchar trust_password[16]; + DOM_CRED ret_creds; + uint32 neg_flags = 0x000001ff; + + + ZERO_STRUCT(ret_creds); + + /* Connect to remote machine */ + if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC))) { + return 1; + } + + if (!cli_nt_session_open(cli, PI_NETLOGON)) { + DEBUG(0,("Error connecting to NETLOGON pipe\n")); + goto fail; + } + + if (!secrets_fetch_trust_account_password(lp_workgroup(), trust_password, NULL)) { + d_printf("Could not retrieve domain trust secret\n"); + goto fail; + } + + result = cli_nt_setup_creds(cli, SEC_CHAN_BDC, trust_password, &neg_flags, 2); + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to setup BDC creds\n"); + goto fail; + } + + dump_database(cli, SAM_DATABASE_DOMAIN, &ret_creds); + dump_database(cli, SAM_DATABASE_BUILTIN, &ret_creds); + dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); + + cli_nt_session_close(cli); + + return 0; + +fail: + if (cli) { + cli_nt_session_close(cli); + } + return -1; +} + +/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */ + +static NTSTATUS +sam_account_from_delta(SAM_ACCOUNT *account, SAM_ACCOUNT_INFO *delta) +{ + fstring s; + uchar lm_passwd[16], nt_passwd[16]; + static uchar zero_buf[16]; + + /* Username, fullname, home dir, dir drive, logon script, acct + desc, workstations, profile. */ + + unistr2_to_ascii(s, &delta->uni_acct_name, sizeof(s) - 1); + pdb_set_nt_username(account, s, PDB_CHANGED); + + /* Unix username is the same - for sainity */ + pdb_set_username(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_full_name, sizeof(s) - 1); + pdb_set_fullname(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_home_dir, sizeof(s) - 1); + pdb_set_homedir(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_dir_drive, sizeof(s) - 1); + pdb_set_dir_drive(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_logon_script, sizeof(s) - 1); + pdb_set_logon_script(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_acct_desc, sizeof(s) - 1); + pdb_set_acct_desc(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_workstations, sizeof(s) - 1); + pdb_set_workstations(account, s, PDB_CHANGED); + + unistr2_to_ascii(s, &delta->uni_profile, sizeof(s) - 1); + pdb_set_profile_path(account, s, PDB_CHANGED); + + /* User and group sid */ + + pdb_set_user_sid_from_rid(account, delta->user_rid, PDB_CHANGED); + pdb_set_group_sid_from_rid(account, delta->group_rid, PDB_CHANGED); + + /* Logon and password information */ + + pdb_set_logon_time(account, nt_time_to_unix(&delta->logon_time), PDB_CHANGED); + pdb_set_logoff_time(account, nt_time_to_unix(&delta->logoff_time), + PDB_CHANGED); + pdb_set_logon_divs(account, delta->logon_divs, PDB_CHANGED); + + /* TODO: logon hours */ + /* TODO: bad password count */ + /* TODO: logon count */ + + pdb_set_pass_last_set_time( + account, nt_time_to_unix(&delta->pwd_last_set_time), PDB_CHANGED); + + pdb_set_kickoff_time(account, get_time_t_max(), PDB_CHANGED); + + /* Decode hashes from password hash + Note that win2000 may send us all zeros for the hashes if it doesn't + think this channel is secure enough - don't set the passwords at all + in that case + */ + if (memcmp(delta->pass.buf_lm_pwd, zero_buf, 16) != 0) { + sam_pwd_hash(delta->user_rid, delta->pass.buf_lm_pwd, lm_passwd, 0); + pdb_set_lanman_passwd(account, lm_passwd, PDB_CHANGED); + } + + if (memcmp(delta->pass.buf_nt_pwd, zero_buf, 16) != 0) { + sam_pwd_hash(delta->user_rid, delta->pass.buf_nt_pwd, nt_passwd, 0); + pdb_set_nt_passwd(account, nt_passwd, PDB_CHANGED); + } + + /* TODO: account expiry time */ + + pdb_set_acct_ctrl(account, delta->acb_info, PDB_CHANGED); + return NT_STATUS_OK; +} + +static NTSTATUS +fetch_account_info(uint32 rid, SAM_ACCOUNT_INFO *delta) +{ + NTSTATUS nt_ret; + fstring account; + pstring add_script; + SAM_ACCOUNT *sam_account=NULL; + GROUP_MAP map; + struct group *grp; + DOM_SID sid; + BOOL try_add = False; + + fstrcpy(account, unistr2_static(&delta->uni_acct_name)); + d_printf("Creating account: %s\n", account); + + if (!NT_STATUS_IS_OK(nt_ret = pdb_init_sam(&sam_account))) + return nt_ret; + + if (!pdb_getsampwnam(sam_account, account)) { + /* Create appropriate user */ + if (delta->acb_info & ACB_NORMAL) { + pstrcpy(add_script, lp_adduser_script()); + } else if ( (delta->acb_info & ACB_WSTRUST) || + (delta->acb_info & ACB_SVRTRUST) ) { + pstrcpy(add_script, lp_addmachine_script()); + } else { + DEBUG(1, ("Unknown user type: %s\n", + smbpasswd_encode_acb_info(delta->acb_info))); + pdb_free_sam(&sam_account); + return NT_STATUS_NO_SUCH_USER; + } + if (*add_script) { + int add_ret; + all_string_sub(add_script, "%u", account, + sizeof(account)); + add_ret = smbrun(add_script,NULL); + DEBUG(1,("fetch_account: Running the command `%s' " + "gave %d\n", add_script, add_ret)); + } + + try_add = True; + } + + sam_account_from_delta(sam_account, delta); + + if (try_add) { + if (!pdb_add_sam_account(sam_account)) { + DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n", + account)); + } + } else { + if (!pdb_update_sam_account(sam_account)) { + DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n", + account)); + } + } + + sid = *pdb_get_group_sid(sam_account); + + if (!pdb_getgrsid(&map, sid, False)) { + DEBUG(0, ("Primary group of %s has no mapping!\n", + pdb_get_username(sam_account))); + pdb_free_sam(&sam_account); + return NT_STATUS_NO_SUCH_GROUP; + } + + if (!(grp = getgrgid(map.gid))) { + DEBUG(0, ("Could not find unix group %d for user %s (group SID=%s)\n", + map.gid, pdb_get_username(sam_account), sid_string_static(&sid))); + pdb_free_sam(&sam_account); + return NT_STATUS_NO_SUCH_GROUP; + } + + smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account)); + + pdb_free_sam(&sam_account); + return NT_STATUS_OK; +} + +static NTSTATUS +fetch_group_info(uint32 rid, SAM_GROUP_INFO *delta) +{ + fstring name; + fstring comment; + struct group *grp = NULL; + DOM_SID group_sid; + fstring sid_string; + GROUP_MAP map; + BOOL insert = True; + + unistr2_to_ascii(name, &delta->uni_grp_name, sizeof(name)-1); + unistr2_to_ascii(comment, &delta->uni_grp_desc, sizeof(comment)-1); + + /* add the group to the mapping table */ + sid_copy(&group_sid, get_global_sam_sid()); + sid_append_rid(&group_sid, rid); + sid_to_string(sid_string, &group_sid); + + if (pdb_getgrsid(&map, group_sid, False)) { + grp = getgrgid(map.gid); + insert = False; + } + + if (grp == NULL) + { + gid_t gid; + + /* No group found from mapping, find it from its name. */ + if ((grp = getgrnam(name)) == NULL) { + /* No appropriate group found, create one */ + d_printf("Creating unix group: '%s'\n", name); + if (smb_create_group(name, &gid) != 0) + return NT_STATUS_ACCESS_DENIED; + if ((grp = getgrgid(gid)) == NULL) + return NT_STATUS_ACCESS_DENIED; + } + } + + map.gid = grp->gr_gid; + map.sid = group_sid; + map.sid_name_use = SID_NAME_DOM_GRP; + fstrcpy(map.nt_name, name); + fstrcpy(map.comment, comment); + + map.priv_set.count = 0; + map.priv_set.set = NULL; + + if (insert) + pdb_add_group_mapping_entry(&map); + else + pdb_update_group_mapping_entry(&map); + + return NT_STATUS_OK; +} + +static NTSTATUS +fetch_group_mem_info(uint32 rid, SAM_GROUP_MEM_INFO *delta) +{ + int i; + TALLOC_CTX *t = NULL; + char **nt_members = NULL; + char **unix_members; + DOM_SID group_sid; + GROUP_MAP map; + struct group *grp; + + if (delta->num_members == 0) { + return NT_STATUS_OK; + } + + sid_copy(&group_sid, get_global_sam_sid()); + sid_append_rid(&group_sid, rid); + + if (!get_domain_group_from_sid(group_sid, &map, False)) { + DEBUG(0, ("Could not find global group %d\n", rid)); + return NT_STATUS_NO_SUCH_GROUP; + } + + if (!(grp = getgrgid(map.gid))) { + DEBUG(0, ("Could not find unix group %d\n", map.gid)); + return NT_STATUS_NO_SUCH_GROUP; + } + + d_printf("Group members of %s: ", grp->gr_name); + + if (!(t = talloc_init("fetch_group_mem_info"))) { + DEBUG(0, ("could not talloc_init\n")); + return NT_STATUS_NO_MEMORY; + } + + nt_members = talloc_zero(t, sizeof(char *) * delta->num_members); + + for (i=0; i<delta->num_members; i++) { + NTSTATUS nt_status; + SAM_ACCOUNT *member = NULL; + DOM_SID member_sid; + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_talloc(t, &member))) { + talloc_destroy(t); + return nt_status; + } + + sid_copy(&member_sid, get_global_sam_sid()); + sid_append_rid(&member_sid, delta->rids[i]); + + if (!pdb_getsampwsid(member, &member_sid)) { + DEBUG(1, ("Found bogus group member: %d (member_sid=%s group=%s)\n", + delta->rids[i], sid_string_static(&member_sid), grp->gr_name)); + pdb_free_sam(&member); + continue; + } + + if (pdb_get_group_rid(member) == rid) { + d_printf("%s(primary),", pdb_get_username(member)); + pdb_free_sam(&member); + continue; + } + + d_printf("%s,", pdb_get_username(member)); + nt_members[i] = talloc_strdup(t, pdb_get_username(member)); + pdb_free_sam(&member); + } + + d_printf("\n"); + + unix_members = grp->gr_mem; + + while (*unix_members) { + BOOL is_nt_member = False; + for (i=0; i<delta->num_members; i++) { + if (nt_members[i] == NULL) { + /* This was a primary group */ + continue; + } + + if (strcmp(*unix_members, nt_members[i]) == 0) { + is_nt_member = True; + break; + } + } + if (!is_nt_member) { + /* We look at a unix group member that is not + an nt group member. So, remove it. NT is + boss here. */ + smb_delete_user_group(grp->gr_name, *unix_members); + } + unix_members += 1; + } + + for (i=0; i<delta->num_members; i++) { + BOOL is_unix_member = False; + + if (nt_members[i] == NULL) { + /* This was the primary group */ + continue; + } + + unix_members = grp->gr_mem; + + while (*unix_members) { + if (strcmp(*unix_members, nt_members[i]) == 0) { + is_unix_member = True; + break; + } + unix_members += 1; + } + + if (!is_unix_member) { + /* We look at a nt group member that is not a + unix group member currently. So, add the nt + group member. */ + smb_add_user_group(grp->gr_name, nt_members[i]); + } + } + + talloc_destroy(t); + return NT_STATUS_OK; +} + +static NTSTATUS fetch_alias_info(uint32 rid, SAM_ALIAS_INFO *delta, + DOM_SID dom_sid) +{ + fstring name; + fstring comment; + struct group *grp = NULL; + DOM_SID alias_sid; + fstring sid_string; + GROUP_MAP map; + BOOL insert = True; + + unistr2_to_ascii(name, &delta->uni_als_name, sizeof(name)-1); + unistr2_to_ascii(comment, &delta->uni_als_desc, sizeof(comment)-1); + + /* Find out whether the group is already mapped */ + sid_copy(&alias_sid, &dom_sid); + sid_append_rid(&alias_sid, rid); + sid_to_string(sid_string, &alias_sid); + + if (pdb_getgrsid(&map, alias_sid, False)) { + grp = getgrgid(map.gid); + insert = False; + } + + if (grp == NULL) { + gid_t gid; + + /* No group found from mapping, find it from its name. */ + if ((grp = getgrnam(name)) == NULL) { + /* No appropriate group found, create one */ + d_printf("Creating unix group: '%s'\n", name); + if (smb_create_group(name, &gid) != 0) + return NT_STATUS_ACCESS_DENIED; + if ((grp = getgrgid(gid)) == NULL) + return NT_STATUS_ACCESS_DENIED; + } + } + + map.gid = grp->gr_gid; + map.sid = alias_sid; + + if (sid_equal(&dom_sid, &global_sid_Builtin)) + map.sid_name_use = SID_NAME_WKN_GRP; + else + map.sid_name_use = SID_NAME_ALIAS; + + fstrcpy(map.nt_name, name); + fstrcpy(map.comment, comment); + + map.priv_set.count = 0; + map.priv_set.set = NULL; + + if (insert) + pdb_add_group_mapping_entry(&map); + else + pdb_update_group_mapping_entry(&map); + + return NT_STATUS_OK; +} + +static NTSTATUS +fetch_alias_mem(uint32 rid, SAM_ALIAS_MEM_INFO *delta, DOM_SID dom_sid) +{ + + return NT_STATUS_OK; +} + +static void +fetch_sam_entry(SAM_DELTA_HDR *hdr_delta, SAM_DELTA_CTR *delta, + DOM_SID dom_sid) +{ + switch(hdr_delta->type) { + case SAM_DELTA_ACCOUNT_INFO: + fetch_account_info(hdr_delta->target_rid, + &delta->account_info); + break; + case SAM_DELTA_GROUP_INFO: + fetch_group_info(hdr_delta->target_rid, + &delta->group_info); + break; + case SAM_DELTA_GROUP_MEM: + fetch_group_mem_info(hdr_delta->target_rid, + &delta->grp_mem_info); + break; + case SAM_DELTA_ALIAS_INFO: + fetch_alias_info(hdr_delta->target_rid, + &delta->alias_info, dom_sid); + break; + case SAM_DELTA_ALIAS_MEM: + fetch_alias_mem(hdr_delta->target_rid, + &delta->als_mem_info, dom_sid); + break; + default: + d_printf("Unknown delta record type %d\n", hdr_delta->type); + break; + } +} + +static void +fetch_database(struct cli_state *cli, unsigned db_type, DOM_CRED *ret_creds, + DOM_SID dom_sid) +{ + unsigned sync_context = 0; + NTSTATUS result; + int i; + TALLOC_CTX *mem_ctx; + SAM_DELTA_HDR *hdr_deltas; + SAM_DELTA_CTR *deltas; + uint32 num_deltas; + + if (!(mem_ctx = talloc_init("fetch_database"))) { + return; + } + + d_printf("Fetching database %u\n", db_type); + + do { + result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, + db_type, sync_context, + &num_deltas, + &hdr_deltas, &deltas); + clnt_deal_with_creds(cli->sess_key, &(cli->clnt_cred), + ret_creds); + for (i = 0; i < num_deltas; i++) { + fetch_sam_entry(&hdr_deltas[i], &deltas[i], dom_sid); + } + sync_context += 1; + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + talloc_destroy(mem_ctx); +} + +/* dump sam database via samsync rpc calls */ +int rpc_vampire(int argc, const char **argv) +{ + NTSTATUS result; + struct cli_state *cli = NULL; + uchar trust_password[16]; + DOM_CRED ret_creds; + uint32 neg_flags = 0x000001ff; + DOM_SID dom_sid; + + ZERO_STRUCT(ret_creds); + + /* Connect to remote machine */ + if (!(cli = net_make_ipc_connection(NET_FLAGS_ANONYMOUS | + NET_FLAGS_PDC))) { + return 1; + } + + if (!cli_nt_session_open(cli, PI_NETLOGON)) { + DEBUG(0,("Error connecting to NETLOGON pipe\n")); + goto fail; + } + + if (!secrets_fetch_trust_account_password(lp_workgroup(), + trust_password, NULL)) { + d_printf("Could not retrieve domain trust secret\n"); + goto fail; + } + + result = cli_nt_setup_creds(cli, SEC_CHAN_BDC, trust_password, + &neg_flags, 2); + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to setup BDC creds\n"); + goto fail; + } + + dom_sid = *get_global_sam_sid(); + fetch_database(cli, SAM_DATABASE_DOMAIN, &ret_creds, dom_sid); + + sid_copy(&dom_sid, &global_sid_Builtin); + fetch_database(cli, SAM_DATABASE_BUILTIN, &ret_creds, dom_sid); + + /* Currently we crash on PRIVS somewhere in unmarshalling */ + /* Dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); */ + + cli_nt_session_close(cli); + + return 0; + +fail: + if (cli) { + cli_nt_session_close(cli); + } + return -1; +} diff --git a/source4/utils/net_time.c b/source4/utils/net_time.c new file mode 100644 index 0000000000..4e6ce2336d --- /dev/null +++ b/source4/utils/net_time.c @@ -0,0 +1,180 @@ +/* + Samba Unix/Linux SMB client library + net time command + Copyright (C) 2001 Andrew Tridgell (tridge@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + + +/* + return the time on a server. This does not require any authentication +*/ +static time_t cli_servertime(const char *host, struct in_addr *ip, int *zone) +{ + struct nmb_name calling, called; + time_t ret = 0; + struct cli_state *cli = NULL; + + cli = cli_initialise(NULL); + if (!cli) goto done; + + if (!cli_connect(cli, host, ip)) { + fprintf(stderr,"Can't contact server\n"); + goto done; + } + + make_nmb_name(&calling, lp_netbios_name(), 0x0); + if (host) { + make_nmb_name(&called, host, 0x20); + } else { + make_nmb_name(&called, "*SMBSERVER", 0x20); + } + + if (!cli_session_request(cli, &calling, &called)) { + fprintf(stderr,"Session request failed\n"); + goto done; + } + if (!cli_negprot(cli)) { + fprintf(stderr,"Protocol negotiation failed\n"); + goto done; + } + + ret = cli->servertime; + if (zone) *zone = cli->serverzone; + +done: + if (cli) cli_shutdown(cli); + return ret; +} + +/* find the servers time on the opt_host host */ +static time_t nettime(int *zone) +{ + return cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, zone); +} + +/* return a time as a string ready to be passed to /bin/date */ +static char *systime(time_t t) +{ + static char s[100]; + struct tm *tm; + + tm = localtime(&t); + + snprintf(s, sizeof(s), "%02d%02d%02d%02d%04d.%02d", + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_year + 1900, tm->tm_sec); + return s; +} + +int net_time_usage(int argc, const char **argv) +{ + d_printf( +"net time\n\tdisplays time on a server\n\n"\ +"net time system\n\tdisplays time on a server in a format ready for /bin/date\n\n"\ +"net time set\n\truns /bin/date with the time from the server\n\n"\ +"net time zone\n\tdisplays the timezone in hours from GMT on the remote computer\n\n"\ +"\n"); + net_common_flags_usage(argc, argv); + return -1; +} + +/* try to set the system clock using /bin/date */ +static int net_time_set(int argc, const char **argv) +{ + time_t t = nettime(NULL); + char *cmd; + + if (t == 0) return -1; + + /* yes, I know this is cheesy. Use "net time system" if you want to + roll your own. I'm putting this in as it works on a large number + of systems and the user has a choice in whether its used or not */ + asprintf(&cmd, "/bin/date %s", systime(t)); + system(cmd); + free(cmd); + + return 0; +} + +/* display the time on a remote box in a format ready for /bin/date */ +static int net_time_system(int argc, const char **argv) +{ + time_t t = nettime(NULL); + + if (t == 0) return -1; + + printf("%s\n", systime(t)); + + return 0; +} + +/* display the time on a remote box in a format ready for /bin/date */ +static int net_time_zone(int argc, const char **argv) +{ + int zone = 0; + int hours, mins; + char zsign; + time_t t; + + t = nettime(&zone); + + if (t == 0) return -1; + + zsign = (zone > 0) ? '-' : '+'; + if (zone < 0) zone = -zone; + + zone /= 60; + hours = zone / 60; + mins = zone % 60; + + printf("%c%02d%02d\n", zsign, hours, mins); + + return 0; +} + +/* display or set the time on a host */ +int net_time(int argc, const char **argv) +{ + time_t t; + struct functable func[] = { + {"SYSTEM", net_time_system}, + {"SET", net_time_set}, + {"ZONE", net_time_zone}, + {NULL, NULL} + }; + + if (!opt_host && !opt_have_ip && + !find_master_ip(opt_target_workgroup, &opt_dest_ip)) { + d_printf("Could not locate a time server. Try "\ + "specifying a target host.\n"); + net_time_usage(argc,argv); + return -1; + } + + if (argc != 0) { + return net_run_function(argc, argv, func, net_time_usage); + } + + /* default - print the time */ + t = cli_servertime(opt_host, opt_have_ip? &opt_dest_ip : NULL, NULL); + if (t == 0) return -1; + + d_printf("%s", ctime(&t)); + return 0; +} diff --git a/source4/utils/nmblookup.c b/source4/utils/nmblookup.c new file mode 100644 index 0000000000..143d2ddd12 --- /dev/null +++ b/source4/utils/nmblookup.c @@ -0,0 +1,338 @@ +/* + Unix SMB/CIFS implementation. + NBT client - used to lookup netbios names + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +extern BOOL AllowDebugChange; + +static BOOL give_flags = False; +static BOOL use_bcast = True; +static BOOL got_bcast = False; +static struct in_addr bcast_addr; +static BOOL recursion_desired = False; +static BOOL translate_addresses = False; +static int ServerFD= -1; +static int RootPort = False; +static BOOL find_status=False; + +/**************************************************************************** + open the socket communication + **************************************************************************/ +static BOOL open_sockets(void) +{ + ServerFD = open_socket_in( SOCK_DGRAM, + (RootPort ? 137 : 0), + (RootPort ? 0 : 3), + interpret_addr(lp_socket_address()), True ); + + if (ServerFD == -1) + return(False); + + set_socket_options( ServerFD, "SO_BROADCAST" ); + + DEBUG(3, ("Socket opened.\n")); + return True; +} + + +/**************************************************************************** +usage on the program +****************************************************************************/ +static void usage(void) +{ + d_printf("Usage: nmblookup [options] name\n"); + d_printf("Version %s\n",VERSION); + d_printf("\t-d debuglevel set the debuglevel\n"); + d_printf("\t-B broadcast address the address to use for broadcasts\n"); + d_printf("\t-f list the NMB flags returned\n"); + d_printf("\t-U unicast address the address to use for unicast\n"); + d_printf("\t-M searches for a master browser\n"); + d_printf("\t-R set recursion desired in packet\n"); + d_printf("\t-S lookup node status as well\n"); + d_printf("\t-T translate IP addresses into names\n"); + d_printf("\t-r Use root port 137 (Win95 only replies to this)\n"); + d_printf("\t-A Do a node status on <name> as an IP Address\n"); + d_printf("\t-i NetBIOS scope Use the given NetBIOS scope for name queries\n"); + d_printf("\t-s smb.conf file Use the given path to the smb.conf file\n"); + d_printf("\t-h Print this help message.\n"); + d_printf("\n If you specify -M and name is \"-\", nmblookup looks up __MSBROWSE__<01>\n"); + d_printf("\n"); +} + +/**************************************************************************** +turn a node status flags field into a string +****************************************************************************/ +static char *node_status_flags(unsigned char flags) +{ + static fstring ret; + fstrcpy(ret,""); + + fstrcat(ret, (flags & 0x80) ? "<GROUP> " : " "); + if ((flags & 0x60) == 0x00) fstrcat(ret,"B "); + if ((flags & 0x60) == 0x20) fstrcat(ret,"P "); + if ((flags & 0x60) == 0x40) fstrcat(ret,"M "); + if ((flags & 0x60) == 0x60) fstrcat(ret,"H "); + if (flags & 0x10) fstrcat(ret,"<DEREGISTERING> "); + if (flags & 0x08) fstrcat(ret,"<CONFLICT> "); + if (flags & 0x04) fstrcat(ret,"<ACTIVE> "); + if (flags & 0x02) fstrcat(ret,"<PERMANENT> "); + + return ret; +} + +/**************************************************************************** +turn the NMB Query flags into a string +****************************************************************************/ +static char *query_flags(int flags) +{ + static fstring ret1; + fstrcpy(ret1, ""); + + if (flags & NM_FLAGS_RS) fstrcat(ret1, "Response "); + if (flags & NM_FLAGS_AA) fstrcat(ret1, "Authoritative "); + if (flags & NM_FLAGS_TC) fstrcat(ret1, "Truncated "); + if (flags & NM_FLAGS_RD) fstrcat(ret1, "Recursion_Desired "); + if (flags & NM_FLAGS_RA) fstrcat(ret1, "Recursion_Available "); + if (flags & NM_FLAGS_B) fstrcat(ret1, "Broadcast "); + + return ret1; +} + +/**************************************************************************** +do a node status query +****************************************************************************/ +static void do_node_status(int fd, const char *name, int type, struct in_addr ip) +{ + struct nmb_name nname; + int count, i, j; + struct node_status *status; + fstring cleanname; + + d_printf("Looking up status of %s\n",inet_ntoa(ip)); + make_nmb_name(&nname, name, type); + status = node_status_query(fd,&nname,ip, &count); + if (status) { + for (i=0;i<count;i++) { + fstrcpy(cleanname, status[i].name); + for (j=0;cleanname[j];j++) { + if (!isprint((int)cleanname[j])) cleanname[j] = '.'; + } + d_printf("\t%-15s <%02x> - %s\n", + cleanname,status[i].type, + node_status_flags(status[i].flags)); + } + SAFE_FREE(status); + } + d_printf("\n"); +} + + +/**************************************************************************** +send out one query +****************************************************************************/ +static BOOL query_one(const char *lookup, unsigned int lookup_type) +{ + int j, count, flags = 0; + struct in_addr *ip_list=NULL; + + if (got_bcast) { + d_printf("querying %s on %s\n", lookup, inet_ntoa(bcast_addr)); + ip_list = name_query(ServerFD,lookup,lookup_type,use_bcast, + use_bcast?True:recursion_desired, + bcast_addr,&count, &flags, NULL); + } else { + struct in_addr *bcast; + for (j=iface_count() - 1; + !ip_list && j >= 0; + j--) { + bcast = iface_n_bcast(j); + d_printf("querying %s on %s\n", + lookup, inet_ntoa(*bcast)); + ip_list = name_query(ServerFD,lookup,lookup_type, + use_bcast, + use_bcast?True:recursion_desired, + *bcast,&count, &flags, NULL); + } + } + + if (!ip_list) return False; + + if (give_flags) + d_printf("Flags: %s\n", query_flags(flags)); + + for (j=0;j<count;j++) { + if (translate_addresses) { + struct hostent *host = gethostbyaddr((char *)&ip_list[j], sizeof(ip_list[j]), AF_INET); + if (host) { + d_printf("%s, ", host -> h_name); + } + } + d_printf("%s %s<%02x>\n",inet_ntoa(ip_list[j]),lookup, lookup_type); + } + + /* We can only do find_status if the ip address returned + was valid - ie. name_query returned true. + */ + if (find_status) { + do_node_status(ServerFD, lookup, lookup_type, ip_list[0]); + } + + safe_free(ip_list); + + return (ip_list != NULL); +} + + +/**************************************************************************** + main program +****************************************************************************/ +int main(int argc,char *argv[]) +{ + int opt; + unsigned int lookup_type = 0x0; + fstring lookup; + extern int optind; + extern char *optarg; + BOOL find_master=False; + int i; + BOOL lookup_by_ip = False; + int commandline_debuglevel = -2; + + DEBUGLEVEL = 1; + /* Prevent smb.conf setting from overridding */ + AllowDebugChange = False; + + *lookup = 0; + + setup_logging(argv[0],True); + + while ((opt = getopt(argc, argv, "d:fB:U:i:s:SMrhART")) != EOF) + switch (opt) + { + case 'B': + bcast_addr = *interpret_addr2(optarg); + got_bcast = True; + use_bcast = True; + break; + case 'f': + give_flags = True; + break; + case 'U': + bcast_addr = *interpret_addr2(optarg); + got_bcast = True; + use_bcast = False; + break; + case 'T': + translate_addresses = !translate_addresses; + break; + case 'i': + lp_set_cmdline("netbios scope", optarg); + break; + case 'M': + find_master = True; + break; + case 'S': + find_status = True; + break; + case 'R': + recursion_desired = True; + break; + case 'd': + commandline_debuglevel = DEBUGLEVEL = atoi(optarg); + break; + case 's': + pstrcpy(dyn_CONFIGFILE, optarg); + break; + case 'r': + RootPort = True; + break; + case 'h': + usage(); + exit(0); + break; + case 'A': + lookup_by_ip = True; + break; + default: + usage(); + exit(1); + } + + if (argc < 2) { + usage(); + exit(1); + } + + if (!lp_load(dyn_CONFIGFILE,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE); + } + + /* + * Ensure we reset DEBUGLEVEL if someone specified it + * on the command line. + */ + + if(commandline_debuglevel != -2) + DEBUGLEVEL = commandline_debuglevel; + + load_interfaces(); + if (!open_sockets()) return(1); + + for (i=optind;i<argc;i++) + { + char *p; + struct in_addr ip; + + fstrcpy(lookup,argv[i]); + + if(lookup_by_ip) + { + fstrcpy(lookup,"*"); + ip = *interpret_addr2(argv[i]); + do_node_status(ServerFD, lookup, lookup_type, ip); + continue; + } + + if (find_master) { + if (*lookup == '-') { + fstrcpy(lookup,"\01\02__MSBROWSE__\02"); + lookup_type = 1; + } else { + lookup_type = 0x1d; + } + } + + p = strchr_m(lookup,'#'); + if (p) { + *p = '\0'; + sscanf(++p,"%x",&lookup_type); + } + + if (!query_one(lookup, lookup_type)) { + d_printf( "name_query failed to find name %s", lookup ); + if( 0 != lookup_type ) + d_printf( "#%02x", lookup_type ); + d_printf( "\n" ); + } + } + + return(0); +} diff --git a/source4/utils/ntlm_auth.c b/source4/utils/ntlm_auth.c new file mode 100644 index 0000000000..b76308c55f --- /dev/null +++ b/source4/utils/ntlm_auth.c @@ -0,0 +1,551 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2002 + Copyright (C) Andrew Bartlett 2003 + Copyright (C) Francesco Chemolli <kinkie@kame.usr.dsi.unimi.it> 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +#undef DBGC_CLASS +#define DBGC_CLASS DBGC_WINBIND + +#define SQUID_BUFFER_SIZE 2010 + +enum squid_mode { + SQUID_2_4_BASIC, + SQUID_2_5_BASIC, + SQUID_2_5_NTLMSSP +}; + + +extern int winbindd_fd; + +static const char *helper_protocol; +static const char *username; +static const char *domain; +static const char *workstation; +static const char *hex_challenge; +static const char *hex_lm_response; +static const char *hex_nt_response; +static unsigned char *challenge; +static size_t challenge_len; +static unsigned char *lm_response; +static size_t lm_response_len; +static unsigned char *nt_response; +static size_t nt_response_len; + +static char *password; + +static char winbind_separator(void) +{ + struct winbindd_response response; + static BOOL got_sep; + static char sep; + + if (got_sep) + return sep; + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request(WINBINDD_INFO, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_printf("could not obtain winbind separator!\n"); + return '\\'; + } + + sep = response.data.info.winbind_separator; + got_sep = True; + + if (!sep) { + d_printf("winbind separator was NULL!\n"); + return '\\'; + } + + return sep; +} + +static const char *get_winbind_domain(void) +{ + struct winbindd_response response; + + static fstring winbind_domain; + if (*winbind_domain) { + return winbind_domain; + } + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request(WINBINDD_DOMAIN_NAME, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_printf("could not obtain winbind domain name!\n"); + return NULL; + } + + fstrcpy(winbind_domain, response.data.domain_name); + + return winbind_domain; + +} + +static const char *get_winbind_netbios_name(void) +{ + struct winbindd_response response; + + static fstring winbind_netbios_name; + + if (*winbind_netbios_name) { + return winbind_netbios_name; + } + + ZERO_STRUCT(response); + + /* Send off request */ + + if (winbindd_request(WINBINDD_NETBIOS_NAME, NULL, &response) != + NSS_STATUS_SUCCESS) { + d_printf("could not obtain winbind netbios name!\n"); + return NULL; + } + + fstrcpy(winbind_netbios_name, response.data.netbios_name); + + return winbind_netbios_name; + +} + +/* Authenticate a user with a plaintext password */ + +static BOOL check_plaintext_auth(const char *user, const char *pass, BOOL stdout_diagnostics) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.auth.user, user); + fstrcpy(request.data.auth.pass, pass); + + result = winbindd_request(WINBINDD_PAM_AUTH, &request, &response); + + /* Display response */ + + if (stdout_diagnostics) { + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + d_printf("Reading winbind reply failed! (0x01)\n"); + } + + d_printf("%s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status); + } else { + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + DEBUG(1, ("Reading winbind reply failed! (0x01)\n")); + } + + DEBUG(3, ("%s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status)); + } + + return (result == NSS_STATUS_SUCCESS); +} + +static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.auth_crap.user, ntlmssp_state->user); + + fstrcpy(request.data.auth_crap.domain, ntlmssp_state->domain); + fstrcpy(request.data.auth_crap.workstation, ntlmssp_state->workstation); + + memcpy(request.data.auth_crap.chal, ntlmssp_state->chal.data, + MIN(ntlmssp_state->chal.length, 8)); + + memcpy(request.data.auth_crap.lm_resp, ntlmssp_state->lm_resp.data, + MIN(ntlmssp_state->lm_resp.length, sizeof(request.data.auth_crap.lm_resp))); + + memcpy(request.data.auth_crap.nt_resp, ntlmssp_state->lm_resp.data, + MIN(ntlmssp_state->nt_resp.length, sizeof(request.data.auth_crap.nt_resp))); + + request.data.auth_crap.lm_resp_len = ntlmssp_state->lm_resp.length; + request.data.auth_crap.nt_resp_len = ntlmssp_state->nt_resp.length; + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + /* Display response */ + + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS(response.data.auth.nt_status); +} + +static void manage_squid_ntlmssp_request(enum squid_mode squid_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state; + DATA_BLOB request, reply; + NTSTATUS nt_status; + + if (!ntlmssp_state) { + ntlmssp_server_start(&ntlmssp_state); + ntlmssp_state->check_password = winbind_pw_check; + ntlmssp_state->get_domain = get_winbind_domain; + ntlmssp_state->get_global_myname = get_winbind_netbios_name; + } + + if (strlen(buf) < 3) { + x_fprintf(x_stdout, "BH\n"); + return; + } + + request = base64_decode_data_blob(buf + 3); + + DEBUG(0, ("got NTLMSSP packet:\n")); + dump_data(0, request.data, request.length); + + nt_status = ntlmssp_server_update(ntlmssp_state, request, &reply); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + char *reply_base64 = base64_encode_data_blob(reply); + x_fprintf(x_stdout, "TT %s\n", reply_base64); + SAFE_FREE(reply_base64); + data_blob_free(&reply); + } else if (!NT_STATUS_IS_OK(nt_status)) { + x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); + } else { + x_fprintf(x_stdout, "AF %s\\%s\n", ntlmssp_state->domain, ntlmssp_state->user); + } + + data_blob_free(&request); +} + +static void manage_squid_basic_request(enum squid_mode squid_mode, + char *buf, int length) +{ + char *user, *pass; + user=buf; + + pass=memchr(buf,' ',length); + if (!pass) { + DEBUG(2, ("Password not found. Denying access\n")); + x_fprintf(x_stderr, "ERR\n"); + return; + } + *pass='\0'; + pass++; + + if (squid_mode == SQUID_2_5_BASIC) { + rfc1738_unescape(user); + rfc1738_unescape(pass); + } + + if (check_plaintext_auth(user, pass, False)) { + x_fprintf(x_stdout, "OK\n"); + } else { + x_fprintf(x_stdout, "ERR\n"); + } +} + +static void manage_squid_request(enum squid_mode squid_mode) +{ + char buf[SQUID_BUFFER_SIZE+1]; + int length; + char *c; + static BOOL err; + + if (x_fgets(buf, sizeof(buf)-1, x_stdin) == NULL) { + DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", errno, + strerror(errno))); + exit(1); /* BIIG buffer */ + } + + c=memchr(buf,'\n',sizeof(buf)-1); + if (c) { + *c = '\0'; + length = c-buf; + } else { + err = 1; + return; + } + if (err) { + DEBUG(2, ("Oversized message\n")); + x_fprintf(x_stderr, "ERR\n"); + err = 0; + return; + } + + DEBUG(10, ("Got '%s' from squid (length: %d).\n",buf,length)); + + if (buf[0] == '\0') { + DEBUG(2, ("Invalid Request\n")); + x_fprintf(x_stderr, "ERR\n"); + return; + } + + if (squid_mode == SQUID_2_5_BASIC || squid_mode == SQUID_2_4_BASIC) { + manage_squid_basic_request(squid_mode, buf, length); + } else if (squid_mode == SQUID_2_5_NTLMSSP) { + manage_squid_ntlmssp_request(squid_mode, buf, length); + } +} + + +static void squid_stream(enum squid_mode squid_mode) { + /* initialize FDescs */ + x_setbuf(x_stdout, NULL); + x_setbuf(x_stderr, NULL); + while(1) { + manage_squid_request(squid_mode); + } +} + + +/* Authenticate a user with a challenge/response */ + +static BOOL check_auth_crap(void) +{ + struct winbindd_request request; + struct winbindd_response response; + NSS_STATUS result; + /* Send off request */ + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + fstrcpy(request.data.auth_crap.user, username); + + fstrcpy(request.data.auth_crap.domain, domain); + fstrcpy(request.data.auth_crap.workstation, workstation); + + memcpy(request.data.auth_crap.chal, challenge, MIN(challenge_len, 8)); + + memcpy(request.data.auth_crap.lm_resp, lm_response, MIN(lm_response_len, sizeof(request.data.auth_crap.lm_resp))); + + memcpy(request.data.auth_crap.nt_resp, nt_response, MIN(nt_response_len, sizeof(request.data.auth_crap.nt_resp))); + + request.data.auth_crap.lm_resp_len = lm_response_len; + request.data.auth_crap.nt_resp_len = nt_response_len; + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + /* Display response */ + + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + d_printf("Reading winbind reply failed! (0x01)\n"); + } + + d_printf("%s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.nt_status); + + return result == NSS_STATUS_SUCCESS; +} + +/* Main program */ + +enum { + OPT_USERNAME = 1000, + OPT_DOMAIN, + OPT_WORKSTATION, + OPT_CHALLENGE, + OPT_RESPONSE, + OPT_LM, + OPT_NT, + OPT_PASSWORD +}; + +/************************************************************* + Routine to set hex password characters into an allocated array. +**************************************************************/ + +static void hex_encode(const unsigned char *buff_in, size_t len, char **out_hex_buffer) +{ + int i; + char *hex_buffer; + + *out_hex_buffer = smb_xmalloc((len*2)+1); + hex_buffer = *out_hex_buffer; + + for (i = 0; i < len; i++) + slprintf(&hex_buffer[i*2], 3, "%02X", buff_in[i]); +} + +/************************************************************* + Routine to get the 32 hex characters and turn them + into a 16 byte array. +**************************************************************/ + +static BOOL hex_decode(const char *hex_buf_in, unsigned char **out_buffer, size_t *size) +{ + int i; + size_t hex_buf_in_len = strlen(hex_buf_in); + unsigned char partial_byte_hex; + unsigned char partial_byte; + const char *hexchars = "0123456789ABCDEF"; + char *p; + BOOL high = True; + + if (!hex_buf_in) + return (False); + + *size = (hex_buf_in_len + 1) / 2; + + *out_buffer = smb_xmalloc(*size); + + for (i = 0; i < hex_buf_in_len; i++) { + partial_byte_hex = toupper(hex_buf_in[i]); + + p = strchr(hexchars, partial_byte_hex); + + if (!p) + return (False); + + partial_byte = PTR_DIFF(p, hexchars); + + if (high) { + (*out_buffer)[i / 2] = (partial_byte << 4); + } else { + (*out_buffer)[i / 2] |= partial_byte; + } + high = !high; + } + return (True); +} + + +int main(int argc, const char **argv) +{ + int opt; + + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + + { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"}, + { "username", 0, POPT_ARG_STRING, &username, OPT_USERNAME, "username"}, + { "domain", 0, POPT_ARG_STRING, &domain, OPT_DOMAIN, "domain name"}, + { "workstation", 0, POPT_ARG_STRING, &domain, OPT_WORKSTATION, "workstation"}, + { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"}, + { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"}, + { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"}, + { "password", 0, POPT_ARG_STRING, &password, OPT_PASSWORD, "User's plaintext password"}, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + { 0, 0, 0, 0 } + }; + + /* Samba client initialisation */ + + dbf = x_stderr; + + /* Parse options */ + + pc = poptGetContext("ntlm_auth", argc, argv, long_options, 0); + + /* Parse command line options */ + + if (argc == 1) { + poptPrintHelp(pc, stderr, 0); + return 1; + } + + pc = poptGetContext(NULL, argc, (const char **)argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case OPT_CHALLENGE: + if (!hex_decode(hex_challenge, &challenge, &challenge_len)) { + fprintf(stderr, "hex decode of %s failed!\n", hex_challenge); + exit(1); + } + break; + case OPT_LM: + if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) { + fprintf(stderr, "hex decode of %s failed!\n", lm_response); + exit(1); + } + break; + case OPT_NT: + if (!hex_decode(hex_lm_response, &lm_response, &lm_response_len)) { + fprintf(stderr, "hex decode of %s failed!\n", lm_response); + exit(1); + } + break; + } + } + + if (helper_protocol) { + if (strcmp(helper_protocol, "squid-2.5-ntlmssp")== 0) { + squid_stream(SQUID_2_5_NTLMSSP); + } else if (strcmp(helper_protocol, "squid-2.5-basic")== 0) { + squid_stream(SQUID_2_5_BASIC); + } else if (strcmp(helper_protocol, "squid-2.4-basic")== 0) { + squid_stream(SQUID_2_4_BASIC); + } else { + fprintf(stderr, "unknown helper protocol [%s]\n", helper_protocol); + exit(1); + } + } + + if (domain == NULL) { + domain = get_winbind_domain(); + } + + if (workstation == NULL) { + workstation = ""; + } + + if (challenge) { + if (!check_auth_crap()) { + exit(1); + } + } else if (password) { + fstring user; + snprintf(user, sizeof(user)-1, "%s%c%s", domain, winbind_separator(), username); + if (!check_plaintext_auth(user, password, True)) { + exit(1); + } + } + + /* Exit code */ + + poptFreeContext(pc); + return 0; +} diff --git a/source4/utils/pdbedit.c b/source4/utils/pdbedit.c new file mode 100644 index 0000000000..cec3e70687 --- /dev/null +++ b/source4/utils/pdbedit.c @@ -0,0 +1,696 @@ +/* + Unix SMB/CIFS implementation. + passdb editing frontend + + Copyright (C) Simo Sorce 2000 + Copyright (C) Andrew Bartlett 2001 + Copyright (C) Jelmer Vernooij 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +#define BIT_BACKEND 0x00000004 +#define BIT_VERBOSE 0x00000008 +#define BIT_SPSTYLE 0x00000010 +#define BIT_RESERV_1 0x00000020 +#define BIT_RESERV_2 0x00000040 +#define BIT_RESERV_3 0x00000080 +#define BIT_FULLNAME 0x00000100 +#define BIT_HOMEDIR 0x00000200 +#define BIT_HDIRDRIVE 0x00000400 +#define BIT_LOGSCRIPT 0x00000800 +#define BIT_PROFILE 0x00001000 +#define BIT_MACHINE 0x00002000 +#define BIT_RESERV_4 0x00004000 +#define BIT_USER 0x00008000 +#define BIT_LIST 0x00010000 +#define BIT_MODIFY 0x00020000 +#define BIT_CREATE 0x00040000 +#define BIT_DELETE 0x00080000 +#define BIT_ACCPOLICY 0x00100000 +#define BIT_ACCPOLVAL 0x00200000 +#define BIT_ACCTCTRL 0x00400000 +#define BIT_RESERV_7 0x00800000 +#define BIT_IMPORT 0x01000000 +#define BIT_EXPORT 0x02000000 + +#define MASK_ALWAYS_GOOD 0x0000001F +#define MASK_USER_GOOD 0x00401F00 + +/********************************************************* + Add all currently available users to another db + ********************************************************/ + +static int export_database (struct pdb_context *in, struct pdb_context *out) { + SAM_ACCOUNT *user = NULL; + + if (NT_STATUS_IS_ERR(in->pdb_setsampwent(in, 0))) { + fprintf(stderr, "Can't sampwent!\n"); + return 1; + } + + if (!NT_STATUS_IS_OK(pdb_init_sam(&user))) { + fprintf(stderr, "Can't initialize new SAM_ACCOUNT!\n"); + return 1; + } + + while (NT_STATUS_IS_OK(in->pdb_getsampwent(in, user))) { + out->pdb_add_sam_account(out, user); + if (!NT_STATUS_IS_OK(pdb_reset_sam(user))){ + fprintf(stderr, "Can't reset SAM_ACCOUNT!\n"); + return 1; + } + } + + in->pdb_endsampwent(in); + + return 0; +} + +/********************************************************* + Print info from sam structure +**********************************************************/ + +static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle) +{ + uid_t uid; + gid_t gid; + time_t tmp; + + /* TODO: chaeck if entry is a user or a workstation */ + if (!sam_pwent) return -1; + + if (verbosity) { + printf ("Unix username: %s\n", pdb_get_username(sam_pwent)); + printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent)); + printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN)); + + if (IS_SAM_UNIX_USER(sam_pwent)) { + uid = pdb_get_uid(sam_pwent); + gid = pdb_get_gid(sam_pwent); + printf ("User ID/Group ID: %d/%d\n", uid, gid); + } + printf ("User SID: %s\n", + sid_string_static(pdb_get_user_sid(sam_pwent))); + printf ("Primary Group SID: %s\n", + sid_string_static(pdb_get_group_sid(sam_pwent))); + printf ("Full Name: %s\n", pdb_get_fullname(sam_pwent)); + printf ("Home Directory: %s\n", pdb_get_homedir(sam_pwent)); + printf ("HomeDir Drive: %s\n", pdb_get_dir_drive(sam_pwent)); + printf ("Logon Script: %s\n", pdb_get_logon_script(sam_pwent)); + printf ("Profile Path: %s\n", pdb_get_profile_path(sam_pwent)); + printf ("Domain: %s\n", pdb_get_domain(sam_pwent)); + printf ("Account desc: %s\n", pdb_get_acct_desc(sam_pwent)); + printf ("Workstations: %s\n", pdb_get_workstations(sam_pwent)); + printf ("Munged dial: %s\n", pdb_get_munged_dial(sam_pwent)); + + tmp = pdb_get_logon_time(sam_pwent); + printf ("Logon time: %s\n", tmp ? http_timestring(tmp) : "0"); + + tmp = pdb_get_logoff_time(sam_pwent); + printf ("Logoff time: %s\n", tmp ? http_timestring(tmp) : "0"); + + tmp = pdb_get_kickoff_time(sam_pwent); + printf ("Kickoff time: %s\n", tmp ? http_timestring(tmp) : "0"); + + tmp = pdb_get_pass_last_set_time(sam_pwent); + printf ("Password last set: %s\n", tmp ? http_timestring(tmp) : "0"); + + tmp = pdb_get_pass_can_change_time(sam_pwent); + printf ("Password can change: %s\n", tmp ? http_timestring(tmp) : "0"); + + tmp = pdb_get_pass_must_change_time(sam_pwent); + printf ("Password must change: %s\n", tmp ? http_timestring(tmp) : "0"); + + } else if (smbpwdstyle) { + if (IS_SAM_UNIX_USER(sam_pwent)) { + char lm_passwd[33]; + char nt_passwd[33]; + + uid = pdb_get_uid(sam_pwent); + pdb_sethexpwd(lm_passwd, + pdb_get_lanman_passwd(sam_pwent), + pdb_get_acct_ctrl(sam_pwent)); + pdb_sethexpwd(nt_passwd, + pdb_get_nt_passwd(sam_pwent), + pdb_get_acct_ctrl(sam_pwent)); + + printf("%s:%d:%s:%s:%s:LCT-%08X:\n", + pdb_get_username(sam_pwent), + uid, + lm_passwd, + nt_passwd, + pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32)pdb_get_pass_last_set_time(sam_pwent)); + } else { + fprintf(stderr, "Can't output in smbpasswd format, no uid on this record.\n"); + } + } else { + if (IS_SAM_UNIX_USER(sam_pwent)) { + printf ("%s:%d:%s\n", pdb_get_username(sam_pwent), pdb_get_uid(sam_pwent), + pdb_get_fullname(sam_pwent)); + } else { + printf ("%s:(null):%s\n", pdb_get_username(sam_pwent), pdb_get_fullname(sam_pwent)); + } + } + + return 0; +} + +/********************************************************* + Get an Print User Info +**********************************************************/ + +static int print_user_info (struct pdb_context *in, const char *username, BOOL verbosity, BOOL smbpwdstyle) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + + if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) { + return -1; + } + + ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username)); + + if (ret==False) { + fprintf (stderr, "Username not found!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle); + pdb_free_sam(&sam_pwent); + + return ret; +} + +/********************************************************* + List Users +**********************************************************/ +static int print_users_list (struct pdb_context *in, BOOL verbosity, BOOL smbpwdstyle) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL check, ret; + + check = NT_STATUS_IS_OK(in->pdb_setsampwent(in, False)); + if (!check) { + return 1; + } + + check = True; + if (!(NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)))) return 1; + + while (check && (ret = NT_STATUS_IS_OK(in->pdb_getsampwent (in, sam_pwent)))) { + if (verbosity) + printf ("---------------\n"); + print_sam_info (sam_pwent, verbosity, smbpwdstyle); + pdb_free_sam(&sam_pwent); + check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)); + } + if (check) pdb_free_sam(&sam_pwent); + + in->pdb_endsampwent(in); + return 0; +} + +/********************************************************* + Set User Info +**********************************************************/ + +static int set_user_info (struct pdb_context *in, const char *username, + const char *fullname, const char *homedir, + const char *drive, const char *script, + const char *profile, const char *account_control) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + + pdb_init_sam(&sam_pwent); + + ret = NT_STATUS_IS_OK(in->pdb_getsampwnam (in, sam_pwent, username)); + if (ret==False) { + fprintf (stderr, "Username not found!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + if (fullname) + pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED); + if (homedir) + pdb_set_homedir(sam_pwent, homedir, PDB_CHANGED); + if (drive) + pdb_set_dir_drive(sam_pwent,drive, PDB_CHANGED); + if (script) + pdb_set_logon_script(sam_pwent, script, PDB_CHANGED); + if (profile) + pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED); + + if (account_control) { + uint16 not_settable = ~(ACB_DISABLED|ACB_HOMDIRREQ|ACB_PWNOTREQ| + ACB_PWNOEXP|ACB_AUTOLOCK); + + uint16 newflag = pdb_decode_acct_ctrl(account_control); + + if (newflag & not_settable) { + fprintf(stderr, "Can only set [NDHLX] flags\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + + pdb_set_acct_ctrl(sam_pwent, + (pdb_get_acct_ctrl(sam_pwent) & not_settable) | newflag, + PDB_CHANGED); + } + + if (NT_STATUS_IS_OK(in->pdb_update_sam_account (in, sam_pwent))) + print_user_info (in, username, True, False); + else { + fprintf (stderr, "Unable to modify entry!\n"); + pdb_free_sam(&sam_pwent); + return -1; + } + pdb_free_sam(&sam_pwent); + return 0; +} + +/********************************************************* + Add New User +**********************************************************/ +static int new_user (struct pdb_context *in, const char *username, const char *fullname, const char *homedir, const char *drive, const char *script, const char *profile) +{ + SAM_ACCOUNT *sam_pwent=NULL; + struct passwd *pwd = NULL; + char *password1, *password2, *staticpass; + + ZERO_STRUCT(sam_pwent); + + if ((pwd = getpwnam_alloc(username))) { + pdb_init_sam_pw (&sam_pwent, pwd); + passwd_free(&pwd); + } else { + fprintf (stderr, "WARNING: user %s does not exist in system passwd\n", username); + pdb_init_sam(&sam_pwent); + if (!pdb_set_username(sam_pwent, username, PDB_CHANGED)) { + return False; + } + } + + staticpass = getpass("new password:"); + password1 = strdup(staticpass); + memset(staticpass, 0, strlen(staticpass)); + staticpass = getpass("retype new password:"); + password2 = strdup(staticpass); + memset(staticpass, 0, strlen(staticpass)); + if (strcmp (password1, password2)) { + fprintf (stderr, "Passwords does not match!\n"); + memset(password1, 0, strlen(password1)); + SAFE_FREE(password1); + memset(password2, 0, strlen(password2)); + SAFE_FREE(password2); + pdb_free_sam (&sam_pwent); + return -1; + } + + pdb_set_plaintext_passwd(sam_pwent, password1); + memset(password1, 0, strlen(password1)); + SAFE_FREE(password1); + memset(password2, 0, strlen(password2)); + SAFE_FREE(password2); + + if (fullname) + pdb_set_fullname(sam_pwent, fullname, PDB_CHANGED); + if (homedir) + pdb_set_homedir (sam_pwent, homedir, PDB_CHANGED); + if (drive) + pdb_set_dir_drive (sam_pwent, drive, PDB_CHANGED); + if (script) + pdb_set_logon_script(sam_pwent, script, PDB_CHANGED); + if (profile) + pdb_set_profile_path (sam_pwent, profile, PDB_CHANGED); + + pdb_set_acct_ctrl (sam_pwent, ACB_NORMAL, PDB_CHANGED); + + if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) { + print_user_info (in, username, True, False); + } else { + fprintf (stderr, "Unable to add user! (does it alredy exist?)\n"); + pdb_free_sam (&sam_pwent); + return -1; + } + pdb_free_sam (&sam_pwent); + return 0; +} + +/********************************************************* + Add New Machine +**********************************************************/ + +static int new_machine (struct pdb_context *in, const char *machine_in) +{ + SAM_ACCOUNT *sam_pwent=NULL; + fstring machinename; + struct passwd *pwd = NULL; + char name[16]; + + fstrcpy(machinename, machine_in); + + if (machinename[strlen (machinename) -1] == '$') + machinename[strlen (machinename) -1] = '\0'; + + strlower_m(machinename); + + safe_strcpy (name, machinename, 16); + safe_strcat (name, "$", 16); + + if ((pwd = getpwnam_alloc(name))) { + if (!NT_STATUS_IS_OK(pdb_init_sam_pw( &sam_pwent, pwd))) { + fprintf(stderr, "Could not init sam from pw\n"); + passwd_free(&pwd); + return -1; + } + passwd_free(&pwd); + } else { + if (!NT_STATUS_IS_OK(pdb_init_sam (&sam_pwent))) { + fprintf(stderr, "Could not init sam from pw\n"); + return -1; + } + } + + pdb_set_plaintext_passwd (sam_pwent, machinename); + + pdb_set_username (sam_pwent, name, PDB_CHANGED); + + pdb_set_acct_ctrl (sam_pwent, ACB_WSTRUST, PDB_CHANGED); + + pdb_set_group_sid_from_rid(sam_pwent, DOMAIN_GROUP_RID_COMPUTERS, PDB_CHANGED); + + if (NT_STATUS_IS_OK(in->pdb_add_sam_account (in, sam_pwent))) { + print_user_info (in, name, True, False); + } else { + fprintf (stderr, "Unable to add machine! (does it already exist?)\n"); + pdb_free_sam (&sam_pwent); + return -1; + } + pdb_free_sam (&sam_pwent); + return 0; +} + +/********************************************************* + Delete user entry +**********************************************************/ + +static int delete_user_entry (struct pdb_context *in, const char *username) +{ + SAM_ACCOUNT *samaccount = NULL; + + if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) { + return -1; + } + + if (NT_STATUS_IS_ERR(in->pdb_getsampwnam(in, samaccount, username))) { + fprintf (stderr, "user %s does not exist in the passdb\n", username); + return -1; + } + + return NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount)); +} + +/********************************************************* + Delete machine entry +**********************************************************/ + +static int delete_machine_entry (struct pdb_context *in, const char *machinename) +{ + char name[16]; + SAM_ACCOUNT *samaccount = NULL; + + safe_strcpy (name, machinename, 16); + if (name[strlen(name)] != '$') + safe_strcat (name, "$", 16); + + if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) { + return -1; + } + + if (NT_STATUS_IS_ERR(in->pdb_getsampwnam(in, samaccount, name))) { + fprintf (stderr, "machine %s does not exist in the passdb\n", name); + return -1; + } + + return NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount)); +} + +/********************************************************* + Start here. +**********************************************************/ + +int main (int argc, char **argv) +{ + static BOOL list_users = False; + static BOOL verbose = False; + static BOOL spstyle = False; + static BOOL machine = False; + static BOOL add_user = False; + static BOOL delete_user = False; + static BOOL modify_user = False; + uint32 setparms, checkparms; + int opt; + static char *full_name = NULL; + static const char *user_name = NULL; + static char *home_dir = NULL; + static char *home_drive = NULL; + static char *backend = NULL; + static char *backend_in = NULL; + static char *backend_out = NULL; + static char *logon_script = NULL; + static char *profile_path = NULL; + static char *account_control = NULL; + static char *account_policy = NULL; + static long int account_policy_value = 0; + BOOL account_policy_value_set = False; + + struct pdb_context *bin; + struct pdb_context *bout; + struct pdb_context *bdef; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"list", 'l', POPT_ARG_NONE, &list_users, 0, "list all users", NULL}, + {"verbose", 'v', POPT_ARG_NONE, &verbose, 0, "be verbose", NULL }, + {"smbpasswd-style", 'w',POPT_ARG_NONE, &spstyle, 0, "give output in smbpasswd style", NULL}, + {"user", 'u', POPT_ARG_STRING, &user_name, 0, "use username", "USER" }, + {"fullname", 'f', POPT_ARG_STRING, &full_name, 0, "set full name", NULL}, + {"homedir", 'h', POPT_ARG_STRING, &home_dir, 0, "set home directory", NULL}, + {"drive", 'D', POPT_ARG_STRING, &home_drive, 0, "set home drive", NULL}, + {"script", 'S', POPT_ARG_STRING, &logon_script, 0, "set logon script", NULL}, + {"profile", 'p', POPT_ARG_STRING, &profile_path, 0, "set profile path", NULL}, + {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL}, + {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL}, + {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL}, + {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL}, + {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL}, + {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL}, + {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL}, + {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL}, + {"value", 'V', POPT_ARG_LONG, &account_policy_value, 'V',"set the account policy to this value", NULL}, + {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL}, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_debug }, + { NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_configfile }, + {0,0,0,0} + }; + + setup_logging("pdbedit", True); + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'V': + account_policy_value_set = True; + break; + } + } + + poptGetArg(pc); /* Drop argv[0], the program name */ + + if (user_name == NULL) + user_name = poptGetArg(pc); + + if (!lp_load(dyn_CONFIGFILE,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE); + exit(1); + } + + init_modules(); + + if (!init_names()) + exit(1); + + setparms = (backend ? BIT_BACKEND : 0) + + (verbose ? BIT_VERBOSE : 0) + + (spstyle ? BIT_SPSTYLE : 0) + + (full_name ? BIT_FULLNAME : 0) + + (home_dir ? BIT_HOMEDIR : 0) + + (home_drive ? BIT_HDIRDRIVE : 0) + + (logon_script ? BIT_LOGSCRIPT : 0) + + (profile_path ? BIT_PROFILE : 0) + + (machine ? BIT_MACHINE : 0) + + (user_name ? BIT_USER : 0) + + (list_users ? BIT_LIST : 0) + + (modify_user ? BIT_MODIFY : 0) + + (add_user ? BIT_CREATE : 0) + + (delete_user ? BIT_DELETE : 0) + + (account_control ? BIT_ACCTCTRL : 0) + + (account_policy ? BIT_ACCPOLICY : 0) + + (account_policy_value_set ? BIT_ACCPOLVAL : 0) + + (backend_in ? BIT_IMPORT : 0) + + (backend_out ? BIT_EXPORT : 0); + + if (setparms & BIT_BACKEND) { + if (!NT_STATUS_IS_OK(make_pdb_context_string(&bdef, backend))) { + fprintf(stderr, "Can't initialize passdb backend.\n"); + return 1; + } + } else { + if (!NT_STATUS_IS_OK(make_pdb_context_list(&bdef, lp_passdb_backend()))) { + fprintf(stderr, "Can't initialize passdb backend.\n"); + return 1; + } + } + + /* the lowest bit options are always accepted */ + checkparms = setparms & ~MASK_ALWAYS_GOOD; + + /* account policy operations */ + if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) { + uint32 value; + int field = account_policy_name_to_fieldnum(account_policy); + if (field == 0) { + fprintf(stderr, "No account policy by that name\n"); + exit(1); + } + if (!account_policy_get(field, &value)) { + fprintf(stderr, "valid account policy, but unable to fetch value!\n"); + exit(1); + } + if (account_policy_value_set) { + printf("account policy value for %s was %u\n", account_policy, value); + if (!account_policy_set(field, account_policy_value)) { + fprintf(stderr, "valid account policy, but unable to set value!\n"); + exit(1); + } + printf("account policy value for %s is now %lu\n", account_policy, account_policy_value); + exit(0); + } else { + printf("account policy value for %s is %u\n", account_policy, value); + exit(0); + } + } + + /* import and export operations */ + if (((checkparms & BIT_IMPORT) || (checkparms & BIT_EXPORT)) + && !(checkparms & ~(BIT_IMPORT +BIT_EXPORT))) { + if (backend_in) { + if (!NT_STATUS_IS_OK(make_pdb_context_string(&bin, backend_in))) { + fprintf(stderr, "Can't initialize passdb backend.\n"); + return 1; + } + } else { + bin = bdef; + } + if (backend_out) { + if (!NT_STATUS_IS_OK(make_pdb_context_string(&bout, backend_out))) { + fprintf(stderr, "Can't initialize %s.\n", backend_out); + return 1; + } + } else { + bout = bdef; + } + return export_database(bin, bout); + } + + /* if BIT_USER is defined but nothing else then threat it as -l -u for compatibility */ + /* fake up BIT_LIST if only BIT_USER is defined */ + if ((checkparms & BIT_USER) && !(checkparms & ~BIT_USER)) { + checkparms += BIT_LIST; + } + + /* modify flag is optional to maintain backwards compatibility */ + /* fake up BIT_MODIFY if BIT_USER and at least one of MASK_USER_GOOD is defined */ + if (!((checkparms & ~MASK_USER_GOOD) & ~BIT_USER) && (checkparms & MASK_USER_GOOD)) { + checkparms += BIT_MODIFY; + } + + /* list users operations */ + if (checkparms & BIT_LIST) { + if (!(checkparms & ~BIT_LIST)) { + return print_users_list (bdef, verbose, spstyle); + } + if (!(checkparms & ~(BIT_USER + BIT_LIST))) { + return print_user_info (bdef, user_name, verbose, spstyle); + } + } + + /* mask out users options */ + checkparms &= ~MASK_USER_GOOD; + + /* account operation */ + if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) { + /* check use of -u option */ + if (!(checkparms & BIT_USER)) { + fprintf (stderr, "Username not specified! (use -u option)\n"); + return -1; + } + + /* account creation operations */ + if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE))) { + if (checkparms & BIT_MACHINE) { + return new_machine (bdef, user_name); + } else { + return new_user (bdef, user_name, full_name, home_dir, + home_drive, logon_script, + profile_path); + } + } + + /* account deletion operations */ + if (!(checkparms & ~(BIT_DELETE + BIT_USER + BIT_MACHINE))) { + if (checkparms & BIT_MACHINE) { + return delete_machine_entry (bdef, user_name); + } else { + return delete_user_entry (bdef, user_name); + } + } + + /* account modification operations */ + if (!(checkparms & ~(BIT_MODIFY + BIT_USER))) { + return set_user_info (bdef, user_name, full_name, + home_dir, + home_drive, + logon_script, + profile_path, account_control); + } + } + + if (setparms >= 0x20) { + fprintf (stderr, "Incompatible or insufficient options on command line!\n"); + } + poptPrintHelp(pc, stderr, 0); + + return 1; +} diff --git a/source4/utils/profiles.c b/source4/utils/profiles.c new file mode 100644 index 0000000000..4f40b93810 --- /dev/null +++ b/source4/utils/profiles.c @@ -0,0 +1,729 @@ +/* + Samba Unix/Linux SMB client utility profiles.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 report and change SIDs in registry files + + 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 windows NT registry has 2 different blocks, where one can occure 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! + +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, separated 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) + +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 "includes.h" +#include <stdio.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <sys/mman.h> + +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; +} 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 sec_desc_rec { + WORD rev; + WORD type; + DWORD owner_off; + DWORD group_off; + DWORD sacl_off; + DWORD dacl_off; +} MY_SEC_DESC; + +typedef struct ace_struct { + unsigned char type; + unsigned char flags; + unsigned short length; + unsigned int perms; + DOM_SID trustee; +} ACE; + +typedef struct acl_struct { + WORD rev; + WORD size; + DWORD num_aces; + ACE *aces; /* One or more ACEs */ +} ACL; + +#define OFF(f) (0x1000 + (f) + 4) + +static void print_sid(DOM_SID *sid); + +int verbose = 1; +DOM_SID old_sid, new_sid; +int change = 0, new = 0; + +/* Compare two SIDs for equality */ +static int my_sid_equal(DOM_SID *s1, DOM_SID *s2) +{ + int sa1, sa2; + + if (s1->sid_rev_num != s2->sid_rev_num) return 0; + + sa1 = s1->num_auths; sa2 = s2->num_auths; + + if (sa1 != sa2) return 0; + + return !memcmp((char *)&s1->id_auth, (char *)&s2->id_auth, + 6 + sa1 * 4); + +} + +/* + * Quick and dirty to read a SID in S-1-5-21-x-y-z-rid format and + * construct a DOM_SID + */ +static int get_sid(DOM_SID *sid, char *sid_str) +{ + int i = 0, auth; + char *lstr; + + if (strncmp(sid_str, "S-1-5", 5)) { + fprintf(stderr, "Does not conform to S-1-5...: %s\n", sid_str); + return 0; + } + + /* We only allow strings of form S-1-5... */ + + sid->sid_rev_num = 1; + sid->id_auth[5] = 5; + + lstr = sid_str + 5; + + while (1) { + if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) { + if (i < 4) { + fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i); + return 0; + } + sid->num_auths=i; + print_sid(sid); + return 1; + } + + SIVAL(&sid->sub_auths[i], 0, auth); + i++; + lstr = strchr(lstr + 1, '-'); + } + + return 1; +} + +/* + * Replace SID1, component by component with SID2 + * Assumes will never be called with unequal length SIDS + * so only touches 21-x-y-z-rid portion + * This routine does not need to deal with endianism as + * long as the incoming SIDs are both in the same (LE) format. + */ +static void change_sid(DOM_SID *s1, DOM_SID *s2) +{ + int i; + + for (i=0; i<s1->num_auths; i++) { + s1->sub_auths[i] = s2->sub_auths[i]; + } +} + +static void print_sid(DOM_SID *sid) +{ + int i, comps = sid->num_auths; + fprintf(stdout, "S-%u-%u", sid->sid_rev_num, sid->id_auth[5]); + + for (i = 0; i < comps; i++) { + + fprintf(stdout, "-%u", IVAL(&sid->sub_auths[i],0)); + + } + fprintf(stdout, "\n"); +} + +static void process_sid(DOM_SID *sid, DOM_SID *o_sid, DOM_SID *n_sid) +{ + int i; + if (my_sid_equal(sid, o_sid)) { + + for (i=0; i<sid->num_auths; i++) { + sid->sub_auths[i] = n_sid->sub_auths[i]; + + } + + } + +} + +static void process_acl(ACL *acl, const char *prefix) +{ + int ace_cnt, i; + ACE *ace; + + ace_cnt = IVAL(&acl->num_aces, 0); + ace = (ACE *)&acl->aces; + if (verbose) fprintf(stdout, "%sACEs: %u\n", prefix, ace_cnt); + for (i=0; i<ace_cnt; i++) { + if (verbose) fprintf(stdout, "%s Perms: %08X, SID: ", prefix, + IVAL(&ace->perms, 0)); + if (change) + process_sid(&ace->trustee, &old_sid, &new_sid); + print_sid(&ace->trustee); + ace = (ACE *)((char *)ace + SVAL(&ace->length, 0)); + } +} + +static void usage(void) +{ + fprintf(stderr, "usage: profiles [-c <OLD-SID> -n <NEW-SID>] <profilefile>\n"); + fprintf(stderr, "Version: %s\n", VERSION); + fprintf(stderr, "\n\t-v\t sets verbose mode"); + fprintf(stderr, "\n\t-c S-1-5-21-z-y-x-oldrid - provides SID to change"); + fprintf(stderr, "\n\t-n S-1-5-21-a-b-c-newrid - provides SID to change to"); + fprintf(stderr, "\n\t\tBoth must be present if the other is."); + fprintf(stderr, "\n\t\tIf neither present, just report the SIDs found\n"); +} + +int main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + int opt; + int fd, start = 0; + char *base; + struct stat sbuf; + REGF_HDR *regf_hdr; + HBIN_HDR *hbin_hdr; + NK_HDR *nk_hdr; + SK_HDR *sk_hdr; + DWORD first_sk_off, sk_off; + MY_SEC_DESC *sec_desc; + int *ptr; + + if (argc < 2) { + usage(); + exit(1); + } + + /* + * Now, process the arguments + */ + + while ((opt = getopt(argc, argv, "c:n:v")) != EOF) { + switch (opt) { + case 'c': + change = 1; + if (!get_sid(&old_sid, optarg)) { + fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n"); + usage(); + exit(254); + } + break; + + case 'n': + new = 1; + if (!get_sid(&new_sid, optarg)) { + fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n"); + usage(); + exit(253); + } + + break; + + case 'v': + verbose++; + break; + + default: + usage(); + exit(255); + } + } + + if ((!change & new) || (change & !new)) { + fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n"); + usage(); + exit(252); + } + + fd = open(argv[optind], O_RDWR, 0000); + + if (fd < 0) { + fprintf(stderr, "Could not open %s: %s\n", argv[optind], + strerror(errno)); + exit(2); + } + + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "Could not stat file %s, %s\n", argv[optind], + strerror(errno)); + exit(3); + } + + /* + * Now, mmap the file into memory, check the header and start + * dealing with the records. We are interested in the sk record + */ + start = 0; + base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + + if ((int)base == -1) { + fprintf(stderr, "Could not mmap file: %s, %s\n", argv[optind], + strerror(errno)); + exit(4); + } + + /* + * In what follows, and in places above, in order to work on both LE and + * BE platforms, we have to use the Samba macros to extract SHORT, LONG + * and associated UNSIGNED quantities from the data in the mmap'd file. + * NOTE, however, that we do not need to do anything with memory + * addresses that we construct from pointers in our address space. + * For example, + * + * sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]); + * + * is simply taking the address of a structure we already have the address + * of in our address space, while, the fields within it, will have to + * be accessed with the macros: + * + * owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + + * IVAL(&sec_desc->owner_off, 0)); + * + * Which is pulling out an offset and adding it to an existing pointer. + * + */ + + regf_hdr = (REGF_HDR *)base; + + if (verbose) fprintf(stdout, "Registry file size: %u\n", (unsigned int)sbuf.st_size); + + if (IVAL(®f_hdr->REGF_ID, 0) != REG_REGF_ID) { + fprintf(stderr, "Incorrect Registry file (doesn't have header ID): %s\n", argv[optind]); + exit(5); + } + + if (verbose) fprintf(stdout, "First Key Off: %u, Data Block Size: %u\n", + IVAL(®f_hdr->first_key, 0), + IVAL(®f_hdr->dblk_size, 0)); + + hbin_hdr = (HBIN_HDR *)(base + 0x1000); /* No need for Endian stuff */ + + /* + * This should be the hbin_hdr + */ + + if (IVAL(&hbin_hdr->HBIN_ID, 0) != REG_HBIN_ID) { + fprintf(stderr, "Incorrect hbin hdr: %s\n", argv[optind]); + exit(6); + } + + if (verbose) fprintf(stdout, "Next Off: %u, Prev Off: %u\n", + IVAL(&hbin_hdr->next_off, 0), + IVAL(&hbin_hdr->prev_off, 0)); + + nk_hdr = (NK_HDR *)(base + 0x1000 + IVAL(®f_hdr->first_key, 0) + 4); + + if (SVAL(&nk_hdr->NK_ID, 0) != REG_NK_ID) { + fprintf(stderr, "Incorrect NK Header: %s\n", argv[optind]); + exit(7); + } + + sk_off = first_sk_off = IVAL(&nk_hdr->sk_off, 0); + if (verbose) { + fprintf(stdout, "Type: %0x\n", SVAL(&nk_hdr->type, 0)); + fprintf(stdout, "SK Off : %o\n", (0x1000 + sk_off + 4)); + } + + sk_hdr = (SK_HDR *)(base + 0x1000 + sk_off + 4); + + do { + DOM_SID *owner_sid, *group_sid; + ACL *sacl, *dacl; + if (SVAL(&sk_hdr->SK_ID, 0) != REG_SK_ID) { + fprintf(stderr, "Incorrect SK Header format: %08X\n", + (0x1000 + sk_off + 4)); + exit(8); + } + ptr = (int *)sk_hdr; + if (verbose) fprintf(stdout, "Off: %08X, Refs: %u, Size: %u\n", + sk_off, IVAL(&sk_hdr->ref_cnt, 0), + IVAL(&sk_hdr->rec_size, 0)); + + sec_desc = (MY_SEC_DESC *)&(sk_hdr->sec_desc[0]); + owner_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + + IVAL(&sec_desc->owner_off, 0)); + group_sid = (DOM_SID *)(&sk_hdr->sec_desc[0] + + IVAL(&sec_desc->group_off, 0)); + sacl = (ACL *)(&sk_hdr->sec_desc[0] + + IVAL(&sec_desc->sacl_off, 0)); + dacl = (ACL *)(&sk_hdr->sec_desc[0] + + IVAL(&sec_desc->dacl_off, 0)); + if (verbose)fprintf(stdout, " Owner SID: "); + if (change) process_sid(owner_sid, &old_sid, &new_sid); + if (verbose) print_sid(owner_sid); + if (verbose) fprintf(stdout, " Group SID: "); + if (change) process_sid(group_sid, &old_sid, &new_sid); + if (verbose) print_sid(group_sid); + fprintf(stdout, " SACL: "); + if (!sec_desc->sacl_off) { /* LE zero == BE zero */ + if (verbose) fprintf(stdout, "NONE\n"); + } + else + process_acl(sacl, " "); + if (verbose) fprintf(stdout, " DACL: "); + if (!sec_desc->dacl_off) { + if (verbose) fprintf(stdout, "NONE\n"); + } + else + process_acl(dacl, " "); + sk_off = IVAL(&sk_hdr->prev_off, 0); + sk_hdr = (SK_HDR *)(base + OFF(IVAL(&sk_hdr->prev_off, 0))); + } while (sk_off != first_sk_off); + + munmap(base, sbuf.st_size); + + close(fd); + return 0; +} diff --git a/source4/utils/rewrite.c b/source4/utils/rewrite.c new file mode 100644 index 0000000000..5c0b2b6956 --- /dev/null +++ b/source4/utils/rewrite.c @@ -0,0 +1,32 @@ +#include "includes.h" + +/* + + this is a set of temporary stub functions used during the samba4 rewrite. + This file will need to go away before the rewrite is complete. +*/ + +BOOL become_user_permanently(uid_t uid, gid_t gid) +{ return True; } + +BOOL is_setuid_root(void) +{ return False; } + +int share_mode_forall(SHAREMODE_FN(fn)) +{ return 0; } + +#define BRLOCK_FN(fn) \ + void (*fn)(SMB_DEV_T dev, SMB_INO_T ino, int pid, \ + enum brl_type lock_type, \ + br_off start, br_off size) +int brl_forall(BRLOCK_FN(fn)) +{ return 0; } + +BOOL locking_end(void) +{ return True; } + +BOOL locking_init(int read_only) +{ return True; } + +uid_t sec_initial_gid(void) +{ return 0; } diff --git a/source4/utils/rpccheck.c b/source4/utils/rpccheck.c new file mode 100644 index 0000000000..ae109f69b6 --- /dev/null +++ b/source4/utils/rpccheck.c @@ -0,0 +1,62 @@ +/* + Unix SMB/CIFS implementation. + + Copyright (C) Jean François Micouleau 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +main() +{ + char filter[]="0123456789ABCDEF"; + + char s[128]; + char d=0; + int x=0; + prs_struct ps; + TALLOC_CTX *ctx; + + /* change that struct */ + SAMR_R_QUERY_USERINFO rpc_stub; + + ZERO_STRUCT(rpc_stub); + + setup_logging("", True); + DEBUGLEVEL=10; + + ctx=talloc_init("main"); + if (!ctx) exit(1); + + prs_init(&ps, 1600, 4, ctx, MARSHALL); + + while (scanf("%s", s)!=-1) { + if (strlen(s)==2 && strchr_m(filter, *s)!=NULL && strchr_m(filter, *(s+1))!=NULL) { + d=strtol(s, NULL, 16); + if(!prs_append_data(&ps, &d, 1)) + printf("error while reading data\n"); + } + } + + prs_switch_type(&ps, UNMARSHALL); + prs_set_offset(&ps, 0); + + /* change that call */ + if(!samr_io_r_query_userinfo("", &rpc_stub, &ps, 0)) + printf("error while UNMARSHALLING the data\n"); + + printf("\n"); +} diff --git a/source4/utils/smbcacls.c b/source4/utils/smbcacls.c new file mode 100644 index 0000000000..4c85ea58c2 --- /dev/null +++ b/source4/utils/smbcacls.c @@ -0,0 +1,937 @@ +/* + Unix SMB/CIFS implementation. + ACL get/set utility + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +static fstring password; +static pstring username; +static pstring owner_username; +static fstring server; +static int got_pass; +static int test_args; +static TALLOC_CTX *ctx; + +#define CREATE_ACCESS_READ READ_CONTROL_ACCESS +#define CREATE_ACCESS_WRITE (WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS) + +/* numeric is set when the user wants numeric SIDs and ACEs rather + than going via LSA calls to resolve them */ +static int numeric; + +enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD }; +enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP}; +enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; + +struct perm_value { + const char *perm; + uint32 mask; +}; + +/* These values discovered by inspection */ + +static const struct perm_value special_values[] = { + { "R", 0x00120089 }, + { "W", 0x00120116 }, + { "X", 0x001200a0 }, + { "D", 0x00010000 }, + { "P", 0x00040000 }, + { "O", 0x00080000 }, + { NULL, 0 }, +}; + +static const struct perm_value standard_values[] = { + { "READ", 0x001200a9 }, + { "CHANGE", 0x001301bf }, + { "FULL", 0x001f01ff }, + { NULL, 0 }, +}; + +static struct cli_state *global_hack_cli; +static POLICY_HND pol; +static BOOL got_policy_hnd; + +static struct cli_state *connect_one(const char *share); + +/* Open cli connection and policy handle */ + +static BOOL cacls_open_policy_hnd(void) +{ + /* Initialise cli LSA connection */ + + if (!global_hack_cli) { + global_hack_cli = connect_one("IPC$"); + if (!cli_nt_session_open (global_hack_cli, PI_LSARPC)) { + return False; + } + } + + /* Open policy handle */ + + if (!got_policy_hnd) { + + /* Some systems don't support SEC_RIGHTS_MAXIMUM_ALLOWED, + but NT sends 0x2000000 so we might as well do it too. */ + + if (!NT_STATUS_IS_OK(cli_lsa_open_policy(global_hack_cli, global_hack_cli->mem_ctx, True, + GENERIC_EXECUTE_ACCESS, &pol))) { + return False; + } + + got_policy_hnd = True; + } + + return True; +} + +/* convert a SID to a string, either numeric or username/group */ +static void SidToString(fstring str, DOM_SID *sid) +{ + char **domains = NULL; + char **names = NULL; + uint32 *types = NULL; + + sid_to_string(str, sid); + + if (numeric) return; + + /* Ask LSA to convert the sid to a name */ + + if (!cacls_open_policy_hnd() || + !NT_STATUS_IS_OK(cli_lsa_lookup_sids(global_hack_cli, global_hack_cli->mem_ctx, + &pol, 1, sid, &domains, + &names, &types)) || + !domains || !domains[0] || !names || !names[0]) { + return; + } + + /* Converted OK */ + + slprintf(str, sizeof(fstring) - 1, "%s%s%s", + domains[0], lp_winbind_separator(), + names[0]); + +} + +/* convert a string to a SID, either numeric or username/group */ +static BOOL StringToSid(DOM_SID *sid, const char *str) +{ + uint32 *types = NULL; + DOM_SID *sids = NULL; + BOOL result = True; + + if (strncmp(str, "S-", 2) == 0) { + return string_to_sid(sid, str); + } + + if (!cacls_open_policy_hnd() || + !NT_STATUS_IS_OK(cli_lsa_lookup_names(global_hack_cli, global_hack_cli->mem_ctx, + &pol, 1, &str, &sids, + &types))) { + result = False; + goto done; + } + + sid_copy(sid, &sids[0]); + done: + + return result; +} + + +/* print an ACE on a FILE, using either numeric or ascii representation */ +static void print_ace(FILE *f, SEC_ACE *ace) +{ + const struct perm_value *v; + fstring sidstr; + int do_print = 0; + uint32 got_mask; + + SidToString(sidstr, &ace->trustee); + + fprintf(f, "%s:", sidstr); + + if (numeric) { + fprintf(f, "%d/%d/0x%08x", + ace->type, ace->flags, ace->info.mask); + return; + } + + /* Ace type */ + + if (ace->type == SEC_ACE_TYPE_ACCESS_ALLOWED) { + fprintf(f, "ALLOWED"); + } else if (ace->type == SEC_ACE_TYPE_ACCESS_DENIED) { + fprintf(f, "DENIED"); + } else { + fprintf(f, "%d", ace->type); + } + + /* Not sure what flags can be set in a file ACL */ + + fprintf(f, "/%d/", ace->flags); + + /* Standard permissions */ + + for (v = standard_values; v->perm; v++) { + if (ace->info.mask == v->mask) { + fprintf(f, "%s", v->perm); + return; + } + } + + /* Special permissions. Print out a hex value if we have + leftover bits in the mask. */ + + got_mask = ace->info.mask; + + again: + for (v = special_values; v->perm; v++) { + if ((ace->info.mask & v->mask) == v->mask) { + if (do_print) { + fprintf(f, "%s", v->perm); + } + got_mask &= ~v->mask; + } + } + + if (!do_print) { + if (got_mask != 0) { + fprintf(f, "0x%08x", ace->info.mask); + } else { + do_print = 1; + goto again; + } + } +} + + +/* parse an ACE in the same format as print_ace() */ +static BOOL parse_ace(SEC_ACE *ace, char *str) +{ + char *p; + const char *cp; + fstring tok; + unsigned atype, aflags, amask; + DOM_SID sid; + SEC_ACCESS mask; + const struct perm_value *v; + + ZERO_STRUCTP(ace); + p = strchr_m(str,':'); + if (!p) return False; + *p = '\0'; + p++; + /* Try to parse numeric form */ + + if (sscanf(p, "%i/%i/%i", &atype, &aflags, &amask) == 3 && + StringToSid(&sid, str)) { + goto done; + } + + /* Try to parse text form */ + + if (!StringToSid(&sid, str)) { + return False; + } + + cp = p; + if (!next_token(&cp, tok, "/", sizeof(fstring))) { + return False; + } + + if (strncmp(tok, "ALLOWED", strlen("ALLOWED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_ALLOWED; + } else if (strncmp(tok, "DENIED", strlen("DENIED")) == 0) { + atype = SEC_ACE_TYPE_ACCESS_DENIED; + } else { + return False; + } + + /* Only numeric form accepted for flags at present */ + + if (!(next_token(&cp, tok, "/", sizeof(fstring)) && + sscanf(tok, "%i", &aflags))) { + return False; + } + + if (!next_token(&cp, tok, "/", sizeof(fstring))) { + return False; + } + + if (strncmp(tok, "0x", 2) == 0) { + if (sscanf(tok, "%i", &amask) != 1) { + return False; + } + goto done; + } + + for (v = standard_values; v->perm; v++) { + if (strcmp(tok, v->perm) == 0) { + amask = v->mask; + goto done; + } + } + + p = tok; + + while(*p) { + BOOL found = False; + + for (v = special_values; v->perm; v++) { + if (v->perm[0] == *p) { + amask |= v->mask; + found = True; + } + } + + if (!found) return False; + p++; + } + + if (*p) { + return False; + } + + done: + mask.mask = amask; + init_sec_ace(ace, &sid, atype, mask, aflags); + return True; +} + +/* add an ACE to a list of ACEs in a SEC_ACL */ +static BOOL add_ace(SEC_ACL **the_acl, SEC_ACE *ace) +{ + SEC_ACL *new; + SEC_ACE *aces; + if (! *the_acl) { + (*the_acl) = make_sec_acl(ctx, 3, 1, ace); + return True; + } + + aces = calloc(1+(*the_acl)->num_aces,sizeof(SEC_ACE)); + memcpy(aces, (*the_acl)->ace, (*the_acl)->num_aces * sizeof(SEC_ACE)); + memcpy(aces+(*the_acl)->num_aces, ace, sizeof(SEC_ACE)); + new = make_sec_acl(ctx,(*the_acl)->revision,1+(*the_acl)->num_aces, aces); + SAFE_FREE(aces); + (*the_acl) = new; + return True; +} + +/* parse a ascii version of a security descriptor */ +static SEC_DESC *sec_desc_parse(char *str) +{ + const char *p = str; + fstring tok; + SEC_DESC *ret; + size_t sd_size; + DOM_SID *grp_sid=NULL, *owner_sid=NULL; + SEC_ACL *dacl=NULL; + int revision=1; + + while (next_token(&p, tok, "\t,\r\n", sizeof(tok))) { + + if (strncmp(tok,"REVISION:", 9) == 0) { + revision = strtol(tok+9, NULL, 16); + continue; + } + + if (strncmp(tok,"OWNER:", 6) == 0) { + owner_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID)); + if (!owner_sid || + !StringToSid(owner_sid, tok+6)) { + printf("Failed to parse owner sid\n"); + return NULL; + } + continue; + } + + if (strncmp(tok,"GROUP:", 6) == 0) { + grp_sid = (DOM_SID *)calloc(1, sizeof(DOM_SID)); + if (!grp_sid || + !StringToSid(grp_sid, tok+6)) { + printf("Failed to parse group sid\n"); + return NULL; + } + continue; + } + + if (strncmp(tok,"ACL:", 4) == 0) { + SEC_ACE ace; + if (!parse_ace(&ace, tok+4)) { + printf("Failed to parse ACL %s\n", tok); + return NULL; + } + if(!add_ace(&dacl, &ace)) { + printf("Failed to add ACL %s\n", tok); + return NULL; + } + continue; + } + + printf("Failed to parse security descriptor\n"); + return NULL; + } + + ret = make_sec_desc(ctx,revision, owner_sid, grp_sid, + NULL, dacl, &sd_size); + + SAFE_FREE(grp_sid); + SAFE_FREE(owner_sid); + + return ret; +} + + +/* print a ascii version of a security descriptor on a FILE handle */ +static void sec_desc_print(FILE *f, SEC_DESC *sd) +{ + fstring sidstr; + uint32 i; + + printf("REVISION:%d\n", sd->revision); + + /* Print owner and group sid */ + + if (sd->owner_sid) { + SidToString(sidstr, sd->owner_sid); + } else { + fstrcpy(sidstr, ""); + } + + printf("OWNER:%s\n", sidstr); + + if (sd->grp_sid) { + SidToString(sidstr, sd->grp_sid); + } else { + fstrcpy(sidstr, ""); + } + + fprintf(f, "GROUP:%s\n", sidstr); + + /* Print aces */ + for (i = 0; sd->dacl && i < sd->dacl->num_aces; i++) { + SEC_ACE *ace = &sd->dacl->ace[i]; + fprintf(f, "ACL:"); + print_ace(f, ace); + fprintf(f, "\n"); + } + +} + +/***************************************************** +dump the acls for a file +*******************************************************/ +static int cacl_dump(struct cli_state *cli, char *filename) +{ + int result = EXIT_FAILED; + int fnum = -1; + SEC_DESC *sd; + + if (test_args) + return EXIT_OK; + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + + if (fnum == -1) { + printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); + goto done; + } + + sd = cli_query_secdesc(cli, fnum, ctx); + + if (!sd) { + printf("ERROR: secdesc query failed: %s\n", cli_errstr(cli)); + goto done; + } + + sec_desc_print(stdout, sd); + + result = EXIT_OK; + +done: + if (fnum != -1) + cli_close(cli, fnum); + + return result; +} + +/***************************************************** +Change the ownership or group ownership of a file. Just +because the NT docs say this can't be done :-). JRA. +*******************************************************/ + +static int owner_set(struct cli_state *cli, enum chown_mode change_mode, + char *filename, char *new_username) +{ + int fnum; + DOM_SID sid; + SEC_DESC *sd, *old; + size_t sd_size; + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + + if (fnum == -1) { + printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + if (!StringToSid(&sid, new_username)) + return EXIT_PARSE_ERROR; + + old = cli_query_secdesc(cli, fnum, ctx); + + cli_close(cli, fnum); + + if (!old) { + printf("owner_set: Failed to query old descriptor\n"); + return EXIT_FAILED; + } + + sd = make_sec_desc(ctx,old->revision, + (change_mode == REQUEST_CHOWN) ? &sid : old->owner_sid, + (change_mode == REQUEST_CHGRP) ? &sid : old->grp_sid, + NULL, old->dacl, &sd_size); + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE); + + if (fnum == -1) { + printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + if (!cli_set_secdesc(cli, fnum, sd)) { + printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); + } + + cli_close(cli, fnum); + + return EXIT_OK; +} + + +/* The MSDN is contradictory over the ordering of ACE entries in an ACL. + However NT4 gives a "The information may have been modified by a + computer running Windows NT 5.0" if denied ACEs do not appear before + allowed ACEs. */ + +static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2) +{ + if (sec_ace_equal(ace1, ace2)) + return 0; + + if (ace1->type != ace2->type) + return ace2->type - ace1->type; + + if (sid_compare(&ace1->trustee, &ace2->trustee)) + return sid_compare(&ace1->trustee, &ace2->trustee); + + if (ace1->flags != ace2->flags) + return ace1->flags - ace2->flags; + + if (ace1->info.mask != ace2->info.mask) + return ace1->info.mask - ace2->info.mask; + + if (ace1->size != ace2->size) + return ace1->size - ace2->size; + + return memcmp(ace1, ace2, sizeof(SEC_ACE)); +} + +static void sort_acl(SEC_ACL *the_acl) +{ + uint32 i; + if (!the_acl) return; + + qsort(the_acl->ace, the_acl->num_aces, sizeof(the_acl->ace[0]), QSORT_CAST ace_compare); + + for (i=1;i<the_acl->num_aces;) { + if (sec_ace_equal(&the_acl->ace[i-1], &the_acl->ace[i])) { + int j; + for (j=i; j<the_acl->num_aces-1; j++) { + the_acl->ace[j] = the_acl->ace[j+1]; + } + the_acl->num_aces--; + } else { + i++; + } + } +} + +/***************************************************** +set the ACLs on a file given an ascii description +*******************************************************/ +static int cacl_set(struct cli_state *cli, char *filename, + char *the_acl, enum acl_mode mode) +{ + int fnum; + SEC_DESC *sd, *old; + uint32 i, j; + size_t sd_size; + int result = EXIT_OK; + + sd = sec_desc_parse(the_acl); + + if (!sd) return EXIT_PARSE_ERROR; + if (test_args) return EXIT_OK; + + /* The desired access below is the only one I could find that works + with NT4, W2KP and Samba */ + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_READ); + + if (fnum == -1) { + printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + old = cli_query_secdesc(cli, fnum, ctx); + + if (!old) { + printf("calc_set: Failed to query old descriptor\n"); + return EXIT_FAILED; + } + + cli_close(cli, fnum); + + /* the logic here is rather more complex than I would like */ + switch (mode) { + case SMB_ACL_DELETE: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + BOOL found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (sec_ace_equal(&sd->dacl->ace[i], + &old->dacl->ace[j])) { + uint32 k; + for (k=j; k<old->dacl->num_aces-1;k++) { + old->dacl->ace[k] = old->dacl->ace[k+1]; + } + old->dacl->num_aces--; + if (old->dacl->num_aces == 0) { + SAFE_FREE(old->dacl->ace); + SAFE_FREE(old->dacl); + old->off_dacl = 0; + } + found = True; + break; + } + } + + if (!found) { + printf("ACL for ACE:"); + print_ace(stdout, &sd->dacl->ace[i]); + printf(" not found\n"); + } + } + break; + + case SMB_ACL_MODIFY: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + BOOL found = False; + + for (j=0;old->dacl && j<old->dacl->num_aces;j++) { + if (sid_equal(&sd->dacl->ace[i].trustee, + &old->dacl->ace[j].trustee)) { + old->dacl->ace[j] = sd->dacl->ace[i]; + found = True; + } + } + + if (!found) { + fstring str; + + SidToString(str, &sd->dacl->ace[i].trustee); + printf("ACL for SID %s not found\n", str); + } + } + + break; + + case SMB_ACL_ADD: + for (i=0;sd->dacl && i<sd->dacl->num_aces;i++) { + add_ace(&old->dacl, &sd->dacl->ace[i]); + } + break; + + case SMB_ACL_SET: + old = sd; + break; + } + + /* Denied ACE entries must come before allowed ones */ + sort_acl(old->dacl); + + /* Create new security descriptor and set it */ + sd = make_sec_desc(ctx,old->revision, old->owner_sid, old->grp_sid, + NULL, old->dacl, &sd_size); + + fnum = cli_nt_create(cli, filename, CREATE_ACCESS_WRITE); + + if (fnum == -1) { + printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + if (!cli_set_secdesc(cli, fnum, sd)) { + printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); + result = EXIT_FAILED; + } + + /* Clean up */ + + cli_close(cli, fnum); + + return result; +} + + +/***************************************************** +return a connection to a server +*******************************************************/ +static struct cli_state *connect_one(const char *share) +{ + struct cli_state *c; + struct in_addr ip; + NTSTATUS nt_status; + zero_ip(&ip); + + if (!got_pass) { + char *pass = getpass("Password: "); + if (pass) { + fstrcpy(password, pass); + got_pass = True; + } + } + + if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, lp_netbios_name(), server, + &ip, 0, + share, "?????", + username, lp_workgroup(), + password, 0, NULL))) { + return c; + } else { + DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); + return NULL; + } +} + + +static void usage(void) +{ + printf( +"Usage: smbcacls //server1/share1 filename [options]\n\ +\n\ +\t-D <acls> delete an acl\n\ +\t-M <acls> modify an acl\n\ +\t-A <acls> add an acl\n\ +\t-S <acls> set acls\n\ +\t-C username change ownership of a file\n\ +\t-G username change group ownership of a file\n\ +\t-n don't resolve sids or masks to names\n\ +\t-h print help\n\ +\t-d debuglevel set debug output level\n\ +\t-U username user to autheticate as\n\ +\n\ +The username can be of the form username%%password or\n\ +workgroup\\username%%password.\n\n\ +An acl is of the form ACL:<SID>:type/flags/mask\n\ +You can string acls together with spaces, commas or newlines\n\ +"); +} + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc,char *argv[]) +{ + char *share; + pstring filename; + extern char *optarg; + extern int optind; + int opt; + char *p; + enum acl_mode mode = SMB_ACL_SET; + char *the_acl = NULL; + enum chown_mode change_mode = REQUEST_NONE; + int result; + + struct cli_state *cli; + + ctx=talloc_init("main"); + + setlinebuf(stdout); + + dbf = x_stderr; + + if (argc < 3 || argv[1][0] == '-') { + usage(); + talloc_destroy(ctx); + exit(EXIT_PARSE_ERROR); + } + + setup_logging(argv[0],True); + + share = argv[1]; + pstrcpy(filename, argv[2]); + all_string_sub(share,"/","\\",0); + + argc -= 2; + argv += 2; + + lp_load(dyn_CONFIGFILE,True,False,False); + load_interfaces(); + + if (getenv("USER")) { + pstrcpy(username,getenv("USER")); + + if ((p=strchr_m(username,'%'))) { + *p = 0; + fstrcpy(password,p+1); + got_pass = True; + memset(strchr_m(getenv("USER"), '%') + 1, 'X', + strlen(password)); + } + } + + while ((opt = getopt(argc, argv, "U:nhS:D:A:M:C:G:td:")) != EOF) { + switch (opt) { + case 'U': + pstrcpy(username,optarg); + p = strchr_m(username,'%'); + if (p) { + *p = 0; + fstrcpy(password, p+1); + got_pass = 1; + } + break; + + case 'S': + the_acl = optarg; + mode = SMB_ACL_SET; + break; + + case 'D': + the_acl = optarg; + mode = SMB_ACL_DELETE; + break; + + case 'M': + the_acl = optarg; + mode = SMB_ACL_MODIFY; + break; + + case 'A': + the_acl = optarg; + mode = SMB_ACL_ADD; + break; + + case 'C': + pstrcpy(owner_username,optarg); + change_mode = REQUEST_CHOWN; + break; + + case 'G': + pstrcpy(owner_username,optarg); + change_mode = REQUEST_CHGRP; + break; + + case 'n': + numeric = 1; + break; + + case 't': + test_args = 1; + break; + + case 'h': + usage(); + talloc_destroy(ctx); + exit(EXIT_PARSE_ERROR); + + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + + default: + printf("Unknown option %c (%d)\n", (char)opt, opt); + talloc_destroy(ctx); + exit(EXIT_PARSE_ERROR); + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + usage(); + talloc_destroy(ctx); + exit(EXIT_PARSE_ERROR); + } + + /* Make connection to server */ + + fstrcpy(server,share+2); + share = strchr_m(server,'\\'); + if (!share) { + share = strchr_m(server,'/'); + if (!share) { + return -1; + } + } + + *share = 0; + share++; + + if (!test_args) { + cli = connect_one(share); + if (!cli) { + talloc_destroy(ctx); + exit(EXIT_FAILED); + } + } else { + exit(0); + } + + all_string_sub(filename, "/", "\\", 0); + if (filename[0] != '\\') { + pstring s; + s[0] = '\\'; + safe_strcpy(&s[1], filename, sizeof(pstring)-1); + pstrcpy(filename, s); + } + + /* Perform requested action */ + + if (change_mode != REQUEST_NONE) { + result = owner_set(cli, change_mode, filename, owner_username); + } else if (the_acl) { + result = cacl_set(cli, filename, the_acl, mode); + } else { + result = cacl_dump(cli, filename); + } + + talloc_destroy(ctx); + + return result; +} + diff --git a/source4/utils/smbcontrol.c b/source4/utils/smbcontrol.c new file mode 100644 index 0000000000..0861eb5c5a --- /dev/null +++ b/source4/utils/smbcontrol.c @@ -0,0 +1,714 @@ +/* + Unix SMB/CIFS implementation. + program to send control messages to Samba processes + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) 2001, 2002 by Martin Pool + Copyright (C) Simo Sorce 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" + +extern BOOL AllowDebugChange; + +static const struct { + const char *name; + int value; +} msg_types[] = { + {"debug", MSG_DEBUG}, + {"force-election", MSG_FORCE_ELECTION}, + {"ping", MSG_PING}, + {"profile", MSG_PROFILE}, + {"profilelevel", MSG_REQ_PROFILELEVEL}, + {"debuglevel", MSG_REQ_DEBUGLEVEL}, + {"printnotify", MSG_PRINTER_NOTIFY2 }, + {"close-share", MSG_SMB_FORCE_TDIS}, + {"samsync", MSG_SMB_SAM_SYNC}, + {"samrepl", MSG_SMB_SAM_REPL}, + {"pool-usage", MSG_REQ_POOL_USAGE }, + {"dmalloc-mark", MSG_REQ_DMALLOC_MARK }, + {"dmalloc-log-changed", MSG_REQ_DMALLOC_LOG_CHANGED }, + {"shutdown", MSG_SHUTDOWN }, + {"drvupgrade", MSG_PRINTER_DRVUPGRADE}, + {"tallocdump", MSG_REQ_TALLOC_USAGE}, + {NULL, -1} +}; + +time_t timeout_start; + +#define MAX_WAIT 10 + +/* we need these because we link to printing*.o */ + +void become_root(void) {} +void unbecome_root(void) {} + + +static void usage(BOOL doexit) +{ + int i; + if (doexit) { + printf("Usage: smbcontrol -i -s configfile\n"); + printf(" smbcontrol <destination> <message-type> <parameters>\n\n"); + } else { + printf("<destination> <message-type> <parameters>\n\n"); + } + printf("\t<destination> is one of \"nmbd\", \"smbd\" or a process ID\n"); + printf("\t<message-type> is one of:\n"); + for (i=0; msg_types[i].name; i++) + printf("\t\t%s\n", msg_types[i].name); + printf("\n"); + if (doexit) exit(1); +} + +static int pong_count; +static BOOL got_level; +static BOOL got_pool; +static BOOL pong_registered = False; +static BOOL debuglevel_registered = False; +static BOOL poolusage_registered = False; +static BOOL profilelevel_registered = False; + + +/** + * Wait for replies for up to @p *max_secs seconds, or until @p + * max_replies are received. max_replies may be NULL in which case it + * is ignored. + * + * @note This is a pretty lame timeout; all it means is that after + * max_secs we won't look for any more messages. + **/ +static void wait_for_replies(int max_secs, int *max_replies) +{ + time_t timeout_end = time(NULL) + max_secs; + + while ((!max_replies || (*max_replies)-- > 0) + && (time(NULL) < timeout_end)) { + message_dispatch(); + } +} + + +/**************************************************************************** +a useful function for testing the message system +****************************************************************************/ +void pong_function(int msg_type, pid_t src, void *buf, size_t len) +{ + pong_count++; + printf("PONG from PID %u\n",(unsigned int)src); +} + +/**************************************************************************** + Prints out the current talloc list. +****************************************************************************/ +void tallocdump_function(int msg_type, pid_t src, void *buf, size_t len) +{ + char *info = (char *)buf; + + printf("Current talloc contexts for process %u\n", (unsigned int)src ); + if (len == 0) + printf("None returned\n"); + else + printf(info); + printf("\n"); + got_pool = True; +} + +/**************************************************************************** +Prints out the current Debug level returned by MSG_DEBUGLEVEL +****************************************************************************/ +void debuglevel_function(int msg_type, pid_t src, void *buf, size_t len) +{ + const char *levels = (char *)buf; + + printf("Current debug levels of PID %u are:\n",(unsigned int)src); + printf("%s\n", levels); + + got_level = True; +} + +/**************************************************************************** +Prints out the current Profile level returned by MSG_PROFILELEVEL +****************************************************************************/ +void profilelevel_function(int msg_type, pid_t src, void *buf, size_t len) +{ + int level; + const char *s=NULL; + memcpy(&level, buf, sizeof(int)); + + if (level) { + switch (level) { + case 1: + s = "off"; + break; + case 3: + s = "count only"; + break; + case 7: + s = "count and time"; + break; + default: + s = "BOGUS"; + break; + } + printf("Profiling %s on PID %u\n",s,(unsigned int)src); + } else { + printf("Profiling not available on PID %u\n",(unsigned int)src); + } + got_level = True; +} + +/** + * Handle reply from POOL_USAGE. + **/ +static void pool_usage_cb(int msg_type, pid_t src_pid, void *buf, size_t len) +{ + printf("Got POOL_USAGE reply from pid%u:\n%.*s", + (unsigned int) src_pid, (int) len, (const char *) buf); +} + + +/** + * Send a message to a named destination + * + * @return False if an error occurred. + **/ +static BOOL send_message(char *dest, int msg_type, void *buf, int len, BOOL duplicates) +{ + pid_t pid; + /* "smbd" is the only broadcast operation */ + if (strequal(dest,"smbd")) { + TDB_CONTEXT *tdb; + BOOL ret; + int n_sent = 0; + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDWR, 0); + if (!tdb) { + fprintf(stderr,"Failed to open connections database in send_message.\n"); + return False; + } + + ret = message_send_all(tdb,msg_type, buf, len, duplicates, + &n_sent); + DEBUG(10,("smbcontrol/send_message: broadcast message to " + "%d processes\n", n_sent)); + tdb_close(tdb); + + return ret; + } else if (strequal(dest,"nmbd")) { + pid = pidfile_pid(dest); + if (pid == 0) { + fprintf(stderr,"Can't find pid for nmbd\n"); + return False; + } + } else if (strequal(dest,"self")) { + pid = sys_getpid(); + } else { + pid = atoi(dest); + if (pid == 0) { + fprintf(stderr,"Not a valid pid\n"); + return False; + } + } + + DEBUG(10,("smbcontrol/send_message: send message to pid%d\n", pid)); + return message_send_pid(pid, msg_type, buf, len, duplicates); +} + +/**************************************************************************** +evaluate a message type string +****************************************************************************/ +static int parse_type(char *mtype) +{ + int i; + for (i=0;msg_types[i].name;i++) { + if (strequal(mtype, msg_types[i].name)) return msg_types[i].value; + } + return -1; +} + + +static void register_all(void) +{ + message_register(MSG_POOL_USAGE, pool_usage_cb); +} + +/* This guy is here so we can link printing/notify.c to the smbcontrol + binary without having to pull in tons of other crap. */ + +TDB_CONTEXT *conn_tdb_ctx(void) +{ + static TDB_CONTEXT *tdb; + + if (tdb) + return tdb; + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0); + + if (!tdb) + DEBUG(3, ("Failed to open connections database in send_spoolss_notify2_msg\n")); + + return tdb; +} + +/**************************************************************************** +do command +****************************************************************************/ +static BOOL do_command(char *dest, char *msg_name, int iparams, char **params) +{ + int i, n, v; + int mtype; + BOOL retval=False; + BOOL check_notify_msgs = False; + + mtype = parse_type(msg_name); + if (mtype == -1) { + fprintf(stderr,"Couldn't resolve message type: %s\n", msg_name); + return(False); + } + + switch (mtype) { + case MSG_DEBUG: { + char *buf, *b; + char **p; + int dim = 0; + + if (!params || !params[0]) { + fprintf(stderr,"MSG_DEBUG needs a parameter\n"); + return(False); + } + + /* first pass retrieve total lenght */ + for (p = params; p && *p ; p++) + dim += (strnlen(*p, 1024) +1); /* lenght + space */ + b = buf = malloc(dim); + if (!buf) { + fprintf(stderr, "Out of memory!"); + return(False); + } + /* now build a single string with all parameters */ + for(p = params; p && *p; p++) { + int l = strnlen(*p, 1024); + strncpy(b, *p, l); + b[l] = ' '; + b = b + l + 1; + } + b[-1] = '\0'; + + send_message(dest, MSG_DEBUG, buf, dim, False); + + free(buf); + + break; + } + + case MSG_PROFILE: + if (!params || !params[0]) { + fprintf(stderr,"MSG_PROFILE needs a parameter\n"); + return(False); + } + if (strequal(params[0], "off")) { + v = 0; + } else if (strequal(params[0], "count")) { + v = 1; + } else if (strequal(params[0], "on")) { + v = 2; + } else if (strequal(params[0], "flush")) { + v = 3; + } else { + fprintf(stderr, + "MSG_PROFILE parameter must be off, count, on, or flush\n"); + return(False); + } + send_message(dest, MSG_PROFILE, &v, sizeof(int), False); + break; + + case MSG_FORCE_ELECTION: + if (!strequal(dest, "nmbd")) { + fprintf(stderr,"force-election can only be sent to nmbd\n"); + return(False); + } + send_message(dest, MSG_FORCE_ELECTION, NULL, 0, False); + break; + + case MSG_REQ_PROFILELEVEL: + if (!profilelevel_registered) { + message_register(MSG_PROFILELEVEL, profilelevel_function); + profilelevel_registered = True; + } + got_level = False; + retval = send_message(dest, MSG_REQ_PROFILELEVEL, NULL, 0, True); + if (retval) { + timeout_start = time(NULL); + while (!got_level) { + message_dispatch(); + if ((time(NULL) - timeout_start) > MAX_WAIT) { + fprintf(stderr,"profilelevel timeout\n"); + break; + } + } + } + break; + + case MSG_REQ_TALLOC_USAGE: + if (!poolusage_registered) { + message_register(MSG_TALLOC_USAGE, tallocdump_function); + poolusage_registered = True; + } + got_pool = False; + retval = send_message(dest, MSG_REQ_TALLOC_USAGE, NULL, 0, True); + if (retval) { + timeout_start = time(NULL); + while (!got_pool) { + message_dispatch(); + if ((time(NULL) - timeout_start) > MAX_WAIT) { + fprintf(stderr,"tallocdump timeout\n"); + break; + } + } + } + break; + + case MSG_REQ_DEBUGLEVEL: + if (!debuglevel_registered) { + message_register(MSG_DEBUGLEVEL, debuglevel_function); + debuglevel_registered = True; + } + got_level = False; + retval = send_message(dest, MSG_REQ_DEBUGLEVEL, NULL, 0, True); + if (retval) { + timeout_start = time(NULL); + while (!got_level) { + message_dispatch(); + if ((time(NULL) - timeout_start) > MAX_WAIT) { + fprintf(stderr,"debuglevel timeout\n"); + break; + } + } + } + break; + + /* Send a notification message to a printer */ + + case MSG_PRINTER_NOTIFY2: { + char *cmd; + + /* Read subcommand */ + + if (!params || !params[0]) { + fprintf(stderr, "Must specify subcommand:\n"); + fprintf(stderr, "\tqueuepause <printername>\n"); + fprintf(stderr, "\tqueueresume <printername>\n"); + fprintf(stderr, "\tjobpause <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobresume <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n"); + fprintf(stderr, "\tprinter <printername> <comment|port|driver> <new value>\n"); + return False; + } + + cmd = params[0]; + + check_notify_msgs = True; + + /* Pause a print queue */ + + if (strequal(cmd, "queuepause")) { + + if (!params[1]) { + fprintf(stderr, "queuepause command requires a printer name\n"); + return False; + } + + //TODL: notify_printer_status_byname(params[1], PRINTER_STATUS_PAUSED); + break; + } + + /* Resume a print queue */ + + if (strequal(cmd, "queueresume")) { + + if (!params[1]) { + fprintf(stderr, "queueresume command requires a printer name\n"); + return False; + } + + //TODL: notify_printer_status_byname(params[1], PRINTER_STATUS_OK); + break; + } + + /* Pause a print job */ + + if (strequal(cmd, "jobpause")) { + int jobid; + + if (!params[1] || !params[2]) { + fprintf(stderr, "jobpause command requires a printer name and a jobid\n"); + return False; + } + + jobid = atoi(params[2]); + + //TODL: notify_job_status_byname( + //TODL: params[1], jobid, JOB_STATUS_PAUSED, + //TODL: SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + break; + } + + /* Resume a print job */ + + if (strequal(cmd, "jobresume")) { + int jobid; + + if (!params[1] || !params[2]) { + fprintf(stderr, "jobresume command requires a printer name and a jobid\n"); + return False; + } + + jobid = atoi(params[2]); + + //TODL: notify_job_status_byname( + //TODL: params[1], jobid, JOB_STATUS_QUEUED, + //TODL: SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + break; + } + + /* Delete a print job */ + + if (strequal(cmd, "jobdelete")) { + int jobid; + + if (!params[1] || !params[2]) { + fprintf(stderr, "jobdelete command requires a printer name and a jobid\n"); + return False; + } + + jobid = atoi(params[2]); + + //TODL: notify_job_status_byname( + //TODL: params[1], jobid, JOB_STATUS_DELETING, + //TODL: SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + //TODL: notify_job_status_byname( + //TODL: params[1], jobid, JOB_STATUS_DELETING| + //TODL: JOB_STATUS_DELETED, + //TODL: SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + } + + /* printer change notify */ + + if (strequal(cmd, "printer")) { + int attribute = -1; + + if (!params[1] || !params[2] || !params[3]) { + fprintf(stderr, "printer command requires an and attribute name and value!\n"); + fprintf(stderr, "supported attributes:\n"); + fprintf(stderr, "\tcomment:\n"); + fprintf(stderr, "\tport:\n"); + fprintf(stderr, "\tdriver:\n"); + return False; + } + if ( strequal(params[2], "comment") ) + attribute = PRINTER_NOTIFY_COMMENT; + else if ( strequal(params[2], "port") ) + attribute = PRINTER_NOTIFY_PORT_NAME; + else if ( strequal(params[2], "driver") ) + attribute = PRINTER_NOTIFY_DRIVER_NAME; + + if ( attribute == -1 ) { + fprintf(stderr, "bad attribute!\n"); + return False; + } + + //TODL: notify_printer_byname( params[1], attribute, params[3]); + + break; + } + + break; + } + + + case MSG_SMB_FORCE_TDIS: + if (!strequal(dest, "smbd")) { + fprintf(stderr,"close-share can only be sent to smbd\n"); + return(False); + } + if (!params || !params[0]) { + fprintf(stderr, "close-share needs a share name or '*'\n"); + return (False); + } + retval = send_message(dest, MSG_SMB_FORCE_TDIS, params[0], + strlen(params[0]) + 1, False); + break; + + case MSG_SMB_SAM_SYNC: + if (!strequal(dest, "smbd")) { + fprintf(stderr, "samsync can only be sent to smbd\n"); + return False; + } + + if (params) { + fprintf(stderr, "samsync does not take any parameters\n"); + return False; + } + + retval = send_message(dest, MSG_SMB_SAM_SYNC, NULL, 0, False); + + break; + + case MSG_SMB_SAM_REPL: { + uint32 seqnum; + + if (!strequal(dest, "smbd")) { + fprintf(stderr, "sam repl can only be sent to smbd\n"); + return False; + } + + if (!params || !params[0]) { + fprintf(stderr, "SAM_REPL needs a parameter\n"); + return False; + } + + seqnum = atoi(params[0]); + + retval = send_message(dest, MSG_SMB_SAM_SYNC, + (char *)&seqnum, sizeof(uint32), False); + + break; + } + + case MSG_PING: + if (!pong_registered) { + message_register(MSG_PONG, pong_function); + pong_registered = True; + } + if (!params || !params[0]) { + fprintf(stderr,"MSG_PING needs a parameter\n"); + return(False); + } + n = atoi(params[0]); + pong_count = 0; + for (i=0;i<n;i++) { + if (iparams > 1) + retval = send_message(dest, MSG_PING, params[1], strlen(params[1]) + 1, True); + else + retval = send_message(dest, MSG_PING, NULL, 0, True); + if (retval == False) + return False; + } + wait_for_replies(MAX_WAIT, &n); + if (n > 0) { + fprintf(stderr,"PING timeout\n"); + } + break; + + case MSG_REQ_POOL_USAGE: + if (!send_message(dest, MSG_REQ_POOL_USAGE, NULL, 0, True)) + return False; + wait_for_replies(MAX_WAIT, NULL); + + break; + + case MSG_REQ_DMALLOC_LOG_CHANGED: + case MSG_REQ_DMALLOC_MARK: + if (!send_message(dest, mtype, NULL, 0, False)) + return False; + break; + + case MSG_SHUTDOWN: + if (!send_message(dest, MSG_SHUTDOWN, NULL, 0, False)) + return False; + break; + case MSG_PRINTER_DRVUPGRADE: + if (!send_message(dest, MSG_PRINTER_DRVUPGRADE, params[0], 0, False)) + return False; + break; + } + + /* check if we have any pending print notify messages */ + + if ( check_notify_msgs ) + ;//TODO: print_notify_send_messages(0); + + return (True); +} + + int main(int argc, char *argv[]) +{ + int opt; + char temp[255]; + extern int optind; + BOOL interactive = False; + + AllowDebugChange = False; + DEBUGLEVEL = 0; + + setup_logging(argv[0],True); + + if (argc < 2) usage(True); + + while ((opt = getopt(argc, argv,"is:")) != EOF) { + switch (opt) { + case 'i': + interactive = True; + break; + case 's': + pstrcpy(dyn_CONFIGFILE, optarg); + break; + default: + printf("Unknown option %c (%d)\n", (char)opt, opt); + usage(True); + } + } + + lp_load(dyn_CONFIGFILE,False,False,False); + + if (!message_init()) exit(1); + + argc -= optind; + argv = &argv[optind]; + + register_all(); + + if (!interactive) { + if (argc < 2) usage(True); + /* Need to invert sense of return code -- samba + * routines mostly return True==1 for success, but + * shell needs 0. */ + return ! do_command(argv[0],argv[1], argc-2, argc > 2 ? &argv[2] : 0); + } + + while (True) { + char *myargv[4]; + int myargc; + + printf("smbcontrol> "); + if (!fgets(temp, sizeof(temp)-1, stdin)) break; + myargc = 0; + while ((myargc < 4) && + (myargv[myargc] = strtok(myargc?NULL:temp," \t\n"))) { + myargc++; + } + if (!myargc) break; + if (strequal(myargv[0],"q")) break; + if (myargc < 2) + usage(False); + else if (!do_command(myargv[0],myargv[1],myargc-2,myargc > 2 ? &myargv[2] : 0)) + usage(False); + } + return(0); +} + diff --git a/source4/utils/smbfilter.c b/source4/utils/smbfilter.c new file mode 100644 index 0000000000..1a0d639f02 --- /dev/null +++ b/source4/utils/smbfilter.c @@ -0,0 +1,245 @@ +/* + Unix SMB/CIFS implementation. + SMB filter/socket plugin + Copyright (C) Andrew Tridgell 1999 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +#define SECURITY_MASK 0 +#define SECURITY_SET 0 + +/* this forces non-unicode */ +#define CAPABILITY_MASK 0 +#define CAPABILITY_SET 0 + +/* and non-unicode for the client too */ +#define CLI_CAPABILITY_MASK 0 +#define CLI_CAPABILITY_SET 0 + +static char *netbiosname; +static char packet[BUFFER_SIZE]; + +static void save_file(const char *fname, void *packet, size_t length) +{ + int fd; + fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (fd == -1) { + perror(fname); + return; + } + if (write(fd, packet, length) != length) { + fprintf(stderr,"Failed to write %s\n", fname); + return; + } + close(fd); + printf("Wrote %d bytes to %s\n", length, fname); +} + +static void filter_reply(char *buf) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + unsigned x; + + if (msg_type) return; + + switch (type) { + + case SMBnegprot: + /* force the security bits */ + x = CVAL(buf, smb_vwv1); + x = (x | SECURITY_SET) & ~SECURITY_MASK; + SCVAL(buf, smb_vwv1, x); + + /* force the capabilities */ + x = IVAL(buf,smb_vwv9+1); + x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK; + SIVAL(buf, smb_vwv9+1, x); + break; + + } +} + +static void filter_request(char *buf) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + pstring name1,name2; + unsigned x; + + if (msg_type) { + /* it's a netbios special */ + switch (msg_type) { + case 0x81: + /* session request */ + name_extract(buf,4,name1); + name_extract(buf,4 + name_len(buf + 4),name2); + d_printf("sesion_request: %s -> %s\n", + name1, name2); + if (netbiosname) { + /* replace the destination netbios name */ + name_mangle(netbiosname, buf+4, 0x20); + } + } + return; + } + + /* it's an ordinary SMB request */ + switch (type) { + case SMBsesssetupX: + /* force the client capabilities */ + x = IVAL(buf,smb_vwv11); + d_printf("SMBsesssetupX cap=0x%08x\n", x); + d_printf("pwlen=%d/%d\n", SVAL(buf, smb_vwv7), SVAL(buf, smb_vwv8)); + system("mv sessionsetup.dat sessionsetup1.dat"); + save_file("sessionsetup.dat", smb_buf(buf), SVAL(buf, smb_vwv7)); + x = (x | CLI_CAPABILITY_SET) & ~CLI_CAPABILITY_MASK; + SIVAL(buf, smb_vwv11, x); + break; + } + +} + + +static void filter_child(int c, struct in_addr dest_ip) +{ + int s; + + /* we have a connection from a new client, now connect to the server */ + s = open_socket_out(SOCK_STREAM, &dest_ip, 445, LONG_CONNECT_TIMEOUT); + + if (s == -1) { + d_printf("Unable to connect to %s\n", inet_ntoa(dest_ip)); + exit(1); + } + + while (c != -1 || s != -1) { + fd_set fds; + int num; + + FD_ZERO(&fds); + if (s != -1) FD_SET(s, &fds); + if (c != -1) FD_SET(c, &fds); + + num = sys_select_intr(MAX(s+1, c+1),&fds,NULL,NULL,NULL); + if (num <= 0) continue; + + if (c != -1 && FD_ISSET(c, &fds)) { + if (!receive_smb(c, packet, 0)) { + d_printf("client closed connection\n"); + exit(0); + } + filter_request(packet); + if (!send_smb(s, packet)) { + d_printf("server is dead\n"); + exit(1); + } + } + if (s != -1 && FD_ISSET(s, &fds)) { + if (!receive_smb(s, packet, 0)) { + d_printf("server closed connection\n"); + exit(0); + } + filter_reply(packet); + if (!send_smb(c, packet)) { + d_printf("client is dead\n"); + exit(1); + } + } + } + d_printf("Connection closed\n"); + exit(0); +} + + +static void start_filter(char *desthost) +{ + int s, c; + struct in_addr dest_ip; + + CatchChild(); + + /* start listening on port 445 locally */ + s = open_socket_in(SOCK_STREAM, 445, 0, 0, True); + + if (s == -1) { + d_printf("bind failed\n"); + exit(1); + } + + if (listen(s, 5) == -1) { + d_printf("listen failed\n"); + } + + if (!resolve_name(desthost, &dest_ip, 0x20)) { + d_printf("Unable to resolve host %s\n", desthost); + exit(1); + } + + while (1) { + fd_set fds; + int num; + struct sockaddr addr; + socklen_t in_addrlen = sizeof(addr); + + FD_ZERO(&fds); + FD_SET(s, &fds); + + num = sys_select_intr(s+1,&fds,NULL,NULL,NULL); + if (num > 0) { + c = accept(s, &addr, &in_addrlen); + if (c != -1) { + if (fork() == 0) { + close(s); + filter_child(c, dest_ip); + exit(0); + } else { + close(c); + } + } + } + } +} + + +int main(int argc, char *argv[]) +{ + char *desthost; + pstring configfile; + + setup_logging(argv[0],True); + + pstrcpy(configfile,dyn_CONFIGFILE); + + if (argc < 2) { + fprintf(stderr,"smbfilter <desthost> <netbiosname>\n"); + exit(1); + } + + desthost = argv[1]; + if (argc > 2) { + netbiosname = argv[2]; + } + + if (!lp_load(configfile,True,False,False)) { + d_printf("Unable to load config file\n"); + } + + start_filter(desthost); + return 0; +} diff --git a/source4/utils/smbgroupedit.c b/source4/utils/smbgroupedit.c new file mode 100644 index 0000000000..69f69aea5d --- /dev/null +++ b/source4/utils/smbgroupedit.c @@ -0,0 +1,410 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-2000, + * Copyright (C) Jean François Micouleau 1998-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. + */ + +#include "includes.h" + +/* + * Next two lines needed for SunOS and don't + * hurt anything else... + */ +extern char *optarg; +extern int optind; + +/********************************************************* + Print command usage on stderr and die. +**********************************************************/ +static void usage(void) +{ + if (getuid() == 0) { + printf("smbgroupedit options\n"); + } else { + printf("You need to be root to use this tool!\n"); + } + printf("options:\n"); + printf(" -a group create new group\n"); + printf(" -n group NT group name\n"); + printf(" -p privilege only local\n"); + printf(" -d description group description\n"); + printf(" -v list groups\n"); + printf(" -l long list (include details)\n"); + printf(" -s short list (default)\n"); + printf(" -c SID change group\n"); + printf(" -u unix group\n"); + printf(" -d description group description\n"); + printf(" -r rid RID of new group\n"); + printf(" -x group delete this group\n"); + printf("\n"); + printf(" -t[b|d|l] type: builtin, domain, local \n"); + exit(1); +} + +/********************************************************* + Figure out if the input was an NT group or a SID string. + Return the SID. +**********************************************************/ +static BOOL get_sid_from_input(DOM_SID *sid, char *input) +{ + GROUP_MAP map; + + if (StrnCaseCmp( input, "S-", 2)) { + /* Perhaps its the NT group name? */ + if (!pdb_getgrnam(&map, input, MAPPING_WITHOUT_PRIV)) { + printf("NT Group %s doesn't exist in mapping DB\n", input); + return False; + } else { + *sid = map.sid; + } + } else { + if (!string_to_sid(sid, input)) { + printf("converting sid %s from a string failed!\n", input); + return False; + } + } + return True; +} + +/********************************************************* + add a group. +**********************************************************/ +static int addgroup(gid_t gid, enum SID_NAME_USE sid_type, char *ntgroup, char *ntcomment, char *privilege, uint32 rid) +{ + PRIVILEGE_SET se_priv; + DOM_SID sid; + fstring string_sid; + fstring comment; + + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, rid); + + sid_to_string(string_sid, &sid); + + if (ntcomment==NULL) + fstrcpy(comment, "Local Unix group"); + else + fstrcpy(comment, ntcomment); + + init_privilege(&se_priv); + if (privilege!=NULL) + convert_priv_from_text(&se_priv, privilege); + + if(!add_initial_entry(gid, string_sid, sid_type, ntgroup, + comment, se_priv, PR_ACCESS_FROM_NETWORK)) { + printf("adding entry for group %s failed!\n", ntgroup); + free_privilege(&se_priv); + return -1; + } + + free_privilege(&se_priv); + return 0; +} + +/********************************************************* + Change a group. +**********************************************************/ +static int changegroup(char *sid_string, char *group, enum SID_NAME_USE sid_type, char *ntgroup, char *groupdesc, char *privilege) +{ + DOM_SID sid; + GROUP_MAP map; + gid_t gid; + + if (!get_sid_from_input(&sid, sid_string)) { + return -1; + } + + /* Get the current mapping from the database */ + if(!pdb_getgrsid(&map, sid, MAPPING_WITH_PRIV)) { + printf("This SID does not exist in the database\n"); + return -1; + } + + /* If a new Unix group is specified, check and change */ + if (group!=NULL) { + gid=nametogid(group); + if (gid==-1) { + printf("The UNIX group does not exist\n"); + return -1; + } else + map.gid=gid; + } + + /* + * Allow changing of group type only between domain and local + * We disallow changing Builtin groups !!! (SID problem) + */ + if (sid_type==SID_NAME_ALIAS + || sid_type==SID_NAME_DOM_GRP + || sid_type==SID_NAME_UNKNOWN) { + if (map.sid_name_use==SID_NAME_ALIAS + || map.sid_name_use==SID_NAME_DOM_GRP + || map.sid_name_use==SID_NAME_UNKNOWN) { + map.sid_name_use=sid_type; + } else { + printf("cannot change group type to builtin\n"); + }; + } else { + printf("cannot change group type from builtin\n"); + } + + if (ntgroup!=NULL) + fstrcpy(map.nt_name, ntgroup); + + /* Change comment if new one */ + if (groupdesc!=NULL) + fstrcpy(map.comment, groupdesc); + + /* Change the privilege if new one */ + if (privilege!=NULL) + convert_priv_from_text(&map.priv_set, privilege); + + if (!pdb_update_group_mapping_entry(&map)) { + printf("Could not update group database\n"); + free_privilege(&map.priv_set); + return -1; + } + + free_privilege(&map.priv_set); + return 0; +} + +/********************************************************* + Delete the group. +**********************************************************/ +static int deletegroup(char *group) +{ + DOM_SID sid; + + if (!get_sid_from_input(&sid, group)) { + return -1; + } + + if(!pdb_delete_group_mapping_entry(sid)) { + printf("removing group %s from the mapping db failed!\n", group); + return -1; + } + + return 0; +} + +/********************************************************* + List the groups. +**********************************************************/ +static int listgroup(enum SID_NAME_USE sid_type, BOOL long_list) +{ + int entries,i; + TALLOC_CTX *mem_ctx; + GROUP_MAP *map=NULL; + fstring string_sid; + fstring group_type; + fstring priv_text; + + if (!long_list) + printf("NT group (SID) -> Unix group\n"); + + if (!pdb_enum_group_mapping(sid_type, &map, &entries, ENUM_ALL_MAPPED, MAPPING_WITH_PRIV)) + return -1; + + mem_ctx = talloc_init("smbgroupedit talloc"); + if (!mem_ctx) return -1; + for (i=0; i<entries; i++) { + decode_sid_name_use(group_type, (map[i]).sid_name_use); + sid_to_string(string_sid, &map[i].sid); + convert_priv_to_text(&(map[i].priv_set), priv_text); + free_privilege(&(map[i].priv_set)); + + if (!long_list) + printf("%s (%s) -> %s\n", map[i].nt_name, string_sid, + gidtoname(mem_ctx, map[i].gid)); + else { + printf("%s\n", map[i].nt_name); + printf("\tSID : %s\n", string_sid); + printf("\tUnix group: %s\n", gidtoname(mem_ctx, map[i].gid)); + printf("\tGroup type: %s\n", group_type); + printf("\tComment : %s\n", map[i].comment); + printf("\tPrivilege : %s\n\n", priv_text); + } + } + talloc_destroy(mem_ctx); + + return 0; +} + +/********************************************************* + Start here. +**********************************************************/ +int main (int argc, char **argv) +{ + int ch; + BOOL add_group = False; + BOOL view_group = False; + BOOL change_group = False; + BOOL delete_group = False; + BOOL nt_group = False; + BOOL priv = False; + BOOL group_type = False; + BOOL long_list = False; + + char *group = NULL; + char *sid = NULL; + char *ntgroup = NULL; + char *privilege = NULL; + char *groupt = NULL; + char *group_desc = NULL; + + enum SID_NAME_USE sid_type; + uint32 rid = -1; + + setup_logging("groupedit", True); + + if (argc < 2) { + usage(); + return 0; + } + + if (!lp_load(dyn_CONFIGFILE,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", + dyn_CONFIGFILE); + exit(1); + } + + if (!init_names()) + exit(1); + + if(!initialize_password_db(True)) { + fprintf(stderr, "Can't setup password database vectors.\n"); + exit(1); + } + + if(get_global_sam_sid()==False) { + fprintf(stderr, "Can not read machine SID\n"); + return 0; + } + + while ((ch = getopt(argc, argv, "a:c:d:ln:p:r:st:u:vx:")) != EOF) { + switch(ch) { + case 'a': + add_group = True; + group=optarg; + break; + case 'c': + change_group = True; + sid=optarg; + break; + case 'd': + group_desc=optarg; + break; + case 'l': + long_list = True; + break; + case 'n': + nt_group = True; + ntgroup=optarg; + break; + case 'p': + priv = True; + privilege=optarg; + break; + case 'r': + rid = atoi(optarg); + break; + case 's': + long_list = False; + break; + case 't': + group_type = True; + groupt=optarg; + break; + case 'u': + group=optarg; + break; + case 'v': + view_group = True; + break; + case 'x': + delete_group = True; + group=optarg; + break; + /*default: + usage();*/ + } + } + + + if (((add_group?1:0) + (view_group?1:0) + (change_group?1:0) + (delete_group?1:0)) > 1) { + fprintf (stderr, "Incompatible options on command line!\n"); + usage(); + exit(1); + } + + /* no option on command line -> list groups */ + if (((add_group?1:0) + (view_group?1:0) + (change_group?1:0) + (delete_group?1:0)) == 0) + view_group = True; + + + if (group_type==False) + sid_type=SID_NAME_UNKNOWN; + else { + switch (groupt[0]) { + case 'l': + case 'L': + sid_type=SID_NAME_ALIAS; + break; + case 'd': + case 'D': + sid_type=SID_NAME_DOM_GRP; + break; + case 'b': + case 'B': + sid_type=SID_NAME_WKN_GRP; + break; + default: + sid_type=SID_NAME_UNKNOWN; + break; + } + } + + if (add_group) { + gid_t gid=nametogid(group); + if (gid==-1) { + printf("unix group %s doesn't exist!\n", group); + return -1; + } + + if (rid == -1) { + rid = pdb_gid_to_group_rid(gid); + } + return addgroup(gid, sid_type, ntgroup?ntgroup:group, + group_desc, privilege, rid); + } + + if (view_group) + return listgroup(sid_type, long_list); + + if (delete_group) + return deletegroup(group); + + if (change_group) { + return changegroup(sid, group, sid_type, ntgroup, group_desc, privilege); + } + + usage(); + + return 0; +} diff --git a/source4/utils/smbpasswd.c b/source4/utils/smbpasswd.c new file mode 100644 index 0000000000..577e467fbd --- /dev/null +++ b/source4/utils/smbpasswd.c @@ -0,0 +1,605 @@ +/* + * Unix SMB/CIFS implementation. + * Copyright (C) Jeremy Allison 1995-1998 + * Copyright (C) Tim Potter 2001 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 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" + +extern BOOL AllowDebugChange; + +/* + * Next two lines needed for SunOS and don't + * hurt anything else... + */ +extern char *optarg; +extern int optind; + +/* forced running in root-mode */ +static BOOL got_pass = False, got_username = False; +static BOOL stdin_passwd_get = False; +static fstring user_name, user_password; +static char *new_passwd = NULL; +static const char *remote_machine = NULL; + +static fstring ldap_secret; + +/********************************************************* + Print command usage on stderr and die. +**********************************************************/ +static void usage(void) +{ + printf("When run by root:\n"); + printf(" smbpasswd [options] [username] [password]\n"); + printf("otherwise:\n"); + printf(" smbpasswd [options] [password]\n\n"); + + printf("options:\n"); + printf(" -L local mode (must be first option)\n"); + printf(" -h print this usage message\n"); + printf(" -s use stdin for password prompt\n"); + printf(" -c smb.conf file Use the given path to the smb.conf file\n"); + printf(" -D LEVEL debug level\n"); + printf(" -r MACHINE remote machine\n"); + printf(" -U USER remote username\n"); + + printf("extra options when run by root or in local mode:\n"); + printf(" -a add user\n"); + printf(" -d disable user\n"); + printf(" -e enable user\n"); + printf(" -i interdomain trust account\n"); + printf(" -m machine trust account\n"); + printf(" -n set no password\n"); + printf(" -w ldap admin password\n"); + printf(" -x delete user\n"); + printf(" -R ORDER name resolve order\n"); + + exit(1); +} + +static void set_line_buffering(FILE *f) +{ + setvbuf(f, NULL, _IOLBF, 0); +} + +/******************************************************************* + Process command line options + ******************************************************************/ +static int process_options(int argc, char **argv, int local_flags) +{ + int ch; + pstring configfile; + pstrcpy(configfile, dyn_CONFIGFILE); + + local_flags |= LOCAL_SET_PASSWORD; + + ZERO_STRUCT(user_name); + ZERO_STRUCT(user_password); + + user_name[0] = '\0'; + + while ((ch = getopt(argc, argv, "c:axdehminjr:sw:R:D:U:L")) != EOF) { + switch(ch) { + case 'L': + local_flags |= LOCAL_AM_ROOT; + break; + case 'c': + pstrcpy(configfile,optarg); + break; + case 'a': + local_flags |= LOCAL_ADD_USER; + break; + case 'x': + local_flags |= LOCAL_DELETE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'd': + local_flags |= LOCAL_DISABLE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'e': + local_flags |= LOCAL_ENABLE_USER; + local_flags &= ~LOCAL_SET_PASSWORD; + break; + case 'm': + local_flags |= LOCAL_TRUST_ACCOUNT; + break; + case 'i': + local_flags |= LOCAL_INTERDOM_ACCOUNT; + break; + case 'j': + d_printf("See 'net join' for this functionality\n"); + exit(1); + break; + case 'n': + local_flags |= LOCAL_SET_NO_PASSWORD; + local_flags &= ~LOCAL_SET_PASSWORD; + new_passwd = smb_xstrdup("NO PASSWORD"); + break; + case 'r': + remote_machine = optarg; + break; + case 's': + set_line_buffering(stdin); + set_line_buffering(stdout); + set_line_buffering(stderr); + stdin_passwd_get = True; + break; + case 'w': + local_flags |= LOCAL_SET_LDAP_ADMIN_PW; + fstrcpy(ldap_secret, optarg); + break; + case 'R': + lp_set_name_resolve_order(optarg); + break; + case 'D': + DEBUGLEVEL = atoi(optarg); + break; + case 'U': { + char *lp; + + got_username = True; + fstrcpy(user_name, optarg); + + if ((lp = strchr(user_name, '%'))) { + *lp = 0; + fstrcpy(user_password, lp + 1); + got_pass = True; + memset(strchr_m(optarg, '%') + 1, 'X', + strlen(user_password)); + } + + break; + } + case 'h': + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (!got_username) + fstrcpy(user_name, ""); + break; + case 1: + if (!(local_flags & LOCAL_AM_ROOT)) { + new_passwd = argv[0]; + } else { + if (got_username) { + usage(); + } else { + fstrcpy(user_name, argv[0]); + } + } + break; + case 2: + if (!(local_flags & LOCAL_AM_ROOT) || got_username || got_pass) { + usage(); + } + + fstrcpy(user_name, argv[0]); + new_passwd = smb_xstrdup(argv[1]); + break; + default: + usage(); + } + + if (!lp_load(configfile,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", + dyn_CONFIGFILE); + exit(1); + } + + return local_flags; +} + +/************************************************************* + Utility function to prompt for passwords from stdin. Each + password entered must end with a newline. +*************************************************************/ +static char *stdin_new_passwd(void) +{ + static fstring new_pw; + size_t len; + + ZERO_ARRAY(new_pw); + + /* + * if no error is reported from fgets() and string at least contains + * the newline that ends the password, then replace the newline with + * a null terminator. + */ + if ( fgets(new_pw, sizeof(new_pw), stdin) != NULL) { + if ((len = strlen(new_pw)) > 0) { + if(new_pw[len-1] == '\n') + new_pw[len - 1] = 0; + } + } + return(new_pw); +} + + +/************************************************************* + Utility function to get passwords via tty or stdin + Used if the '-s' option is set to silently get passwords + to enable scripting. +*************************************************************/ +static char *get_pass( const char *prompt, BOOL stdin_get) +{ + char *p; + if (stdin_get) { + p = stdin_new_passwd(); + } else { + p = getpass(prompt); + } + return smb_xstrdup(p); +} + +/************************************************************* + Utility function to prompt for new password. +*************************************************************/ +static char *prompt_for_new_password(BOOL stdin_get) +{ + char *p; + fstring new_pw; + + ZERO_ARRAY(new_pw); + + p = get_pass("New SMB password:", stdin_get); + + fstrcpy(new_pw, p); + SAFE_FREE(p); + + p = get_pass("Retype new SMB password:", stdin_get); + + if (strcmp(p, new_pw)) { + fprintf(stderr, "Mismatch - password unchanged.\n"); + ZERO_ARRAY(new_pw); + SAFE_FREE(p); + return NULL; + } + + return p; +} + + +/************************************************************* + Change a password either locally or remotely. +*************************************************************/ + +static BOOL password_change(const char *remote_mach, char *username, + char *old_passwd, char *new_pw, int local_flags) +{ + BOOL ret; + pstring err_str; + pstring msg_str; + + if (remote_mach != NULL) { + if (local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER| + LOCAL_TRUST_ACCOUNT|LOCAL_SET_NO_PASSWORD)) { + /* these things can't be done remotely yet */ + return False; + } + ret = remote_password_change(remote_mach, username, + old_passwd, new_pw, err_str, sizeof(err_str)); + if(*err_str) + fprintf(stderr, err_str); + return ret; + } + + ret = local_password_change(username, local_flags, new_pw, + err_str, sizeof(err_str), msg_str, sizeof(msg_str)); + + if(*msg_str) + printf(msg_str); + if(*err_str) + fprintf(stderr, err_str); + + return ret; +} + +/******************************************************************* + Store the LDAP admin password in secrets.tdb + ******************************************************************/ +static BOOL store_ldap_admin_pw (char* pw) +{ + if (!pw) + return False; + + if (!secrets_init()) + return False; + + return secrets_store_ldap_pw(lp_ldap_admin_dn(), pw); +} + + +/************************************************************* + Handle password changing for root. +*************************************************************/ + +static int process_root(int local_flags) +{ + struct passwd *pwd; + int result = 0; + char *old_passwd = NULL; + + if (local_flags & LOCAL_SET_LDAP_ADMIN_PW) + { + printf("Setting stored password for \"%s\" in secrets.tdb\n", + lp_ldap_admin_dn()); + if (!store_ldap_admin_pw(ldap_secret)) + DEBUG(0,("ERROR: Failed to store the ldap admin password!\n")); + goto done; + } + + /* + * Ensure both add/delete user are not set + * Ensure add/delete user and either remote machine or join domain are + * not both set. + */ + if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) || + ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) && + (remote_machine != NULL))) { + usage(); + } + + /* Only load interfaces if we are doing network operations. */ + + if (remote_machine) { + load_interfaces(); + } + + if (!user_name[0] && (pwd = getpwuid_alloc(geteuid()))) { + fstrcpy(user_name, pwd->pw_name); + passwd_free(&pwd); + } + + if (!user_name[0]) { + fprintf(stderr,"You must specify a username\n"); + exit(1); + } + + if (local_flags & LOCAL_TRUST_ACCOUNT) { + /* add the $ automatically */ + static fstring buf; + + /* + * Remove any trailing '$' before we + * generate the initial machine password. + */ + + if (user_name[strlen(user_name)-1] == '$') { + user_name[strlen(user_name)-1] = 0; + } + + if (local_flags & LOCAL_ADD_USER) { + SAFE_FREE(new_passwd); + new_passwd = smb_xstrdup(user_name); + strlower(new_passwd); + } + + /* + * Now ensure the username ends in '$' for + * the machine add. + */ + + slprintf(buf, sizeof(buf)-1, "%s$", user_name); + fstrcpy(user_name, buf); + } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) { + static fstring buf; + + if (local_flags & LOCAL_ADD_USER) { + /* + * Prompt for trusting domain's account password + */ + new_passwd = prompt_for_new_password(stdin_passwd_get); + if(!new_passwd) { + fprintf(stderr, "Unable to get newpassword.\n"); + exit(1); + } + } + + /* prepare uppercased and '$' terminated username */ + slprintf(buf, sizeof(buf) - 1, "%s$", user_name); + fstrcpy(user_name, buf); + + } else { + + if (remote_machine != NULL) { + old_passwd = get_pass("Old SMB password:",stdin_passwd_get); + } + + if (!(local_flags & LOCAL_SET_PASSWORD)) { + + /* + * If we are trying to enable a user, first we need to find out + * if they are using a modern version of the smbpasswd file that + * disables a user by just writing a flag into the file. If so + * then we can re-enable a user without prompting for a new + * password. If not (ie. they have a no stored password in the + * smbpasswd file) then we need to prompt for a new password. + */ + + if(local_flags & LOCAL_ENABLE_USER) { + SAM_ACCOUNT *sampass = NULL; + BOOL ret; + + pdb_init_sam(&sampass); + ret = pdb_getsampwnam(sampass, user_name); + if((sampass != False) && (pdb_get_lanman_passwd(sampass) == NULL)) { + local_flags |= LOCAL_SET_PASSWORD; + } + pdb_free_sam(&sampass); + } + } + + if(local_flags & LOCAL_SET_PASSWORD) { + new_passwd = prompt_for_new_password(stdin_passwd_get); + + if(!new_passwd) { + fprintf(stderr, "Unable to get new password.\n"); + exit(1); + } + } + } + + if (!password_change(remote_machine, user_name, old_passwd, new_passwd, local_flags)) { + fprintf(stderr,"Failed to modify password entry for user %s\n", user_name); + result = 1; + goto done; + } + + if(remote_machine) { + printf("Password changed for user %s on %s.\n", user_name, remote_machine ); + } else if(!(local_flags & (LOCAL_ADD_USER|LOCAL_DISABLE_USER|LOCAL_ENABLE_USER|LOCAL_DELETE_USER|LOCAL_SET_NO_PASSWORD|LOCAL_SET_PASSWORD))) { + SAM_ACCOUNT *sampass = NULL; + BOOL ret; + + pdb_init_sam(&sampass); + ret = pdb_getsampwnam(sampass, user_name); + + printf("Password changed for user %s.", user_name ); + if( (ret != False) && (pdb_get_acct_ctrl(sampass)&ACB_DISABLED) ) + printf(" User has disabled flag set."); + if((ret != False) && (pdb_get_acct_ctrl(sampass) & ACB_PWNOTREQ) ) + printf(" User has no password flag set."); + printf("\n"); + pdb_free_sam(&sampass); + } + + done: + SAFE_FREE(new_passwd); + return result; +} + + +/************************************************************* + Handle password changing for non-root. +*************************************************************/ + +static int process_nonroot(int local_flags) +{ + struct passwd *pwd = NULL; + int result = 0; + char *old_pw = NULL; + char *new_pw = NULL; + + if (local_flags & ~(LOCAL_AM_ROOT | LOCAL_SET_PASSWORD)) { + /* Extra flags that we can't honor non-root */ + usage(); + } + + if (!user_name[0]) { + pwd = getpwuid_alloc(getuid()); + if (pwd) { + fstrcpy(user_name,pwd->pw_name); + passwd_free(&pwd); + } else { + fprintf(stderr, "smbpasswd: you don't exist - go away\n"); + exit(1); + } + } + + /* + * A non-root user is always setting a password + * via a remote machine (even if that machine is + * localhost). + */ + + load_interfaces(); /* Delayed from main() */ + + if (remote_machine == NULL) { + remote_machine = "127.0.0.1"; + } + + if (remote_machine != NULL) { + old_pw = get_pass("Old SMB password:",stdin_passwd_get); + } + + if (!new_passwd) { + new_pw = prompt_for_new_password(stdin_passwd_get); + } + else + new_pw = smb_xstrdup(new_passwd); + + if (!new_pw) { + fprintf(stderr, "Unable to get new password.\n"); + exit(1); + } + + if (!password_change(remote_machine, user_name, old_pw, new_pw, 0)) { + fprintf(stderr,"Failed to change password for %s\n", user_name); + result = 1; + goto done; + } + + printf("Password changed for user %s\n", user_name); + + done: + SAFE_FREE(old_pw); + SAFE_FREE(new_pw); + + return result; +} + + + +/********************************************************* + Start here. +**********************************************************/ +int main(int argc, char **argv) +{ + int local_flags = 0; + + AllowDebugChange = False; + +#if defined(HAVE_SET_AUTH_PARAMETERS) + set_auth_parameters(argc, argv); +#endif /* HAVE_SET_AUTH_PARAMETERS */ + + if (getuid() == 0) { + local_flags = LOCAL_AM_ROOT; + } + + local_flags = process_options(argc, argv, local_flags); + + setup_logging("smbpasswd", True); + + /* + * Set the machine NETBIOS name if not already + * set from the config file. + */ + + if (!init_names()) + return 1; + + /* Check the effective uid - make sure we are not setuid */ + if (is_setuid_root()) { + fprintf(stderr, "smbpasswd must *NOT* be setuid root.\n"); + exit(1); + } + + if (local_flags & LOCAL_AM_ROOT) { + secrets_init(); + return process_root(local_flags); + } + + return process_nonroot(local_flags); +} diff --git a/source4/utils/smbtree.c b/source4/utils/smbtree.c new file mode 100644 index 0000000000..ff6120ee90 --- /dev/null +++ b/source4/utils/smbtree.c @@ -0,0 +1,369 @@ +/* + Unix SMB/CIFS implementation. + Network neighbourhood browser. + + Copyright (C) Tim Potter 2000 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +static BOOL use_bcast; + +struct user_auth_info { + pstring username; + pstring password; + pstring workgroup; +}; + +/* How low can we go? */ + +enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE}; +static enum tree_level level = LEV_SHARE; + +static void usage(void) +{ + printf( +"Usage: smbtree [options]\n\ +\n\ +\t-d debuglevel set debug output level\n\ +\t-U username user to autheticate as\n\ +\t-W workgroup workgroup of user to authenticate as\n\ +\t-D list only domains (workgroups) of tree\n\ +\t-S list domains and servers of tree\n\ +\t-b use bcast instead of using the master browser\n\ +\n\ +The username can be of the form username%%password or\n\ +workgroup\\username%%password.\n\n\ +"); +} + +/* Holds a list of workgroups or servers */ + +struct name_list { + struct name_list *prev, *next; + pstring name, comment; + uint32 server_type; +}; + +static struct name_list *workgroups, *servers, *shares; + +static void free_name_list(struct name_list *list) +{ + while(list) + DLIST_REMOVE(list, list); +} + +static void add_name(const char *machine_name, uint32 server_type, + const char *comment, void *state) +{ + struct name_list **name_list = (struct name_list **)state; + struct name_list *new_name; + + new_name = (struct name_list *)malloc(sizeof(struct name_list)); + + if (!new_name) + return; + + ZERO_STRUCTP(new_name); + + pstrcpy(new_name->name, machine_name); + pstrcpy(new_name->comment, comment); + new_name->server_type = server_type; + + DLIST_ADD(*name_list, new_name); +} + +/* Return a cli_state pointing at the IPC$ share for the given server */ + +static struct cli_state *get_ipc_connect(char *server, struct in_addr *server_ip, + struct user_auth_info *user_info) +{ + struct cli_state *cli; + char *myname; + NTSTATUS nt_status; + + myname = get_myname(); + + nt_status = cli_full_connection(&cli, myname, server, server_ip, 0, "IPC$", "IPC", + user_info->username, lp_workgroup(), user_info->password, + CLI_FULL_CONNECTION_ANNONYMOUS_FALLBACK, NULL); + + free(myname); + if (NT_STATUS_IS_OK(nt_status)) { + return cli; + } else { + return NULL; + } +} + +/* Return the IP address and workgroup of a master browser on the + network. */ + +static BOOL find_master_ip_bcast(pstring workgroup, struct in_addr *server_ip) +{ + struct in_addr *ip_list; + int i, count; + + /* Go looking for workgroups by broadcasting on the local network */ + + if (!name_resolve_bcast(MSBROWSE, 1, &ip_list, &count)) { + return False; + } + + for (i = 0; i < count; i++) { + static fstring name; + + if (!name_status_find("*", 0, 0x1d, ip_list[i], name)) + continue; + + if (!find_master_ip(name, server_ip)) + continue; + + pstrcpy(workgroup, name); + + DEBUG(4, ("found master browser %s, %s\n", + name, inet_ntoa(ip_list[i]))); + + return True; + } + + return False; +} + +/**************************************************************************** + display tree of smb workgroups, servers and shares +****************************************************************************/ +static BOOL get_workgroups(struct user_auth_info *user_info) +{ + struct cli_state *cli; + struct in_addr server_ip; + pstring master_workgroup; + + /* Try to connect to a #1d name of our current workgroup. If that + doesn't work broadcast for a master browser and then jump off + that workgroup. */ + + pstrcpy(master_workgroup, lp_workgroup()); + + if (use_bcast || !find_master_ip(lp_workgroup(), &server_ip)) { + DEBUG(4, ("Unable to find master browser for workgroup %s\n", + master_workgroup)); + if (!find_master_ip_bcast(master_workgroup, &server_ip)) { + DEBUG(4, ("Unable to find master browser by " + "broadcast\n")); + return False; + } + } + + if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info))) + return False; + + if (!cli_NetServerEnum(cli, master_workgroup, + SV_TYPE_DOMAIN_ENUM, add_name, &workgroups)) + return False; + + return True; +} + +/* Retrieve the list of servers for a given workgroup */ + +static BOOL get_servers(char *workgroup, struct user_auth_info *user_info) +{ + struct cli_state *cli; + struct in_addr server_ip; + + /* Open an IPC$ connection to the master browser for the workgroup */ + + if (!find_master_ip(workgroup, &server_ip)) { + DEBUG(4, ("Cannot find master browser for workgroup %s\n", + workgroup)); + return False; + } + + if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info))) + return False; + + if (!cli_NetServerEnum(cli, workgroup, SV_TYPE_ALL, add_name, + &servers)) + return False; + + return True; +} + +static BOOL get_shares(char *server_name, struct user_auth_info *user_info) +{ + struct cli_state *cli; + + if (!(cli = get_ipc_connect(server_name, NULL, user_info))) + return False; + + if (!cli_RNetShareEnum(cli, add_name, &shares)) + return False; + + return True; +} + +static BOOL print_tree(struct user_auth_info *user_info) +{ + struct name_list *wg, *sv, *sh; + + /* List workgroups */ + + if (!get_workgroups(user_info)) + return False; + + for (wg = workgroups; wg; wg = wg->next) { + + printf("%s\n", wg->name); + + /* List servers */ + + free_name_list(servers); + servers = NULL; + + if (level == LEV_WORKGROUP || + !get_servers(wg->name, user_info)) + continue; + + for (sv = servers; sv; sv = sv->next) { + + printf("\t\\\\%-15s\t\t%s\n", + sv->name, sv->comment); + + /* List shares */ + + free_name_list(shares); + shares = NULL; + + if (level == LEV_SERVER || + !get_shares(sv->name, user_info)) + continue; + + for (sh = shares; sh; sh = sh->next) { + printf("\t\t\\\\%s\\%-15s\t%s\n", + sv->name, sh->name, sh->comment); + } + } + } + + return True; +} + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc,char *argv[]) +{ + extern char *optarg; + extern int optind; + int opt; + char *p; + struct user_auth_info user_info; + BOOL got_pass = False; + + /* Initialise samba stuff */ + + setlinebuf(stdout); + + dbf = x_stderr; + + setup_logging(argv[0],True); + + lp_load(dyn_CONFIGFILE,True,False,False); + load_interfaces(); + + if (getenv("USER")) { + pstrcpy(user_info.username, getenv("USER")); + + if ((p=strchr(user_info.username, '%'))) { + *p = 0; + pstrcpy(user_info.password, p+1); + got_pass = True; + memset(strchr(getenv("USER"), '%') + 1, 'X', + strlen(user_info.password)); + } + } + + pstrcpy(user_info.workgroup, lp_workgroup()); + + /* Parse command line args */ + + while ((opt = getopt(argc, argv, "U:hd:W:DSb")) != EOF) { + switch (opt) { + case 'U': + pstrcpy(user_info.username,optarg); + p = strchr(user_info.username,'%'); + if (p) { + *p = 0; + pstrcpy(user_info.password, p+1); + got_pass = 1; + } + break; + + case 'b': + use_bcast = True; + break; + + case 'h': + usage(); + exit(1); + + case 'd': + DEBUGLEVEL = atoi(optarg); + break; + + case 'W': + pstrcpy(user_info.workgroup, optarg); + break; + + case 'D': + level = LEV_WORKGROUP; + break; + + case 'S': + level = LEV_SERVER; + break; + + default: + printf("Unknown option %c (%d)\n", (char)opt, opt); + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (argc > 0) { + usage(); + exit(1); + } + + if (!got_pass) { + char *pass = getpass("Password: "); + if (pass) { + pstrcpy(user_info.password, pass); + } + got_pass = True; + } + + /* Now do our stuff */ + + if (!print_tree(&user_info)) + return 1; + + return 0; +} diff --git a/source4/utils/smbw_sample.c b/source4/utils/smbw_sample.c new file mode 100644 index 0000000000..5cd792df7a --- /dev/null +++ b/source4/utils/smbw_sample.c @@ -0,0 +1,94 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <dirent.h> +#include <sys/stat.h> + +static void usage(void) +{ + printf(" +smbw_sample - a sample program that uses smbw + +smbw_sample <options> path + + options: + -W workgroup + -l logfile + -P prefix + -d debuglevel + -U username%%password + -R resolve order + +note that path must start with /smb/ +"); +} + +int main(int argc, char *argv[]) +{ + DIR *dir; + struct dirent *dent; + int opt; + char *p; + extern char *optarg; + extern int optind; + char *path; + + lp_load(dyn_CONFIGFILE,1,0,0); + smbw_setup_shared(); + + while ((opt = getopt(argc, argv, "W:U:R:d:P:l:hL:")) != EOF) { + switch (opt) { + case 'W': + smbw_setshared("WORKGROUP", optarg); + break; + case 'l': + smbw_setshared("LOGFILE", optarg); + break; + case 'P': + smbw_setshared("PREFIX", optarg); + break; + case 'd': + smbw_setshared("DEBUG", optarg); + break; + case 'U': + p = strchr_m(optarg,'%'); + if (p) { + *p=0; + smbw_setshared("PASSWORD",p+1); + } + smbw_setshared("USER", optarg); + break; + case 'R': + smbw_setshared("RESOLVE_ORDER",optarg); + break; + case 'h': + default: + usage(); + exit(1); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + exit(1); + } + + path = argv[0]; + + smbw_init(); + + dir = smbw_opendir(path); + if (!dir) { + printf("failed to open %s\n", path); + exit(1); + } + + while ((dent = smbw_readdir(dir))) { + printf("%s\n", dent->d_name); + } + smbw_closedir(dir); + return 0; +} diff --git a/source4/utils/status.c b/source4/utils/status.c new file mode 100644 index 0000000000..76b9253646 --- /dev/null +++ b/source4/utils/status.c @@ -0,0 +1,665 @@ +/* + Unix SMB/CIFS implementation. + status reporting + Copyright (C) Andrew Tridgell 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. + + Revision History: + + 12 aug 96: Erik.Devriendt@te6.siemens.be + added support for shared memory implementation of share mode locking + + 21-Jul-1998: rsharpe@ns.aus.com (Richard Sharpe) + Added -L (locks only) -S (shares only) flags and code + +*/ + +/* + * This program reports current SMB connections + */ + +#include "includes.h" + +static pstring Ucrit_username = ""; /* added by OH */ +static pid_t Ucrit_pid[100]; /* Ugly !!! */ /* added by OH */ +static int Ucrit_MaxPid=0; /* added by OH */ +static unsigned int Ucrit_IsActive = 0; /* added by OH */ +static int verbose, brief; +static int shares_only = 0; /* Added by RJS */ +static int locks_only = 0; /* Added by RJS */ +static BOOL processes_only=False; +static int show_brl; + +/* we need these because we link to locking*.o */ + void become_root(void) {} + void unbecome_root(void) {} + + +/* added by OH */ +static void Ucrit_addUsername(const char *username) +{ + pstrcpy(Ucrit_username, username); + if(strlen(Ucrit_username) > 0) + Ucrit_IsActive = 1; +} + +static unsigned int Ucrit_checkUsername(const char *username) +{ + if ( !Ucrit_IsActive) return 1; + if (strcmp(Ucrit_username,username) ==0) return 1; + return 0; +} + +static unsigned int Ucrit_checkPid(pid_t pid) +{ + int i; + if ( !Ucrit_IsActive) return 1; + for (i=0;i<Ucrit_MaxPid;i++) + if( pid == Ucrit_pid[i] ) return 1; + return 0; +} + + +static void print_share_mode(share_mode_entry *e, char *fname) +{ + static int count; + if (count==0) { + d_printf("Locked files:\n"); + d_printf("Pid DenyMode Access R/W Oplock Name\n"); + d_printf("--------------------------------------------------------------\n"); + } + count++; + + if (Ucrit_checkPid(e->pid)) { + d_printf("%-5d ",(int)e->pid); + switch (GET_DENY_MODE(e->share_mode)) { + case DENY_NONE: d_printf("DENY_NONE "); break; + case DENY_ALL: d_printf("DENY_ALL "); break; + case DENY_DOS: d_printf("DENY_DOS "); break; + case DENY_READ: d_printf("DENY_READ "); break; + case DENY_WRITE:printf("DENY_WRITE "); break; + case DENY_FCB: d_printf("DENY_FCB "); break; + } + d_printf("0x%-8x ",(unsigned int)e->desired_access); + switch (e->share_mode&0xF) { + case 0: d_printf("RDONLY "); break; + case 1: d_printf("WRONLY "); break; + case 2: d_printf("RDWR "); break; + } + + if((e->op_type & + (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) == + (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) + d_printf("EXCLUSIVE+BATCH "); + else if (e->op_type & EXCLUSIVE_OPLOCK) + d_printf("EXCLUSIVE "); + else if (e->op_type & BATCH_OPLOCK) + d_printf("BATCH "); + else if (e->op_type & LEVEL_II_OPLOCK) + d_printf("LEVEL_II "); + else + d_printf("NONE "); + + d_printf(" %s %s",fname, + asctime(LocalTime((time_t *)&e->time.tv_sec))); + } +} + +static void print_brl(SMB_DEV_T dev, SMB_INO_T ino, int pid, + enum brl_type lock_type, + br_off start, br_off size) +{ + static int count; + if (count==0) { + d_printf("Byte range locks:\n"); + d_printf(" Pid dev:inode R/W start size\n"); + d_printf("------------------------------------------------\n"); + } + count++; + + d_printf("%6d %05x:%05x %s %9.0f %9.0f\n", + (int)pid, (int)dev, (int)ino, + lock_type==READ_LOCK?"R":"W", + (double)start, (double)size); +} + + +/******************************************************************* + dump the elements of the profile structure + ******************************************************************/ +static int profile_dump(void) +{ +#ifdef WITH_PROFILE + if (!profile_setup(True)) { + fprintf(stderr,"Failed to initialise profile memory\n"); + return -1; + } + + d_printf("smb_count: %u\n", profile_p->smb_count); + d_printf("uid_changes: %u\n", profile_p->uid_changes); + d_printf("************************ System Calls ****************************\n"); + d_printf("opendir_count: %u\n", profile_p->syscall_opendir_count); + d_printf("opendir_time: %u\n", profile_p->syscall_opendir_time); + d_printf("readdir_count: %u\n", profile_p->syscall_readdir_count); + d_printf("readdir_time: %u\n", profile_p->syscall_readdir_time); + d_printf("mkdir_count: %u\n", profile_p->syscall_mkdir_count); + d_printf("mkdir_time: %u\n", profile_p->syscall_mkdir_time); + d_printf("rmdir_count: %u\n", profile_p->syscall_rmdir_count); + d_printf("rmdir_time: %u\n", profile_p->syscall_rmdir_time); + d_printf("closedir_count: %u\n", profile_p->syscall_closedir_count); + d_printf("closedir_time: %u\n", profile_p->syscall_closedir_time); + d_printf("open_count: %u\n", profile_p->syscall_open_count); + d_printf("open_time: %u\n", profile_p->syscall_open_time); + d_printf("close_count: %u\n", profile_p->syscall_close_count); + d_printf("close_time: %u\n", profile_p->syscall_close_time); + d_printf("read_count: %u\n", profile_p->syscall_read_count); + d_printf("read_time: %u\n", profile_p->syscall_read_time); + d_printf("read_bytes: %u\n", profile_p->syscall_read_bytes); + d_printf("write_count: %u\n", profile_p->syscall_write_count); + d_printf("write_time: %u\n", profile_p->syscall_write_time); + d_printf("write_bytes: %u\n", profile_p->syscall_write_bytes); +#ifdef WITH_SENDFILE + d_printf("sendfile_count: %u\n", profile_p->syscall_sendfile_count); + d_printf("sendfile_time: %u\n", profile_p->syscall_sendfile_time); + d_printf("sendfile_bytes: %u\n", profile_p->syscall_sendfile_bytes); +#endif + d_printf("lseek_count: %u\n", profile_p->syscall_lseek_count); + d_printf("lseek_time: %u\n", profile_p->syscall_lseek_time); + d_printf("rename_count: %u\n", profile_p->syscall_rename_count); + d_printf("rename_time: %u\n", profile_p->syscall_rename_time); + d_printf("fsync_count: %u\n", profile_p->syscall_fsync_count); + d_printf("fsync_time: %u\n", profile_p->syscall_fsync_time); + d_printf("stat_count: %u\n", profile_p->syscall_stat_count); + d_printf("stat_time: %u\n", profile_p->syscall_stat_time); + d_printf("fstat_count: %u\n", profile_p->syscall_fstat_count); + d_printf("fstat_time: %u\n", profile_p->syscall_fstat_time); + d_printf("lstat_count: %u\n", profile_p->syscall_lstat_count); + d_printf("lstat_time: %u\n", profile_p->syscall_lstat_time); + d_printf("unlink_count: %u\n", profile_p->syscall_unlink_count); + d_printf("unlink_time: %u\n", profile_p->syscall_unlink_time); + d_printf("chmod_count: %u\n", profile_p->syscall_chmod_count); + d_printf("chmod_time: %u\n", profile_p->syscall_chmod_time); + d_printf("fchmod_count: %u\n", profile_p->syscall_fchmod_count); + d_printf("fchmod_time: %u\n", profile_p->syscall_fchmod_time); + d_printf("chown_count: %u\n", profile_p->syscall_chown_count); + d_printf("chown_time: %u\n", profile_p->syscall_chown_time); + d_printf("fchown_count: %u\n", profile_p->syscall_fchown_count); + d_printf("fchown_time: %u\n", profile_p->syscall_fchown_time); + d_printf("chdir_count: %u\n", profile_p->syscall_chdir_count); + d_printf("chdir_time: %u\n", profile_p->syscall_chdir_time); + d_printf("getwd_count: %u\n", profile_p->syscall_getwd_count); + d_printf("getwd_time: %u\n", profile_p->syscall_getwd_time); + d_printf("utime_count: %u\n", profile_p->syscall_utime_count); + d_printf("utime_time: %u\n", profile_p->syscall_utime_time); + d_printf("ftruncate_count: %u\n", profile_p->syscall_ftruncate_count); + d_printf("ftruncate_time: %u\n", profile_p->syscall_ftruncate_time); + d_printf("fcntl_lock_count: %u\n", profile_p->syscall_fcntl_lock_count); + d_printf("fcntl_lock_time: %u\n", profile_p->syscall_fcntl_lock_time); + d_printf("readlink_count: %u\n", profile_p->syscall_readlink_count); + d_printf("readlink_time: %u\n", profile_p->syscall_readlink_time); + d_printf("symlink_count: %u\n", profile_p->syscall_symlink_count); + d_printf("symlink_time: %u\n", profile_p->syscall_symlink_time); + d_printf("************************ Statcache *******************************\n"); + d_printf("lookups: %u\n", profile_p->statcache_lookups); + d_printf("misses: %u\n", profile_p->statcache_misses); + d_printf("hits: %u\n", profile_p->statcache_hits); + d_printf("************************ Writecache ******************************\n"); + d_printf("read_hits: %u\n", profile_p->writecache_read_hits); + d_printf("abutted_writes: %u\n", profile_p->writecache_abutted_writes); + d_printf("total_writes: %u\n", profile_p->writecache_total_writes); + d_printf("non_oplock_writes: %u\n", profile_p->writecache_non_oplock_writes); + d_printf("direct_writes: %u\n", profile_p->writecache_direct_writes); + d_printf("init_writes: %u\n", profile_p->writecache_init_writes); + d_printf("flushed_writes[SEEK]: %u\n", profile_p->writecache_flushed_writes[SEEK_FLUSH]); + d_printf("flushed_writes[READ]: %u\n", profile_p->writecache_flushed_writes[READ_FLUSH]); + d_printf("flushed_writes[WRITE]: %u\n", profile_p->writecache_flushed_writes[WRITE_FLUSH]); + d_printf("flushed_writes[READRAW]: %u\n", profile_p->writecache_flushed_writes[READRAW_FLUSH]); + d_printf("flushed_writes[OPLOCK_RELEASE]: %u\n", profile_p->writecache_flushed_writes[OPLOCK_RELEASE_FLUSH]); + d_printf("flushed_writes[CLOSE]: %u\n", profile_p->writecache_flushed_writes[CLOSE_FLUSH]); + d_printf("flushed_writes[SYNC]: %u\n", profile_p->writecache_flushed_writes[SYNC_FLUSH]); + d_printf("flushed_writes[SIZECHANGE]: %u\n", profile_p->writecache_flushed_writes[SIZECHANGE_FLUSH]); + d_printf("num_perfect_writes: %u\n", profile_p->writecache_num_perfect_writes); + d_printf("num_write_caches: %u\n", profile_p->writecache_num_write_caches); + d_printf("allocated_write_caches: %u\n", profile_p->writecache_allocated_write_caches); + d_printf("************************ SMB Calls *******************************\n"); + d_printf("mkdir_count: %u\n", profile_p->SMBmkdir_count); + d_printf("mkdir_time: %u\n", profile_p->SMBmkdir_time); + d_printf("rmdir_count: %u\n", profile_p->SMBrmdir_count); + d_printf("rmdir_time: %u\n", profile_p->SMBrmdir_time); + d_printf("open_count: %u\n", profile_p->SMBopen_count); + d_printf("open_time: %u\n", profile_p->SMBopen_time); + d_printf("create_count: %u\n", profile_p->SMBcreate_count); + d_printf("create_time: %u\n", profile_p->SMBcreate_time); + d_printf("close_count: %u\n", profile_p->SMBclose_count); + d_printf("close_time: %u\n", profile_p->SMBclose_time); + d_printf("flush_count: %u\n", profile_p->SMBflush_count); + d_printf("flush_time: %u\n", profile_p->SMBflush_time); + d_printf("unlink_count: %u\n", profile_p->SMBunlink_count); + d_printf("unlink_time: %u\n", profile_p->SMBunlink_time); + d_printf("mv_count: %u\n", profile_p->SMBmv_count); + d_printf("mv_time: %u\n", profile_p->SMBmv_time); + d_printf("getatr_count: %u\n", profile_p->SMBgetatr_count); + d_printf("getatr_time: %u\n", profile_p->SMBgetatr_time); + d_printf("setatr_count: %u\n", profile_p->SMBsetatr_count); + d_printf("setatr_time: %u\n", profile_p->SMBsetatr_time); + d_printf("read_count: %u\n", profile_p->SMBread_count); + d_printf("read_time: %u\n", profile_p->SMBread_time); + d_printf("write_count: %u\n", profile_p->SMBwrite_count); + d_printf("write_time: %u\n", profile_p->SMBwrite_time); + d_printf("lock_count: %u\n", profile_p->SMBlock_count); + d_printf("lock_time: %u\n", profile_p->SMBlock_time); + d_printf("unlock_count: %u\n", profile_p->SMBunlock_count); + d_printf("unlock_time: %u\n", profile_p->SMBunlock_time); + d_printf("ctemp_count: %u\n", profile_p->SMBctemp_count); + d_printf("ctemp_time: %u\n", profile_p->SMBctemp_time); + d_printf("mknew_count: %u\n", profile_p->SMBmknew_count); + d_printf("mknew_time: %u\n", profile_p->SMBmknew_time); + d_printf("chkpth_count: %u\n", profile_p->SMBchkpth_count); + d_printf("chkpth_time: %u\n", profile_p->SMBchkpth_time); + d_printf("exit_count: %u\n", profile_p->SMBexit_count); + d_printf("exit_time: %u\n", profile_p->SMBexit_time); + d_printf("lseek_count: %u\n", profile_p->SMBlseek_count); + d_printf("lseek_time: %u\n", profile_p->SMBlseek_time); + d_printf("lockread_count: %u\n", profile_p->SMBlockread_count); + d_printf("lockread_time: %u\n", profile_p->SMBlockread_time); + d_printf("writeunlock_count: %u\n", profile_p->SMBwriteunlock_count); + d_printf("writeunlock_time: %u\n", profile_p->SMBwriteunlock_time); + d_printf("readbraw_count: %u\n", profile_p->SMBreadbraw_count); + d_printf("readbraw_time: %u\n", profile_p->SMBreadbraw_time); + d_printf("readBmpx_count: %u\n", profile_p->SMBreadBmpx_count); + d_printf("readBmpx_time: %u\n", profile_p->SMBreadBmpx_time); + d_printf("readBs_count: %u\n", profile_p->SMBreadBs_count); + d_printf("readBs_time: %u\n", profile_p->SMBreadBs_time); + d_printf("writebraw_count: %u\n", profile_p->SMBwritebraw_count); + d_printf("writebraw_time: %u\n", profile_p->SMBwritebraw_time); + d_printf("writeBmpx_count: %u\n", profile_p->SMBwriteBmpx_count); + d_printf("writeBmpx_time: %u\n", profile_p->SMBwriteBmpx_time); + d_printf("writeBs_count: %u\n", profile_p->SMBwriteBs_count); + d_printf("writeBs_time: %u\n", profile_p->SMBwriteBs_time); + d_printf("writec_count: %u\n", profile_p->SMBwritec_count); + d_printf("writec_time: %u\n", profile_p->SMBwritec_time); + d_printf("setattrE_count: %u\n", profile_p->SMBsetattrE_count); + d_printf("setattrE_time: %u\n", profile_p->SMBsetattrE_time); + d_printf("getattrE_count: %u\n", profile_p->SMBgetattrE_count); + d_printf("getattrE_time: %u\n", profile_p->SMBgetattrE_time); + d_printf("lockingX_count: %u\n", profile_p->SMBlockingX_count); + d_printf("lockingX_time: %u\n", profile_p->SMBlockingX_time); + d_printf("trans_count: %u\n", profile_p->SMBtrans_count); + d_printf("trans_time: %u\n", profile_p->SMBtrans_time); + d_printf("transs_count: %u\n", profile_p->SMBtranss_count); + d_printf("transs_time: %u\n", profile_p->SMBtranss_time); + d_printf("ioctl_count: %u\n", profile_p->SMBioctl_count); + d_printf("ioctl_time: %u\n", profile_p->SMBioctl_time); + d_printf("ioctls_count: %u\n", profile_p->SMBioctls_count); + d_printf("ioctls_time: %u\n", profile_p->SMBioctls_time); + d_printf("copy_count: %u\n", profile_p->SMBcopy_count); + d_printf("copy_time: %u\n", profile_p->SMBcopy_time); + d_printf("move_count: %u\n", profile_p->SMBmove_count); + d_printf("move_time: %u\n", profile_p->SMBmove_time); + d_printf("echo_count: %u\n", profile_p->SMBecho_count); + d_printf("echo_time: %u\n", profile_p->SMBecho_time); + d_printf("writeclose_count: %u\n", profile_p->SMBwriteclose_count); + d_printf("writeclose_time: %u\n", profile_p->SMBwriteclose_time); + d_printf("openX_count: %u\n", profile_p->SMBopenX_count); + d_printf("openX_time: %u\n", profile_p->SMBopenX_time); + d_printf("readX_count: %u\n", profile_p->SMBreadX_count); + d_printf("readX_time: %u\n", profile_p->SMBreadX_time); + d_printf("writeX_count: %u\n", profile_p->SMBwriteX_count); + d_printf("writeX_time: %u\n", profile_p->SMBwriteX_time); + d_printf("trans2_count: %u\n", profile_p->SMBtrans2_count); + d_printf("trans2_time: %u\n", profile_p->SMBtrans2_time); + d_printf("transs2_count: %u\n", profile_p->SMBtranss2_count); + d_printf("transs2_time: %u\n", profile_p->SMBtranss2_time); + d_printf("findclose_count: %u\n", profile_p->SMBfindclose_count); + d_printf("findclose_time: %u\n", profile_p->SMBfindclose_time); + d_printf("findnclose_count: %u\n", profile_p->SMBfindnclose_count); + d_printf("findnclose_time: %u\n", profile_p->SMBfindnclose_time); + d_printf("tcon_count: %u\n", profile_p->SMBtcon_count); + d_printf("tcon_time: %u\n", profile_p->SMBtcon_time); + d_printf("tdis_count: %u\n", profile_p->SMBtdis_count); + d_printf("tdis_time: %u\n", profile_p->SMBtdis_time); + d_printf("negprot_count: %u\n", profile_p->SMBnegprot_count); + d_printf("negprot_time: %u\n", profile_p->SMBnegprot_time); + d_printf("sesssetupX_count: %u\n", profile_p->SMBsesssetupX_count); + d_printf("sesssetupX_time: %u\n", profile_p->SMBsesssetupX_time); + d_printf("ulogoffX_count: %u\n", profile_p->SMBulogoffX_count); + d_printf("ulogoffX_time: %u\n", profile_p->SMBulogoffX_time); + d_printf("tconX_count: %u\n", profile_p->SMBtconX_count); + d_printf("tconX_time: %u\n", profile_p->SMBtconX_time); + d_printf("dskattr_count: %u\n", profile_p->SMBdskattr_count); + d_printf("dskattr_time: %u\n", profile_p->SMBdskattr_time); + d_printf("search_count: %u\n", profile_p->SMBsearch_count); + d_printf("search_time: %u\n", profile_p->SMBsearch_time); + d_printf("ffirst_count: %u\n", profile_p->SMBffirst_count); + d_printf("ffirst_time: %u\n", profile_p->SMBffirst_time); + d_printf("funique_count: %u\n", profile_p->SMBfunique_count); + d_printf("funique_time: %u\n", profile_p->SMBfunique_time); + d_printf("fclose_count: %u\n", profile_p->SMBfclose_count); + d_printf("fclose_time: %u\n", profile_p->SMBfclose_time); + d_printf("nttrans_count: %u\n", profile_p->SMBnttrans_count); + d_printf("nttrans_time: %u\n", profile_p->SMBnttrans_time); + d_printf("nttranss_count: %u\n", profile_p->SMBnttranss_count); + d_printf("nttranss_time: %u\n", profile_p->SMBnttranss_time); + d_printf("ntcreateX_count: %u\n", profile_p->SMBntcreateX_count); + d_printf("ntcreateX_time: %u\n", profile_p->SMBntcreateX_time); + d_printf("ntcancel_count: %u\n", profile_p->SMBntcancel_count); + d_printf("ntcancel_time: %u\n", profile_p->SMBntcancel_time); + d_printf("splopen_count: %u\n", profile_p->SMBsplopen_count); + d_printf("splopen_time: %u\n", profile_p->SMBsplopen_time); + d_printf("splwr_count: %u\n", profile_p->SMBsplwr_count); + d_printf("splwr_time: %u\n", profile_p->SMBsplwr_time); + d_printf("splclose_count: %u\n", profile_p->SMBsplclose_count); + d_printf("splclose_time: %u\n", profile_p->SMBsplclose_time); + d_printf("splretq_count: %u\n", profile_p->SMBsplretq_count); + d_printf("splretq_time: %u\n", profile_p->SMBsplretq_time); + d_printf("sends_count: %u\n", profile_p->SMBsends_count); + d_printf("sends_time: %u\n", profile_p->SMBsends_time); + d_printf("sendb_count: %u\n", profile_p->SMBsendb_count); + d_printf("sendb_time: %u\n", profile_p->SMBsendb_time); + d_printf("fwdname_count: %u\n", profile_p->SMBfwdname_count); + d_printf("fwdname_time: %u\n", profile_p->SMBfwdname_time); + d_printf("cancelf_count: %u\n", profile_p->SMBcancelf_count); + d_printf("cancelf_time: %u\n", profile_p->SMBcancelf_time); + d_printf("getmac_count: %u\n", profile_p->SMBgetmac_count); + d_printf("getmac_time: %u\n", profile_p->SMBgetmac_time); + d_printf("sendstrt_count: %u\n", profile_p->SMBsendstrt_count); + d_printf("sendstrt_time: %u\n", profile_p->SMBsendstrt_time); + d_printf("sendend_count: %u\n", profile_p->SMBsendend_count); + d_printf("sendend_time: %u\n", profile_p->SMBsendend_time); + d_printf("sendtxt_count: %u\n", profile_p->SMBsendtxt_count); + d_printf("sendtxt_time: %u\n", profile_p->SMBsendtxt_time); + d_printf("invalid_count: %u\n", profile_p->SMBinvalid_count); + d_printf("invalid_time: %u\n", profile_p->SMBinvalid_time); + d_printf("************************ Pathworks Calls *************************\n"); + d_printf("setdir_count: %u\n", profile_p->pathworks_setdir_count); + d_printf("setdir_time: %u\n", profile_p->pathworks_setdir_time); + d_printf("************************ Trans2 Calls ****************************\n"); + d_printf("open_count: %u\n", profile_p->Trans2_open_count); + d_printf("open_time: %u\n", profile_p->Trans2_open_time); + d_printf("findfirst_count: %u\n", profile_p->Trans2_findfirst_count); + d_printf("findfirst_time: %u\n", profile_p->Trans2_findfirst_time); + d_printf("findnext_count: %u\n", profile_p->Trans2_findnext_count); + d_printf("findnext_time: %u\n", profile_p->Trans2_findnext_time); + d_printf("qfsinfo_count: %u\n", profile_p->Trans2_qfsinfo_count); + d_printf("qfsinfo_time: %u\n", profile_p->Trans2_qfsinfo_time); + d_printf("setfsinfo_count: %u\n", profile_p->Trans2_setfsinfo_count); + d_printf("setfsinfo_time: %u\n", profile_p->Trans2_setfsinfo_time); + d_printf("qpathinfo_count: %u\n", profile_p->Trans2_qpathinfo_count); + d_printf("qpathinfo_time: %u\n", profile_p->Trans2_qpathinfo_time); + d_printf("setpathinfo_count: %u\n", profile_p->Trans2_setpathinfo_count); + d_printf("setpathinfo_time: %u\n", profile_p->Trans2_setpathinfo_time); + d_printf("qfileinfo_count: %u\n", profile_p->Trans2_qfileinfo_count); + d_printf("qfileinfo_time: %u\n", profile_p->Trans2_qfileinfo_time); + d_printf("setfileinfo_count: %u\n", profile_p->Trans2_setfileinfo_count); + d_printf("setfileinfo_time: %u\n", profile_p->Trans2_setfileinfo_time); + d_printf("fsctl_count: %u\n", profile_p->Trans2_fsctl_count); + d_printf("fsctl_time: %u\n", profile_p->Trans2_fsctl_time); + d_printf("ioctl_count: %u\n", profile_p->Trans2_ioctl_count); + d_printf("ioctl_time: %u\n", profile_p->Trans2_ioctl_time); + d_printf("findnotifyfirst_count: %u\n", profile_p->Trans2_findnotifyfirst_count); + d_printf("findnotifyfirst_time: %u\n", profile_p->Trans2_findnotifyfirst_time); + d_printf("findnotifynext_count: %u\n", profile_p->Trans2_findnotifynext_count); + d_printf("findnotifynext_time: %u\n", profile_p->Trans2_findnotifynext_time); + d_printf("mkdir_count: %u\n", profile_p->Trans2_mkdir_count); + d_printf("mkdir_time: %u\n", profile_p->Trans2_mkdir_time); + d_printf("session_setup_count: %u\n", profile_p->Trans2_session_setup_count); + d_printf("session_setup_time: %u\n", profile_p->Trans2_session_setup_time); + d_printf("get_dfs_referral_count: %u\n", profile_p->Trans2_get_dfs_referral_count); + d_printf("get_dfs_referral_time: %u\n", profile_p->Trans2_get_dfs_referral_time); + d_printf("report_dfs_inconsistancy_count: %u\n", profile_p->Trans2_report_dfs_inconsistancy_count); + d_printf("report_dfs_inconsistancy_time: %u\n", profile_p->Trans2_report_dfs_inconsistancy_time); + d_printf("************************ NT Transact Calls ***********************\n"); + d_printf("create_count: %u\n", profile_p->NT_transact_create_count); + d_printf("create_time: %u\n", profile_p->NT_transact_create_time); + d_printf("ioctl_count: %u\n", profile_p->NT_transact_ioctl_count); + d_printf("ioctl_time: %u\n", profile_p->NT_transact_ioctl_time); + d_printf("set_security_desc_count: %u\n", profile_p->NT_transact_set_security_desc_count); + d_printf("set_security_desc_time: %u\n", profile_p->NT_transact_set_security_desc_time); + d_printf("notify_change_count: %u\n", profile_p->NT_transact_notify_change_count); + d_printf("notify_change_time: %u\n", profile_p->NT_transact_notify_change_time); + d_printf("rename_count: %u\n", profile_p->NT_transact_rename_count); + d_printf("rename_time: %u\n", profile_p->NT_transact_rename_time); + d_printf("query_security_desc_count: %u\n", profile_p->NT_transact_query_security_desc_count); + d_printf("query_security_desc_time: %u\n", profile_p->NT_transact_query_security_desc_time); + d_printf("************************ ACL Calls *******************************\n"); + d_printf("get_nt_acl_count: %u\n", profile_p->get_nt_acl_count); + d_printf("get_nt_acl_time: %u\n", profile_p->get_nt_acl_time); + d_printf("fget_nt_acl_count: %u\n", profile_p->fget_nt_acl_count); + d_printf("fget_nt_acl_time: %u\n", profile_p->fget_nt_acl_time); + d_printf("set_nt_acl_count: %u\n", profile_p->set_nt_acl_count); + d_printf("set_nt_acl_time: %u\n", profile_p->set_nt_acl_time); + d_printf("fset_nt_acl_count: %u\n", profile_p->fset_nt_acl_count); + d_printf("fset_nt_acl_time: %u\n", profile_p->fset_nt_acl_time); + d_printf("chmod_acl_count: %u\n", profile_p->chmod_acl_count); + d_printf("chmod_acl_time: %u\n", profile_p->chmod_acl_time); + d_printf("fchmod_acl_count: %u\n", profile_p->fchmod_acl_count); + d_printf("fchmod_acl_time: %u\n", profile_p->fchmod_acl_time); + d_printf("************************ NMBD Calls ****************************\n"); + d_printf("name_release_count: %u\n", profile_p->name_release_count); + d_printf("name_release_time: %u\n", profile_p->name_release_time); + d_printf("name_refresh_count: %u\n", profile_p->name_refresh_count); + d_printf("name_refresh_time: %u\n", profile_p->name_refresh_time); + d_printf("name_registration_count: %u\n", profile_p->name_registration_count); + d_printf("name_registration_time: %u\n", profile_p->name_registration_time); + d_printf("node_status_count: %u\n", profile_p->node_status_count); + d_printf("node_status_time: %u\n", profile_p->node_status_time); + d_printf("name_query_count: %u\n", profile_p->name_query_count); + d_printf("name_query_time: %u\n", profile_p->name_query_time); + d_printf("host_announce_count: %u\n", profile_p->host_announce_count); + d_printf("host_announce_time: %u\n", profile_p->host_announce_time); + d_printf("workgroup_announce_count: %u\n", profile_p->workgroup_announce_count); + d_printf("workgroup_announce_time: %u\n", profile_p->workgroup_announce_time); + d_printf("local_master_announce_count: %u\n", profile_p->local_master_announce_count); + d_printf("local_master_announce_time: %u\n", profile_p->local_master_announce_time); + d_printf("master_browser_announce_count: %u\n", profile_p->master_browser_announce_count); + d_printf("master_browser_announce_time: %u\n", profile_p->master_browser_announce_time); + d_printf("lm_host_announce_count: %u\n", profile_p->lm_host_announce_count); + d_printf("lm_host_announce_time: %u\n", profile_p->lm_host_announce_time); + d_printf("get_backup_list_count: %u\n", profile_p->get_backup_list_count); + d_printf("get_backup_list_time: %u\n", profile_p->get_backup_list_time); + d_printf("reset_browser_count: %u\n", profile_p->reset_browser_count); + d_printf("reset_browser_time: %u\n", profile_p->reset_browser_time); + d_printf("announce_request_count: %u\n", profile_p->announce_request_count); + d_printf("announce_request_time: %u\n", profile_p->announce_request_time); + d_printf("lm_announce_request_count: %u\n", profile_p->lm_announce_request_count); + d_printf("lm_announce_request_time: %u\n", profile_p->lm_announce_request_time); + d_printf("domain_logon_count: %u\n", profile_p->domain_logon_count); + d_printf("domain_logon_time: %u\n", profile_p->domain_logon_time); + d_printf("sync_browse_lists_count: %u\n", profile_p->sync_browse_lists_count); + d_printf("sync_browse_lists_time: %u\n", profile_p->sync_browse_lists_time); + d_printf("run_elections_count: %u\n", profile_p->run_elections_count); + d_printf("run_elections_time: %u\n", profile_p->run_elections_time); + d_printf("election_count: %u\n", profile_p->election_count); + d_printf("election_time: %u\n", profile_p->election_time); +#else /* WITH_PROFILE */ + fprintf(stderr, "Profile data unavailable\n"); +#endif /* WITH_PROFILE */ + + return 0; +} + + +static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct connections_data crec; + + if (dbuf.dsize != sizeof(crec)) + return 0; + + memcpy(&crec, dbuf.dptr, sizeof(crec)); + + if (crec.cnum == -1) + return 0; + + if (!process_exists(crec.pid) || !Ucrit_checkUsername(uidtoname(crec.uid))) { + return 0; + } + + d_printf("%-10.10s %5d %-12s %s", + crec.name,(int)crec.pid, + crec.machine, + asctime(LocalTime(&crec.start))); + + return 0; +} + +static int traverse_sessionid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void *state) +{ + struct sessionid sessionid; + TALLOC_CTX *mem_ctx; + + if (dbuf.dsize != sizeof(sessionid)) + return 0; + + memcpy(&sessionid, dbuf.dptr, sizeof(sessionid)); + + if (!process_exists(sessionid.pid) || !Ucrit_checkUsername(uidtoname(sessionid.uid))) { + return 0; + } + + mem_ctx = talloc_init("smbgroupedit talloc"); + if (!mem_ctx) return -1; + d_printf("%5d %-12s %-12s %-12s (%s)\n", + (int)sessionid.pid, uidtoname(sessionid.uid), + gidtoname(mem_ctx, sessionid.gid), + sessionid.remote_machine, sessionid.hostname); + talloc_destroy(mem_ctx); + return 0; +} + + + + + int main(int argc, char *argv[]) +{ + int c; + static int profile_only = 0; + TDB_CONTEXT *tdb; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"processes", 'p', POPT_ARG_NONE, &processes_only, 'p', "Show processes only" }, + {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" }, + {"locks", 'L', POPT_ARG_NONE, &locks_only, 'L', "Show locks only" }, + {"shares", 'S', POPT_ARG_NONE, &shares_only, 'S', "Show shares only" }, + {"user", 'u', POPT_ARG_STRING, 0, 'u', "Switch to user" }, + {"brief", 'b', POPT_ARG_NONE, &brief, 'b', "Be brief" }, +#ifdef WITH_PROFILE + {"profile", 'P', POPT_ARG_NONE, &profile_only, 'P', "Do profiling" }, +#endif /* WITH_PROFILE */ + {"byterange", 'B', POPT_ARG_NONE, &show_brl, 'B', "Include byte range locks"}, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + + setup_logging(argv[0],True); + + dbf = x_stderr; + + if (getuid() != geteuid()) { + d_printf("smbstatus should not be run setuid\n"); + return(1); + } + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while ((c = poptGetNextOpt(pc)) != EOF) { + switch (c) { + case 'u': + Ucrit_addUsername(poptGetOptArg(pc)); + break; + } + } + + if (verbose) { + d_printf("using configfile = %s\n", dyn_CONFIGFILE); + } + + if (!lp_load(dyn_CONFIGFILE,False,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE); + return (-1); + } + + if (profile_only) { + return profile_dump(); + } + + tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0); + if (!tdb) { + d_printf("sessionid.tdb not initialised\n"); + } else { + if (locks_only) goto locks; + + d_printf("\nSamba version %s\n",SAMBA_VERSION); + d_printf("PID Username Group Machine \n"); + d_printf("-------------------------------------------------------------------\n"); + + tdb_traverse(tdb, traverse_sessionid, NULL); + tdb_close(tdb); + } + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0); + if (!tdb) { + d_printf("%s not initialised\n", lock_path("connections.tdb")); + d_printf("This is normal if an SMB client has never connected to your server.\n"); + } else { + if (verbose) { + d_printf("Opened %s\n", lock_path("connections.tdb")); + } + + if (brief) + exit(0); + + d_printf("\nService pid machine Connected at\n"); + d_printf("-------------------------------------------------------\n"); + + tdb_traverse(tdb, traverse_fn1, NULL); + tdb_close(tdb); + } + + locks: + if (processes_only) exit(0); + + if (!shares_only) { + int ret; + + if (!locking_init(1)) { + d_printf("Can't initialise locking module - exiting\n"); + exit(1); + } + + ret = share_mode_forall(print_share_mode); + + if (ret == 0) { + d_printf("No locked files\n"); + } else if (ret == -1) { + d_printf("locked file list truncated\n"); + } + + d_printf("\n"); + + if (show_brl) { + brl_forall(print_brl); + } + + locking_end(); + } + + return (0); +} diff --git a/source4/utils/tdb/Makefile b/source4/utils/tdb/Makefile new file mode 100644 index 0000000000..59fbb079bd --- /dev/null +++ b/source4/utils/tdb/Makefile @@ -0,0 +1,29 @@ +# +# Makefile for tdb directory +# + +CFLAGS = -DSTANDALONE -DTDB_DEBUG -g -DHAVE_MMAP=1 +CC = gcc + +PROGS = tdbtest tdbtool tdbtorture +TDB_OBJ = tdb.o spinlock.o + +default: $(PROGS) + +tdbtest: tdbtest.o $(TDB_OBJ) + $(CC) $(CFLAGS) -o tdbtest tdbtest.o $(TDB_OBJ) -lgdbm + +tdbtool: tdbtool.o $(TDB_OBJ) + $(CC) $(CFLAGS) -o tdbtool tdbtool.o $(TDB_OBJ) + +tdbtorture: tdbtorture.o $(TDB_OBJ) + $(CC) $(CFLAGS) -o tdbtorture tdbtorture.o $(TDB_OBJ) + +tdbdump: tdbdump.o $(TDB_OBJ) + $(CC) $(CFLAGS) -o tdbdump tdbdump.o $(TDB_OBJ) + +tdbbackup: tdbbackup.o $(TDB_OBJ) + $(CC) $(CFLAGS) -o tdbbackup tdbbackup.o $(TDB_OBJ) + +clean: + rm -f $(PROGS) *.o *~ *% core test.db test.tdb test.gdbm diff --git a/source4/utils/tdb/README b/source4/utils/tdb/README new file mode 100644 index 0000000000..fac3eacb4d --- /dev/null +++ b/source4/utils/tdb/README @@ -0,0 +1,167 @@ +tdb - a trivial database system +tridge@linuxcare.com December 1999 +================================== + +This is a simple database API. It was inspired by the realisation that +in Samba we have several ad-hoc bits of code that essentially +implement small databases for sharing structures between parts of +Samba. As I was about to add another I realised that a generic +database module was called for to replace all the ad-hoc bits. + +I based the interface on gdbm. I couldn't use gdbm as we need to be +able to have multiple writers to the databases at one time. + +Compilation +----------- + +add HAVE_MMAP=1 to use mmap instead of read/write +add TDB_DEBUG=1 for verbose debug info +add NOLOCK=1 to disable locking code + +Testing +------- + +Compile tdbtest.c and link with gdbm for testing. tdbtest will perform +identical operations via tdb and gdbm then make sure the result is the +same + +Also included is tdbtool, which allows simple database manipulation +on the commandline. + +tdbtest and tdbtool are not built as part of Samba, but are included +for completeness. + +Interface +--------- + +The interface is very similar to gdbm except for the following: + +- different open interface. The tdb_open call is more similar to a + traditional open() +- no tdbm_reorganise() function +- no tdbm_sync() function. No operations are cached in the library anyway +- added a tdb_traverse() function for traversing the whole database + +A general rule for using tdb is that the caller frees any returned +TDB_DATA structures. Just call free(p.dptr) to free a TDB_DATA +return value called p. This is the same as gdbm. + +here is a full list of tdb functions with brief descriptions. + + +---------------------------------------------------------------------- +TDB_CONTEXT *tdb_open(char *name, int hash_size, int tdb_flags, + int open_flags, mode_t mode) + + open the database, creating it if necessary + + The open_flags and mode are passed straight to the open call on the database + file. A flags value of O_WRONLY is invalid + + The hash size is advisory, use zero for a default value. + + return is NULL on error + + possible tdb_flags are: + TDB_CLEAR_IF_FIRST - clear database if we are the only one with it open + TDB_INTERNAL - don't use a file, instaed store the data in + memory. The filename is ignored in this case. + TDB_NOLOCK - don't do any locking + TDB_NOMMAP - don't use mmap + +---------------------------------------------------------------------- +char *tdb_error(TDB_CONTEXT *tdb); + + return a error string for the last tdb error + +---------------------------------------------------------------------- +int tdb_close(TDB_CONTEXT *tdb); + + close a database + +---------------------------------------------------------------------- +int tdb_update(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf); + + update an entry in place - this only works if the new data size + is <= the old data size and the key exists. + on failure return -1 + +---------------------------------------------------------------------- +TDB_DATA tdb_fetch(TDB_CONTEXT *tdb, TDB_DATA key); + + fetch an entry in the database given a key + if the return value has a null dptr then a error occurred + + caller must free the resulting data + +---------------------------------------------------------------------- +int tdb_exists(TDB_CONTEXT *tdb, TDB_DATA key); + + check if an entry in the database exists + + note that 1 is returned if the key is found and 0 is returned if not found + this doesn't match the conventions in the rest of this module, but is + compatible with gdbm + +---------------------------------------------------------------------- +int tdb_traverse(TDB_CONTEXT *tdb, int (*fn)(TDB_CONTEXT *tdb, + TDB_DATA key, TDB_DATA dbuf, void *state), void *state); + + traverse the entire database - calling fn(tdb, key, data, state) on each + element. + + return -1 on error or the record count traversed + + if fn is NULL then it is not called + + a non-zero return value from fn() indicates that the traversal should stop + +---------------------------------------------------------------------- +TDB_DATA tdb_firstkey(TDB_CONTEXT *tdb); + + find the first entry in the database and return its key + + the caller must free the returned data + +---------------------------------------------------------------------- +TDB_DATA tdb_nextkey(TDB_CONTEXT *tdb, TDB_DATA key); + + find the next entry in the database, returning its key + + the caller must free the returned data + +---------------------------------------------------------------------- +int tdb_delete(TDB_CONTEXT *tdb, TDB_DATA key); + + delete an entry in the database given a key + +---------------------------------------------------------------------- +int tdb_store(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); + + store an element in the database, replacing any existing element + with the same key + + If flag==TDB_INSERT then don't overwrite an existing entry + If flag==TDB_MODIFY then don't create a new entry + + return 0 on success, -1 on failure + +---------------------------------------------------------------------- +int tdb_writelock(TDB_CONTEXT *tdb); + + lock the database. If we already have it locked then don't do anything + +---------------------------------------------------------------------- +int tdb_writeunlock(TDB_CONTEXT *tdb); + unlock the database + +---------------------------------------------------------------------- +int tdb_lockchain(TDB_CONTEXT *tdb, TDB_DATA key); + + lock one hash chain. This is meant to be used to reduce locking + contention - it cannot guarantee how many records will be locked + +---------------------------------------------------------------------- +int tdb_unlockchain(TDB_CONTEXT *tdb, TDB_DATA key); + + unlock one hash chain diff --git a/source4/utils/tdb/tdb.magic b/source4/utils/tdb/tdb.magic new file mode 100644 index 0000000000..f5619e7327 --- /dev/null +++ b/source4/utils/tdb/tdb.magic @@ -0,0 +1,10 @@ +# Magic file(1) information about tdb files. +# +# Install this into /etc/magic or the corresponding location for your +# system, or pass as a -m argument to file(1). + +# You may use and redistribute this file without restriction. + +0 string TDB\ file TDB database +>32 lelong =0x2601196D version 6, little-endian +>>36 lelong x hash size %d bytes diff --git a/source4/utils/tdb/tdbbackup.c b/source4/utils/tdb/tdbbackup.c new file mode 100644 index 0000000000..7b344de6c4 --- /dev/null +++ b/source4/utils/tdb/tdbbackup.c @@ -0,0 +1,315 @@ +/* + Unix SMB/CIFS implementation. + low level tdb backup and restore utility + Copyright (C) Andrew Tridgell 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. +*/ + +/* + + This program is meant for backup/restore of tdb databases. Typical usage would be: + tdbbackup *.tdb + when Samba shuts down cleanly, which will make a backup of all the local databases + to *.bak files. Then on Samba startup you would use: + tdbbackup -v *.tdb + and this will check the databases for corruption and if corruption is detected then + the backup will be restored. + + You may also like to do a backup on a regular basis while Samba is + running, perhaps using cron. + + The reason this program is needed is to cope with power failures + while Samba is running. A power failure could lead to database + corruption and Samba will then not start correctly. + + Note that many of the databases in Samba are transient and thus + don't need to be backed up, so you can optimise the above a little + by only running the backup on the critical databases. + + */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <ctype.h> +#include <signal.h> +#include "tdb.h" + +static int failed; + +static char *add_suffix(const char *name, const char *suffix) +{ + char *ret; + int len = strlen(name) + strlen(suffix) + 1; + ret = malloc(len); + if (!ret) { + fprintf(stderr,"Out of memory!\n"); + exit(1); + } + strncpy(ret, name, len); + strncat(ret, suffix, len); + return ret; +} + +static int copy_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ + TDB_CONTEXT *tdb_new = (TDB_CONTEXT *)state; + + if (tdb_store(tdb_new, key, dbuf, TDB_INSERT) != 0) { + fprintf(stderr,"Failed to insert into %s\n", tdb_new->name); + failed = 1; + return 1; + } + return 0; +} + + +static int test_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ + return 0; +} + +/* + carefully backup a tdb, validating the contents and + only doing the backup if its OK + this function is also used for restore +*/ +static int backup_tdb(const char *old_name, const char *new_name) +{ + TDB_CONTEXT *tdb; + TDB_CONTEXT *tdb_new; + char *tmp_name; + struct stat st; + int count1, count2; + + tmp_name = add_suffix(new_name, ".tmp"); + + /* stat the old tdb to find its permissions */ + if (stat(old_name, &st) != 0) { + perror(old_name); + return 1; + } + + /* open the old tdb */ + tdb = tdb_open(old_name, 0, 0, O_RDWR, 0); + if (!tdb) { + printf("Failed to open %s\n", old_name); + return 1; + } + + /* create the new tdb */ + unlink(tmp_name); + tdb_new = tdb_open(tmp_name, tdb->header.hash_size, + TDB_DEFAULT, O_RDWR|O_CREAT|O_EXCL, + st.st_mode & 0777); + if (!tdb_new) { + perror(tmp_name); + free(tmp_name); + return 1; + } + + /* lock the old tdb */ + if (tdb_lockall(tdb) != 0) { + fprintf(stderr,"Failed to lock %s\n", old_name); + tdb_close(tdb); + tdb_close(tdb_new); + unlink(tmp_name); + free(tmp_name); + return 1; + } + + failed = 0; + + /* traverse and copy */ + count1 = tdb_traverse(tdb, copy_fn, (void *)tdb_new); + if (count1 < 0 || failed) { + fprintf(stderr,"failed to copy %s\n", old_name); + tdb_close(tdb); + tdb_close(tdb_new); + unlink(tmp_name); + free(tmp_name); + return 1; + } + + /* close the old tdb */ + tdb_close(tdb); + + /* close the new tdb and re-open read-only */ + tdb_close(tdb_new); + tdb_new = tdb_open(tmp_name, 0, TDB_DEFAULT, O_RDONLY, 0); + if (!tdb_new) { + fprintf(stderr,"failed to reopen %s\n", tmp_name); + unlink(tmp_name); + perror(tmp_name); + free(tmp_name); + return 1; + } + + /* traverse the new tdb to confirm */ + count2 = tdb_traverse(tdb_new, test_fn, 0); + if (count2 != count1) { + fprintf(stderr,"failed to copy %s\n", old_name); + tdb_close(tdb_new); + unlink(tmp_name); + free(tmp_name); + return 1; + } + + /* make sure the new tdb has reached stable storage */ + fsync(tdb_new->fd); + + /* close the new tdb and rename it to .bak */ + tdb_close(tdb_new); + unlink(new_name); + if (rename(tmp_name, new_name) != 0) { + perror(new_name); + free(tmp_name); + return 1; + } + + printf("%s : %d records\n", old_name, count1); + free(tmp_name); + + return 0; +} + + + +/* + verify a tdb and if it is corrupt then restore from *.bak +*/ +static int verify_tdb(const char *fname, const char *bak_name) +{ + TDB_CONTEXT *tdb; + int count = -1; + + /* open the tdb */ + tdb = tdb_open(fname, 0, 0, O_RDONLY, 0); + + /* traverse the tdb, then close it */ + if (tdb) { + count = tdb_traverse(tdb, test_fn, NULL); + tdb_close(tdb); + } + + /* count is < 0 means an error */ + if (count < 0) { + printf("restoring %s\n", fname); + return backup_tdb(bak_name, fname); + } + + printf("%s : %d records\n", fname, count); + + return 0; +} + + +/* + see if one file is newer than another +*/ +static int file_newer(const char *fname1, const char *fname2) +{ + struct stat st1, st2; + if (stat(fname1, &st1) != 0) { + return 0; + } + if (stat(fname2, &st2) != 0) { + return 1; + } + return (st1.st_mtime > st2.st_mtime); +} + +static void usage(void) +{ + printf("Usage: tdbbackup [options] <fname...>\n\n"); + printf(" -h this help message\n"); + printf(" -s suffix set the backup suffix\n"); + printf(" -v veryify mode (restore if corrupt)\n"); +} + + + int main(int argc, char *argv[]) +{ + int i; + int ret = 0; + int c; + int verify = 0; + char *suffix = ".bak"; + extern int optind; + extern char *optarg; + + while ((c = getopt(argc, argv, "vhs:")) != -1) { + switch (c) { + case 'h': + usage(); + exit(0); + case 'v': + verify = 1; + break; + case 's': + suffix = optarg; + break; + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + exit(1); + } + + for (i=0; i<argc; i++) { + const char *fname = argv[i]; + char *bak_name; + + bak_name = add_suffix(fname, suffix); + + if (verify) { + if (verify_tdb(fname, bak_name) != 0) { + ret = 1; + } + } else { + if (file_newer(fname, bak_name) && + backup_tdb(fname, bak_name) != 0) { + ret = 1; + } + } + + free(bak_name); + } + + return ret; +} + +#ifdef VALGRIND +size_t valgrind_strlen(const char *s) +{ + size_t count; + for(count = 0; *s++; count++) + ; + return count; +} +#endif diff --git a/source4/utils/tdb/tdbdump.c b/source4/utils/tdb/tdbdump.c new file mode 100644 index 0000000000..9c1dc2761b --- /dev/null +++ b/source4/utils/tdb/tdbdump.c @@ -0,0 +1,89 @@ +/* + Unix SMB/CIFS implementation. + simple tdb dump util + Copyright (C) Andrew Tridgell 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. +*/ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <ctype.h> +#include <signal.h> +#include "tdb.h" + +static void print_data(TDB_DATA d) +{ + unsigned char *p = d.dptr; + int len = d.dsize; + while (len--) { + if (isprint(*p) && !strchr("\"\\", *p)) { + fputc(*p, stdout); + } else { + printf("\\%02X", *p); + } + p++; + } +} + +static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ + printf("{\n"); + printf("key = \""); + print_data(key); + printf("\"\n"); + printf("data = \""); + print_data(dbuf); + printf("\"\n"); + printf("}\n"); + return 0; +} + +static int dump_tdb(const char *fname) +{ + TDB_CONTEXT *tdb; + + tdb = tdb_open(fname, 0, 0, O_RDONLY, 0); + if (!tdb) { + printf("Failed to open %s\n", fname); + return 1; + } + + tdb_traverse(tdb, traverse_fn, NULL); + return 0; +} + + int main(int argc, char *argv[]) +{ + char *fname; + + if (argc < 2) { + printf("Usage: tdbdump <fname>\n"); + exit(1); + } + + fname = argv[1]; + + return dump_tdb(fname); +} diff --git a/source4/utils/tdb/tdbtest.c b/source4/utils/tdb/tdbtest.c new file mode 100644 index 0000000000..89295a3291 --- /dev/null +++ b/source4/utils/tdb/tdbtest.c @@ -0,0 +1,263 @@ +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <signal.h> +#include "tdb.h" +#include <gdbm.h> + +/* a test program for tdb - the trivial database */ + + + +#define DELETE_PROB 7 +#define STORE_PROB 5 + +static TDB_CONTEXT *db; +static GDBM_FILE gdbm; + +struct timeval tp1,tp2; + +static void start_timer(void) +{ + gettimeofday(&tp1,NULL); +} + +static double end_timer(void) +{ + gettimeofday(&tp2,NULL); + return((tp2.tv_sec - tp1.tv_sec) + + (tp2.tv_usec - tp1.tv_usec)*1.0e-6); +} + +static void fatal(char *why) +{ + perror(why); + exit(1); +} + +static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + fflush(stdout); +} + +static void compare_db(void) +{ + TDB_DATA d, key, nextkey; + datum gd, gkey, gnextkey; + + key = tdb_firstkey(db); + while (key.dptr) { + d = tdb_fetch(db, key); + gkey.dptr = key.dptr; + gkey.dsize = key.dsize; + + gd = gdbm_fetch(gdbm, gkey); + + if (!gd.dptr) fatal("key not in gdbm"); + if (gd.dsize != d.dsize) fatal("data sizes differ"); + if (memcmp(gd.dptr, d.dptr, d.dsize)) { + fatal("data differs"); + } + + nextkey = tdb_nextkey(db, key); + free(key.dptr); + free(d.dptr); + free(gd.dptr); + key = nextkey; + } + + gkey = gdbm_firstkey(gdbm); + while (gkey.dptr) { + gd = gdbm_fetch(gdbm, gkey); + key.dptr = gkey.dptr; + key.dsize = gkey.dsize; + + d = tdb_fetch(db, key); + + if (!d.dptr) fatal("key not in db"); + if (d.dsize != gd.dsize) fatal("data sizes differ"); + if (memcmp(d.dptr, gd.dptr, gd.dsize)) { + fatal("data differs"); + } + + gnextkey = gdbm_nextkey(gdbm, gkey); + free(gkey.dptr); + free(gd.dptr); + free(d.dptr); + gkey = gnextkey; + } +} + +static char *randbuf(int len) +{ + char *buf; + int i; + buf = (char *)malloc(len+1); + + for (i=0;i<len;i++) { + buf[i] = 'a' + (rand() % 26); + } + buf[i] = 0; + return buf; +} + +static void addrec_db(void) +{ + int klen, dlen; + char *k, *d; + TDB_DATA key, data; + + klen = 1 + (rand() % 4); + dlen = 1 + (rand() % 100); + + k = randbuf(klen); + d = randbuf(dlen); + + key.dptr = k; + key.dsize = klen+1; + + data.dptr = d; + data.dsize = dlen+1; + + if (rand() % DELETE_PROB == 0) { + tdb_delete(db, key); + } else if (rand() % STORE_PROB == 0) { + if (tdb_store(db, key, data, TDB_REPLACE) != 0) { + fatal("tdb_store failed"); + } + } else { + data = tdb_fetch(db, key); + if (data.dptr) free(data.dptr); + } + + free(k); + free(d); +} + +static void addrec_gdbm(void) +{ + int klen, dlen; + char *k, *d; + datum key, data; + + klen = 1 + (rand() % 4); + dlen = 1 + (rand() % 100); + + k = randbuf(klen); + d = randbuf(dlen); + + key.dptr = k; + key.dsize = klen+1; + + data.dptr = d; + data.dsize = dlen+1; + + if (rand() % DELETE_PROB == 0) { + gdbm_delete(gdbm, key); + } else if (rand() % STORE_PROB == 0) { + if (gdbm_store(gdbm, key, data, GDBM_REPLACE) != 0) { + fatal("gdbm_store failed"); + } + } else { + data = gdbm_fetch(gdbm, key); + if (data.dptr) free(data.dptr); + } + + free(k); + free(d); +} + +static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ +#if 0 + printf("[%s] [%s]\n", key.dptr, dbuf.dptr); +#endif + tdb_delete(tdb, key); + return 0; +} + +static void merge_test(void) +{ + int i; + char keys[5][2]; + TDB_DATA key, data; + + for (i = 0; i < 5; i++) { + sprintf(keys[i], "%d", i); + key.dptr = keys[i]; + key.dsize = 2; + + data.dptr = "test"; + data.dsize = 4; + + if (tdb_store(db, key, data, TDB_REPLACE) != 0) { + fatal("tdb_store failed"); + } + } + + key.dptr = keys[0]; + tdb_delete(db, key); + key.dptr = keys[4]; + tdb_delete(db, key); + key.dptr = keys[2]; + tdb_delete(db, key); + key.dptr = keys[1]; + tdb_delete(db, key); + key.dptr = keys[3]; + tdb_delete(db, key); +} + +int main(int argc, char *argv[]) +{ + int i, seed=0; + int loops = 10000; + + unlink("test.gdbm"); + + db = tdb_open("test.tdb", 0, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT | O_TRUNC, 0600); + gdbm = gdbm_open("test.gdbm", 512, GDBM_WRITER|GDBM_NEWDB|GDBM_FAST, + 0600, NULL); + + if (!db || !gdbm) { + fatal("db open failed"); + } + + tdb_logging_function(db, tdb_log); + +#if 1 + srand(seed); + start_timer(); + for (i=0;i<loops;i++) addrec_gdbm(); + printf("gdbm got %.2f ops/sec\n", i/end_timer()); +#endif + + merge_test(); + + srand(seed); + start_timer(); + for (i=0;i<loops;i++) addrec_db(); + printf("tdb got %.2f ops/sec\n", i/end_timer()); + + compare_db(); + + printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL)); + printf("traversed %d records\n", tdb_traverse(db, traverse_fn, NULL)); + + tdb_close(db); + gdbm_close(gdbm); + + return 0; +} diff --git a/source4/utils/tdb/tdbtool.c b/source4/utils/tdb/tdbtool.c new file mode 100644 index 0000000000..f5e486be14 --- /dev/null +++ b/source4/utils/tdb/tdbtool.c @@ -0,0 +1,482 @@ +/* + Unix SMB/CIFS implementation. + Samba database functions + Copyright (C) Andrew Tridgell 1999-2000 + Copyright (C) Paul `Rusty' Russell 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Andrew Esh 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. +*/ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <ctype.h> +#include <signal.h> +#include "tdb.h" + +/* a tdb tool for manipulating a tdb database */ + +#define FSTRING_LEN 256 +typedef char fstring[FSTRING_LEN]; + +typedef struct connections_key { + pid_t pid; + int cnum; + fstring name; +} connections_key; + +typedef struct connections_data { + int magic; + pid_t pid; + int cnum; + uid_t uid; + gid_t gid; + char name[24]; + char addr[24]; + char machine[128]; + time_t start; +} connections_data; + +static TDB_CONTEXT *tdb; + +static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state); + +static void print_asc(unsigned char *buf,int len) +{ + int i; + + /* We're probably printing ASCII strings so don't try to display + the trailing NULL character. */ + + if (buf[len - 1] == 0) + len--; + + for (i=0;i<len;i++) + printf("%c",isprint(buf[i])?buf[i]:'.'); +} + +static void print_data(unsigned char *buf,int len) +{ + int i=0; + if (len<=0) return; + printf("[%03X] ",i); + for (i=0;i<len;) { + printf("%02X ",(int)buf[i]); + i++; + if (i%8 == 0) printf(" "); + if (i%16 == 0) { + print_asc(&buf[i-16],8); printf(" "); + print_asc(&buf[i-8],8); printf("\n"); + if (i<len) printf("[%03X] ",i); + } + } + if (i%16) { + int n; + + n = 16 - (i%16); + printf(" "); + if (n>8) printf(" "); + while (n--) printf(" "); + + n = i%16; + if (n > 8) n = 8; + print_asc(&buf[i-(i%16)],n); printf(" "); + n = (i%16) - n; + if (n>0) print_asc(&buf[i-n],n); + printf("\n"); + } +} + +static void help(void) +{ + printf(" +tdbtool: + create dbname : create a database + open dbname : open an existing database + erase : erase the database + dump : dump the database as strings + insert key data : insert a record + store key data : store a record (replace) + show key : show a record by key + delete key : delete a record by key + list : print the database hash table and freelist + free : print the database freelist + 1 | first : print the first record + n | next : print the next record + q | quit : terminate + \\n : repeat 'next' command +"); +} + +static void terror(char *why) +{ + printf("%s\n", why); +} + +static char *get_token(int startover) +{ + static char tmp[1024]; + static char *cont = NULL; + char *insert, *start; + char *k = strtok(NULL, " "); + + if (!k) + return NULL; + + if (startover) + start = tmp; + else + start = cont; + + strcpy(start, k); + insert = start + strlen(start) - 1; + while (*insert == '\\') { + *insert++ = ' '; + k = strtok(NULL, " "); + if (!k) + break; + strcpy(insert, k); + insert = start + strlen(start) - 1; + } + + /* Get ready for next call */ + cont = start + strlen(start) + 1; + return start; +} + +static void create_tdb(void) +{ + char *tok = get_token(1); + if (!tok) { + help(); + return; + } + if (tdb) tdb_close(tdb); + tdb = tdb_open(tok, 0, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (!tdb) { + printf("Could not create %s: %s\n", tok, strerror(errno)); + } +} + +static void open_tdb(void) +{ + char *tok = get_token(1); + if (!tok) { + help(); + return; + } + if (tdb) tdb_close(tdb); + tdb = tdb_open(tok, 0, 0, O_RDWR, 0600); + if (!tdb) { + printf("Could not open %s: %s\n", tok, strerror(errno)); + } +} + +static void insert_tdb(void) +{ + char *k = get_token(1); + char *d = get_token(0); + TDB_DATA key, dbuf; + + if (!k || !d) { + help(); + return; + } + + key.dptr = k; + key.dsize = strlen(k)+1; + dbuf.dptr = d; + dbuf.dsize = strlen(d)+1; + + if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) { + terror("insert failed"); + } +} + +static void store_tdb(void) +{ + char *k = get_token(1); + char *d = get_token(0); + TDB_DATA key, dbuf; + + if (!k || !d) { + help(); + return; + } + + key.dptr = k; + key.dsize = strlen(k)+1; + dbuf.dptr = d; + dbuf.dsize = strlen(d)+1; + + printf("Storing key:\n"); + print_rec(tdb, key, dbuf, NULL); + + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) { + terror("store failed"); + } +} + +static void show_tdb(void) +{ + char *k = get_token(1); + TDB_DATA key, dbuf; + + if (!k) { + help(); + return; + } + + key.dptr = k; +/* key.dsize = strlen(k)+1;*/ + key.dsize = strlen(k); + + dbuf = tdb_fetch(tdb, key); + if (!dbuf.dptr) { + terror("fetch failed"); + return; + } + /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */ + print_rec(tdb, key, dbuf, NULL); +} + +static void delete_tdb(void) +{ + char *k = get_token(1); + TDB_DATA key; + + if (!k) { + help(); + return; + } + + key.dptr = k; + key.dsize = strlen(k)+1; + + if (tdb_delete(tdb, key) != 0) { + terror("delete failed"); + } +} + +#if 0 +static int print_conn_key(TDB_DATA key) +{ + printf( "pid =%5d ", ((connections_key*)key.dptr)->pid); + printf( "cnum =%10d ", ((connections_key*)key.dptr)->cnum); + printf( "name =[%s]\n", ((connections_key*)key.dptr)->name); + return 0; +} + +static int print_conn_data(TDB_DATA dbuf) +{ + printf( "pid =%5d ", ((connections_data*)dbuf.dptr)->pid); + printf( "cnum =%10d ", ((connections_data*)dbuf.dptr)->cnum); + printf( "name =[%s]\n", ((connections_data*)dbuf.dptr)->name); + + printf( "uid =%5d ", ((connections_data*)dbuf.dptr)->uid); + printf( "addr =[%s]\n", ((connections_data*)dbuf.dptr)->addr); + printf( "gid =%5d ", ((connections_data*)dbuf.dptr)->gid); + printf( "machine=[%s]\n", ((connections_data*)dbuf.dptr)->machine); + printf( "start = %s\n", ctime(&((connections_data*)dbuf.dptr)->start)); + return 0; +} +#endif + +static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ +#if 0 + print_conn_key(key); + print_conn_data(dbuf); + return 0; +#else + printf("\nkey %d bytes\n", key.dsize); + print_asc(key.dptr, key.dsize); + printf("\ndata %d bytes\n", dbuf.dsize); + print_data(dbuf.dptr, dbuf.dsize); + return 0; +#endif +} + +static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ + print_asc(key.dptr, key.dsize); + printf("\n"); + return 0; +} + +static int total_bytes; + +static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) +{ + total_bytes += dbuf.dsize; + return 0; +} + +static void info_tdb(void) +{ + int count; + total_bytes = 0; + if ((count = tdb_traverse(tdb, traverse_fn, NULL) == -1)) + printf("Error = %s\n", tdb_errorstr(tdb)); + else + printf("%d records totalling %d bytes\n", count, total_bytes); +} + +static char *tdb_getline(char *prompt) +{ + static char line[1024]; + char *p; + fputs(prompt, stdout); + line[0] = 0; + p = fgets(line, sizeof(line)-1, stdin); + if (p) p = strchr(p, '\n'); + if (p) *p = 0; + return p?line:NULL; +} + +static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, + void *state) +{ + return tdb_delete(the_tdb, key); +} + +static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey) +{ + TDB_DATA dbuf; + *pkey = tdb_firstkey(the_tdb); + + dbuf = tdb_fetch(the_tdb, *pkey); + if (!dbuf.dptr) terror("fetch failed"); + else { + /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */ + print_rec(the_tdb, *pkey, dbuf, NULL); + } +} + +static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey) +{ + TDB_DATA dbuf; + *pkey = tdb_nextkey(the_tdb, *pkey); + + dbuf = tdb_fetch(the_tdb, *pkey); + if (!dbuf.dptr) + terror("fetch failed"); + else + /* printf("%s : %*.*s\n", k, (int)dbuf.dsize, (int)dbuf.dsize, dbuf.dptr); */ + print_rec(the_tdb, *pkey, dbuf, NULL); +} + +int main(int argc, char *argv[]) +{ + int bIterate = 0; + char *line; + char *tok; + TDB_DATA iterate_kbuf; + + if (argv[1]) { + static char tmp[1024]; + sprintf(tmp, "open %s", argv[1]); + tok=strtok(tmp," "); + open_tdb(); + } + + while ((line = tdb_getline("tdb> "))) { + + /* Shell command */ + + if (line[0] == '!') { + system(line + 1); + continue; + } + + if ((tok = strtok(line," "))==NULL) { + if (bIterate) + next_record(tdb, &iterate_kbuf); + continue; + } + if (strcmp(tok,"create") == 0) { + bIterate = 0; + create_tdb(); + continue; + } else if (strcmp(tok,"open") == 0) { + open_tdb(); + continue; + } else if ((strcmp(tok, "q") == 0) || + (strcmp(tok, "quit") == 0)) { + break; + } + + /* all the rest require a open database */ + if (!tdb) { + bIterate = 0; + terror("database not open"); + help(); + continue; + } + + if (strcmp(tok,"insert") == 0) { + bIterate = 0; + insert_tdb(); + } else if (strcmp(tok,"store") == 0) { + bIterate = 0; + store_tdb(); + } else if (strcmp(tok,"show") == 0) { + bIterate = 0; + show_tdb(); + } else if (strcmp(tok,"erase") == 0) { + bIterate = 0; + tdb_traverse(tdb, do_delete_fn, NULL); + } else if (strcmp(tok,"delete") == 0) { + bIterate = 0; + delete_tdb(); + } else if (strcmp(tok,"dump") == 0) { + bIterate = 0; + tdb_traverse(tdb, print_rec, NULL); + } else if (strcmp(tok,"list") == 0) { + tdb_dump_all(tdb); + } else if (strcmp(tok, "free") == 0) { + tdb_printfreelist(tdb); + } else if (strcmp(tok,"info") == 0) { + info_tdb(); + } else if ( (strcmp(tok, "1") == 0) || + (strcmp(tok, "first") == 0)) { + bIterate = 1; + first_record(tdb, &iterate_kbuf); + } else if ((strcmp(tok, "n") == 0) || + (strcmp(tok, "next") == 0)) { + next_record(tdb, &iterate_kbuf); + } else if ((strcmp(tok, "keys") == 0)) { + bIterate = 0; + tdb_traverse(tdb, print_key, NULL); + } else { + help(); + } + } + + if (tdb) tdb_close(tdb); + + return 0; +} diff --git a/source4/utils/tdb/tdbtorture.c b/source4/utils/tdb/tdbtorture.c new file mode 100644 index 0000000000..e27bbff990 --- /dev/null +++ b/source4/utils/tdb/tdbtorture.c @@ -0,0 +1,226 @@ +#include <stdlib.h> +#include <time.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <stdarg.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/wait.h> +#include "tdb.h" + +/* this tests tdb by doing lots of ops from several simultaneous + writers - that stresses the locking code. Build with TDB_DEBUG=1 + for best effect */ + + + +#define REOPEN_PROB 30 +#define DELETE_PROB 8 +#define STORE_PROB 4 +#define APPEND_PROB 6 +#define LOCKSTORE_PROB 0 +#define TRAVERSE_PROB 20 +#define CULL_PROB 100 +#define KEYLEN 3 +#define DATALEN 100 +#define LOCKLEN 20 + +static TDB_CONTEXT *db; + +static void tdb_log(TDB_CONTEXT *tdb, int level, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + vfprintf(stdout, format, ap); + va_end(ap); + fflush(stdout); +#if 0 + { + char *ptr; + asprintf(&ptr,"xterm -e gdb /proc/%d/exe %d", getpid(), getpid()); + system(ptr); + free(ptr); + } +#endif +} + +static void fatal(char *why) +{ + perror(why); + exit(1); +} + +static char *randbuf(int len) +{ + char *buf; + int i; + buf = (char *)malloc(len+1); + + for (i=0;i<len;i++) { + buf[i] = 'a' + (rand() % 26); + } + buf[i] = 0; + return buf; +} + +static int cull_traverse(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, + void *state) +{ + if (random() % CULL_PROB == 0) { + tdb_delete(tdb, key); + } + return 0; +} + +static void addrec_db(void) +{ + int klen, dlen, slen; + char *k, *d, *s; + TDB_DATA key, data, lockkey; + + klen = 1 + (rand() % KEYLEN); + dlen = 1 + (rand() % DATALEN); + slen = 1 + (rand() % LOCKLEN); + + k = randbuf(klen); + d = randbuf(dlen); + s = randbuf(slen); + + key.dptr = k; + key.dsize = klen+1; + + data.dptr = d; + data.dsize = dlen+1; + + lockkey.dptr = s; + lockkey.dsize = slen+1; + +#if REOPEN_PROB + if (random() % REOPEN_PROB == 0) { + tdb_reopen_all(); + goto next; + } +#endif + +#if DELETE_PROB + if (random() % DELETE_PROB == 0) { + tdb_delete(db, key); + goto next; + } +#endif + +#if STORE_PROB + if (random() % STORE_PROB == 0) { + if (tdb_store(db, key, data, TDB_REPLACE) != 0) { + fatal("tdb_store failed"); + } + goto next; + } +#endif + +#if APPEND_PROB + if (random() % APPEND_PROB == 0) { + if (tdb_append(db, key, data) != 0) { + fatal("tdb_append failed"); + } + goto next; + } +#endif + +#if LOCKSTORE_PROB + if (random() % LOCKSTORE_PROB == 0) { + tdb_chainlock(db, lockkey); + data = tdb_fetch(db, key); + if (tdb_store(db, key, data, TDB_REPLACE) != 0) { + fatal("tdb_store failed"); + } + if (data.dptr) free(data.dptr); + tdb_chainunlock(db, lockkey); + goto next; + } +#endif + +#if TRAVERSE_PROB + if (random() % TRAVERSE_PROB == 0) { + tdb_traverse(db, cull_traverse, NULL); + goto next; + } +#endif + + data = tdb_fetch(db, key); + if (data.dptr) free(data.dptr); + +next: + free(k); + free(d); + free(s); +} + +static int traverse_fn(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA dbuf, + void *state) +{ + tdb_delete(tdb, key); + return 0; +} + +#ifndef NPROC +#define NPROC 6 +#endif + +#ifndef NLOOPS +#define NLOOPS 200000 +#endif + +int main(int argc, char *argv[]) +{ + int i, seed=0; + int loops = NLOOPS; + pid_t pids[NPROC]; + + pids[0] = getpid(); + + for (i=0;i<NPROC-1;i++) { + if ((pids[i+1]=fork()) == 0) break; + } + + db = tdb_open("torture.tdb", 2, TDB_CLEAR_IF_FIRST, + O_RDWR | O_CREAT, 0600); + if (!db) { + fatal("db open failed"); + } + tdb_logging_function(db, tdb_log); + + srand(seed + getpid()); + srandom(seed + getpid() + time(NULL)); + for (i=0;i<loops;i++) addrec_db(); + + tdb_traverse(db, NULL, NULL); + tdb_traverse(db, traverse_fn, NULL); + tdb_traverse(db, traverse_fn, NULL); + + tdb_close(db); + + if (getpid() == pids[0]) { + for (i=0;i<NPROC-1;i++) { + int status; + if (waitpid(pids[i+1], &status, 0) != pids[i+1]) { + printf("failed to wait for %d\n", + (int)pids[i+1]); + exit(1); + } + if (WEXITSTATUS(status) != 0) { + printf("child %d exited with status %d\n", + (int)pids[i+1], WEXITSTATUS(status)); + exit(1); + } + } + printf("OK\n"); + } + + return 0; +} diff --git a/source4/utils/testparm.c b/source4/utils/testparm.c new file mode 100644 index 0000000000..b68deaaa5d --- /dev/null +++ b/source4/utils/testparm.c @@ -0,0 +1,338 @@ +/* + Unix SMB/CIFS implementation. + Test validity of smb.conf + Copyright (C) Karl Auer 1993, 1994-1998 + + Extensively modified by Andrew Tridgell, 1995 + Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. +*/ + +/* + * Testbed for loadparm.c/params.c + * + * This module simply loads a specified configuration file and + * if successful, dumps it's contents to stdout. Note that the + * operation is performed with DEBUGLEVEL at 3. + * + * Useful for a quick 'syntax check' of a configuration file. + * + */ + +#include "includes.h" + +extern BOOL AllowDebugChange; + +/*********************************************** + Here we do a set of 'hard coded' checks for bad + configuration settings. +************************************************/ + +static int do_global_checks(void) +{ + int ret = 0; + SMB_STRUCT_STAT st; + + if (lp_security() >= SEC_DOMAIN && !lp_encrypted_passwords()) { + printf("ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n"); + ret = 1; + } + + if (lp_wins_support() && lp_wins_server_list()) { + printf("ERROR: both 'wins support = true' and 'wins server = <server list>' \ +cannot be set in the smb.conf file. nmbd will abort with this setting.\n"); + ret = 1; + } + + if (!directory_exist(lp_lockdir(), &st)) { + printf("ERROR: lock directory %s does not exist\n", + lp_lockdir()); + ret = 1; + } else if ((st.st_mode & 0777) != 0755) { + printf("WARNING: lock directory %s should have permissions 0755 for browsing to work\n", + lp_lockdir()); + ret = 1; + } + + if (!directory_exist(lp_piddir(), &st)) { + printf("ERROR: pid directory %s does not exist\n", + lp_piddir()); + ret = 1; + } + + /* + * Password server sanity checks. + */ + + if((lp_security() == SEC_SERVER || lp_security() >= SEC_DOMAIN) && !lp_passwordserver()) { + pstring sec_setting; + if(lp_security() == SEC_SERVER) + pstrcpy(sec_setting, "server"); + else if(lp_security() == SEC_DOMAIN) + pstrcpy(sec_setting, "domain"); + + printf("ERROR: The setting 'security=%s' requires the 'password server' parameter be set \ +to a valid password server.\n", sec_setting ); + ret = 1; + } + + + /* + * Check 'hosts equiv' and 'use rhosts' compatibility with 'hostname lookup' value. + */ + + if(*lp_hosts_equiv() && !lp_hostname_lookups()) { + printf("ERROR: The setting 'hosts equiv = %s' requires that 'hostname lookups = yes'.\n", lp_hosts_equiv()); + ret = 1; + } + + /* + * Password chat sanity checks. + */ + + if(lp_security() == SEC_USER && lp_unix_password_sync()) { + + /* + * Check that we have a valid lp_passwd_program() if not using pam. + */ + +#ifdef WITH_PAM + if (!lp_pam_password_change()) { +#endif + + if(lp_passwd_program() == NULL) { + printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd program' \ +parameter.\n" ); + ret = 1; + } else { + pstring passwd_prog; + pstring truncated_prog; + const char *p; + + pstrcpy( passwd_prog, lp_passwd_program()); + p = passwd_prog; + *truncated_prog = '\0'; + next_token(&p, truncated_prog, NULL, sizeof(pstring)); + + if(access(truncated_prog, F_OK) == -1) { + printf("ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \ +cannot be executed (error was %s).\n", truncated_prog, strerror(errno) ); + ret = 1; + } + } + +#ifdef WITH_PAM + } +#endif + + if(lp_passwd_chat() == NULL) { + printf("ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \ +parameter.\n"); + ret = 1; + } + + /* + * Check that we have a valid script and that it hasn't + * been written to expect the old password. + */ + + if(lp_encrypted_passwords()) { + if(strstr( lp_passwd_chat(), "%o")!=NULL) { + printf("ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \ +via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() ); + ret = 1; + } + } + } + + if (strlen(lp_winbind_separator()) != 1) { + printf("ERROR: the 'winbind separator' parameter must be a single character.\n"); + ret = 1; + } + + if (*lp_winbind_separator() == '+') { + printf("'winbind separator = +' might cause problems with group membership.\n"); + } + + if (lp_algorithmic_rid_base() < BASE_RID) { + /* Try to prevent admin foot-shooting, we can't put algorithmic + rids below 1000, that's the 'well known RIDs' on NT */ + printf("'algorithmic rid base' must be equal to or above %lu\n", BASE_RID); + } + + if (lp_algorithmic_rid_base() & 1) { + printf("'algorithmic rid base' must be even.\n"); + } + +#ifndef HAVE_DLOPEN + if (lp_preload_modules()) { + printf("WARNING: 'preload modules = ' set while loading plugins not supported.\n"); + } +#endif + + return ret; +} + +int main(int argc, const char *argv[]) +{ + extern char *optarg; + extern int optind; + const char *config_file = dyn_CONFIGFILE; + int s; + static BOOL silent_mode = False; + int ret = 0; + int opt; + poptContext pc; + static const char *term_code = ""; + static char *new_local_machine = NULL; + const char *cname; + const char *caddr; + static int show_defaults; + + struct poptOption long_options[] = { + POPT_AUTOHELP + {"suppress-prompt", 's', POPT_ARG_VAL, &silent_mode, 1, "Suppress prompt for enter"}, + {"verbose", 'v', POPT_ARG_NONE, &show_defaults, 1, "Show default options too"}, + {"server", 'L',POPT_ARG_STRING, &new_local_machine, 0, "Set %%L macro to servername\n"}, + {"encoding", 't', POPT_ARG_STRING, &term_code, 0, "Print parameters with encoding"}, + {NULL, 0, POPT_ARG_INCLUDE_TABLE, popt_common_version}, + {0,0,0,0} + }; + + pc = poptGetContext(NULL, argc, argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]"); + + while((opt = poptGetNextOpt(pc)) != -1); + + setup_logging(poptGetArg(pc), True); + + if (poptPeekArg(pc)) + config_file = poptGetArg(pc); + + cname = poptGetArg(pc); + caddr = poptGetArg(pc); + + if (new_local_machine) { + set_local_machine_name(new_local_machine); + } + + dbf = x_stdout; + DEBUGLEVEL = 2; + AllowDebugChange = False; + + printf("Load smb config files from %s\n",config_file); + + if (!lp_load(config_file,False,True,False)) { + printf("Error loading services.\n"); + return(1); + } + + printf("Loaded services file OK.\n"); + + ret = do_global_checks(); + + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) + if (strlen(lp_servicename(s)) > 8) { + printf("WARNING: You have some share names that are longer than 8 chars\n"); + printf("These may give errors while browsing or may not be accessible\nto some older clients\n"); + break; + } + } + + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) { + const char **deny_list = lp_hostsdeny(s); + const char **allow_list = lp_hostsallow(s); + int i; + if(deny_list) { + for (i=0; deny_list[i]; i++) { + char *hasstar = strchr_m(deny_list[i], '*'); + char *hasquery = strchr_m(deny_list[i], '?'); + if(hasstar || hasquery) { + printf("Invalid character %c in hosts deny list (%s) for service %s.\n", + hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(s) ); + } + } + } + + if(allow_list) { + for (i=0; allow_list[i]; i++) { + char *hasstar = strchr_m(allow_list[i], '*'); + char *hasquery = strchr_m(allow_list[i], '?'); + if(hasstar || hasquery) { + printf("Invalid character %c in hosts allow list (%s) for service %s.\n", + hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(s) ); + } + } + } + + if(lp_level2_oplocks(s) && !lp_oplocks(s)) { + printf("Invalid combination of parameters for service %s. \ + Level II oplocks can only be set if oplocks are also set.\n", + lp_servicename(s) ); + } + } + } + + + if (!silent_mode) { + printf("Server role: "); + switch(lp_server_role()) { + case ROLE_STANDALONE: + printf("ROLE_STANDALONE\n"); + break; + case ROLE_DOMAIN_MEMBER: + printf("ROLE_DOMAIN_MEMBER\n"); + break; + case ROLE_DOMAIN_BDC: + printf("ROLE_DOMAIN_BDC\n"); + break; + case ROLE_DOMAIN_PDC: + printf("ROLE_DOMAIN_PDC\n"); + break; + default: + printf("Unknown -- internal error?\n"); + break; + } + } + + if (!cname) { + if (!silent_mode) { + printf("Press enter to see a dump of your service definitions\n"); + fflush(stdout); + getc(stdin); + } + lp_dump(stdout, show_defaults, lp_numservices()); + } + + if(cname && caddr){ + /* this is totally ugly, a real `quick' hack */ + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) { + if (allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) { + printf("Allow connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(s)); + } else { + printf("Deny connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(s)); + } + } + } + } + return(ret); +} diff --git a/source4/utils/testprns.c b/source4/utils/testprns.c new file mode 100644 index 0000000000..7e52b86afb --- /dev/null +++ b/source4/utils/testprns.c @@ -0,0 +1,61 @@ +/* + Unix SMB/CIFS implementation. + test printer setup + Copyright (C) Karl Auer 1993, 1994-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. +*/ + +/* + * Testbed for pcap.c + * + * This module simply checks a given printer name against the compiled-in + * printcap file. + * + * The operation is performed with DEBUGLEVEL at 3. + * + * Useful for a quick check of a printcap file. + * + */ + +#include "includes.h" + +int main(int argc, char *argv[]) +{ + const char *pszTemp; + + setup_logging(argv[0],True); + + if (argc < 2 || argc > 3) + printf("Usage: testprns printername [printcapfile]\n"); + else + { + dbf = x_fopen("test.log", O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (dbf == NULL) { + printf("Unable to open logfile.\n"); + } else { + DEBUGLEVEL = 3; + pszTemp = (argc < 3) ? PRINTCAP_NAME : argv[2]; + printf("Looking for printer %s in printcap file %s\n", + argv[1], pszTemp); + if (!pcap_printername_ok(argv[1], pszTemp)) + printf("Printer name %s is not valid.\n", argv[1]); + else + printf("Printer name %s is valid.\n", argv[1]); + x_fclose(dbf); + } + } + return (0); +} |