diff options
Diffstat (limited to 'source/utils')
36 files changed, 25352 insertions, 0 deletions
diff --git a/source/utils/.cvsignore b/source/utils/.cvsignore new file mode 100644 index 00000000000..6b8749f64eb --- /dev/null +++ b/source/utils/.cvsignore @@ -0,0 +1 @@ +net_proto.h
\ No newline at end of file diff --git a/source/utils/debug2html.c b/source/utils/debug2html.c new file mode 100644 index 00000000000..f9a1f43f461 --- /dev/null +++ b/source/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/source/utils/editreg.c b/source/utils/editreg.c new file mode 100644 index 00000000000..a0cfa2bb07d --- /dev/null +++ b/source/utils/editreg.c @@ -0,0 +1,4145 @@ +/* + 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. + +(That also seems incorrect, in that the block size if a multiple of 8. +That is, the block, including the 4 byte header, is always a multiple of +8 bytes. Richard Sharpe.) + +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 self-relative 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> + +#define False 0 +#define True 1 +#define REG_KEY_LIST_SIZE 10 + +/* + * 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 SIVAL(buf, val) \ + ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\ + (((unsigned char *)(buf))[1])=(unsigned char)(((val)>>8)&0xFF),\ + (((unsigned char *)(buf))[2])=(unsigned char)(((val)>>16)&0xFF),\ + (((unsigned char *)(buf))[3])=(unsigned char)((val)>>24)) + +#define SSVAL(buf, val) \ + ((((unsigned char *)(buf))[0])=(unsigned char)((val)&0xFF),\ + (((unsigned char *)(buf))[1])=(unsigned char)((val)>>8)) + +static int verbose = 0; +static int print_security = 0; +static int full_print = 0; +static const char *def_owner_sid_str = NULL; + +/* + * 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 key_sec_desc_s KEY_SEC_DESC; + +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; + KEY_SEC_DESC *security; + unsigned int offset; /* Offset of the record in the file */ +} REG_KEY; + +/* + * The KEY_LIST struct lists sub-keys. + */ + +typedef struct key_list_s { + int key_count; + int max_keys; + 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; + int max_vals; + VAL_KEY *vals[1]; +} VAL_LIST; + +#ifndef MAXSUBAUTHS +#define MAXSUBAUTHS 15 +#endif + +typedef struct sid_s { + unsigned char ver, auths; + unsigned char auth[6]; + unsigned int sub_auths[MAXSUBAUTHS]; +} sid_t; + +typedef struct ace_struct_s { + unsigned char type, flags; + unsigned int perms; /* Perhaps a better def is in order */ + sid_t *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; + sid_t *owner, *group; + ACL *sacl, *dacl; +} SEC_DESC; + +#define SEC_DESC_NON 0 +#define SEC_DESC_RES 1 +#define SEC_DESC_OCU 2 +#define SEC_DESC_NBK 3 +typedef struct sk_struct SK_HDR; +struct key_sec_desc_s { + struct key_sec_desc_s *prev, *next; + int ref_cnt; + int state; + int offset; + SK_HDR *sk_hdr; /* This means we must keep the registry in memory */ + SEC_DESC *sec_desc; +}; + +/* + * All of the structures below actually have a four-byte length 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 off_from_first; + DWORD off_to_next; + 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 + +struct sk_struct { + WORD SK_ID; + WORD uk1; + DWORD prev_off; + DWORD next_off; + DWORD ref_cnt; + DWORD rec_size; + char sec_desc[1]; +}; + +typedef struct ace_struct { + unsigned char type; + unsigned char flags; + unsigned short length; + unsigned int perms; + sid_t 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_DELETE -1 +#define REG_TYPE_NONE 0 +#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; + +/* 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; + +/* + * This structure keeps track of the output format of the registry + */ +#define REG_OUTBLK_HDR 1 +#define REG_OUTBLK_HBIN 2 + +typedef struct hbin_blk_s { + int type, size; + struct hbin_blk_s *next; + char *data; /* The data block */ + unsigned int file_offset; /* Offset in file */ + unsigned int free_space; /* Amount of free space in block */ + unsigned int fsp_off; /* Start of free space in block */ + int complete, stored; +} HBIN_BLK; + +/* + * This structure keeps all the registry stuff in one place + */ +typedef 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; + const char *owner_sid_str; + SEC_DESC *def_sec_desc; + /* + * These next pointers point to the blocks used to contain the + * keys when we are preparing to write them to a file + */ + HBIN_BLK *blk_head, *blk_tail, *free_space; +} REGF; + +/* + * 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 + */ + +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); + +static +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); + +static +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; +} + +static +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; +} + +static +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, key_tree->name); + strcat(new_path, "\\"); + + /* + * 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; +} + +static +REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key); + +/* + * Find key by name in a list ... + * Take the first component and search for that in the list + */ +static +REG_KEY *nt_find_key_in_list_by_name(KEY_LIST *list, char *key) +{ + int i; + REG_KEY *res = NULL; + + if (!list || !key || !*key) return NULL; + + for (i = 0; i < list->key_count; i++) + if ((res = nt_find_key_by_name(list->keys[i], key))) + return res; + + return NULL; +} + +/* + * Find key by name in a tree ... We will assume absolute names here, but we + * need the root of the tree ... + */ +static +REG_KEY *nt_find_key_by_name(REG_KEY *tree, char *key) +{ + char *lname = NULL, *c1, *c2; + REG_KEY *tmp; + + if (!tree || !key || !*key) return NULL; + + lname = strdup(key); + if (!lname) return NULL; + + /* + * Make sure that the first component is correct ... + */ + c1 = lname; + c2 = strchr(c1, '\\'); + if (c2) { /* Split here ... */ + *c2 = 0; + c2++; + } + if (strcmp(c1, tree->name) != 0) goto error; + + if (c2) { + tmp = nt_find_key_in_list_by_name(tree->sub_keys, c2); + free(lname); + return tmp; + } + else { + if (lname) free(lname); + return tree; + } + error: + if (lname) free(lname); + return NULL; +} + +/* Make, delete keys */ +static +int nt_delete_val_key(VAL_KEY *val_key) +{ + + if (val_key) { + if (val_key->name) free(val_key->name); + if (val_key->data_blk) free(val_key->data_blk); + free(val_key); + }; + return 1; +} + +static +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; +} + +static +int nt_delete_reg_key(REG_KEY *key, int delete_name); + +static +int nt_delete_key_list(KEY_LIST *key_list, int delete_name) +{ + int i; + + if (key_list) { + for (i=0; i<key_list->key_count; i++) + nt_delete_reg_key(key_list->keys[i], False); + free(key_list); + } + return 1; +} + +/* + * Find the key, and if it exists, delete it ... + */ +static +int nt_delete_key_by_name(REGF *regf, char *name) +{ + REG_KEY *key; + + if (!name || !*name) return 0; + + key = nt_find_key_by_name(regf->root, name); + + if (key) { + if (key == regf->root) regf->root = NULL; + return nt_delete_reg_key(key, True); + } + + return 0; + +} + +static +int nt_delete_sid(sid_t *sid) +{ + + if (sid) free(sid); + return 1; + +} + +static +int nt_delete_ace(ACE *ace) +{ + + if (ace) { + nt_delete_sid(ace->trustee); + free(ace); + } + return 1; + +} + +static +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; +} + +static +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; +} + +static +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; +} + +static +int nt_delete_reg_key(REG_KEY *key, int delete_name) +{ + + if (key) { + if (key->name) free(key->name); + if (key->class_name) free(key->class_name); + + /* + * We will delete the owner if we are not the root and told to ... + */ + + if (key->owner && key->owner->sub_keys && delete_name) { + REG_KEY *own; + KEY_LIST *kl; + int i; + /* Find our owner, look in keylist for us and shuffle up */ + /* Perhaps should be a function */ + + own = key->owner; + kl = own->sub_keys; + + for (i=0; i < kl->key_count && kl->keys[i] != key ; i++) { + /* Just find the entry ... */ + } + + if (i == kl->key_count) { + fprintf(stderr, "Bad data structure. Key not found in key list of owner\n"); + } + else { + int j; + + /* + * Shuffle up. Works for the last one also + */ + for (j = i + 1; j < kl->key_count; j++) { + kl->keys[j - 1] = kl->keys[j]; + } + + kl->key_count--; + } + } + + if (key->sub_keys) nt_delete_key_list(key->sub_keys, False); + if (key->values) nt_delete_val_list(key->values); + if (key->security) nt_delete_key_sec_desc(key->security); + free(key); + } + return 1; +} + +/* + * Convert a string to a value ... + * FIXME: Error handling and convert this at command parse time ... + */ +static +void *str_to_val(int type, char *val, int *len) +{ + unsigned int *dwordp = NULL; + + if (!len || !val) return NULL; + + switch (type) { + case REG_TYPE_REGSZ: + *len = strlen(val); + return (void *)val; + + case REG_TYPE_DWORD: + dwordp = (unsigned int *)malloc(sizeof(unsigned int)); + if (!dwordp) return NULL; + /* Allow for ddddd and 0xhhhhh and 0ooooo */ + if (strncmp(val, "0x", 2) == 0 || strncmp(val, "0X", 2) == 0) { + sscanf(&val[2], "%X", dwordp); + } + else if (*val == '0') { + sscanf(&val[1], "%o", dwordp); + } + else { + sscanf(val, "%d", dwordp); + } + *len = sizeof(unsigned int); + return (void *)dwordp; + + /* FIXME: Implement more of these */ + + default: + return NULL; + } + + return NULL; +} + +/* + * Add a value to the key specified ... We have to parse the value some more + * based on the type to get it in the correct internal form + * An empty name will be converted to "<No Name>" before here + * Hmmm, maybe not. has_name is for that + */ +static +VAL_KEY *nt_add_reg_value(REG_KEY *key, char *name, int type, char *value) +{ + int i; + VAL_KEY *tmp = NULL; + + if (!key || !key->values || !name || !*name) return NULL; + + assert(type != REG_TYPE_DELETE); /* We never process deletes here */ + + for (i = 0; i < key->values->val_count; i++) { + if ((!key->values->vals[i]->has_name && !*name) || + (key->values->vals[i]->has_name && + strcmp(name, key->values->vals[i]->name) == 0)){ /* Change the value */ + free(key->values->vals[i]->data_blk); + key->values->vals[i]->data_blk = str_to_val(type, value, & + key->values->vals[i]->data_len); + return key->values->vals[i]; + } + } + + /* + * If we get here, the name was not found, so insert it + */ + + tmp = (VAL_KEY *)malloc(sizeof(VAL_KEY)); + if (!tmp) goto error; + + memset(tmp, 0, sizeof(VAL_KEY)); + tmp->name = strdup(name); + tmp->has_name = True; + if (!tmp->name) goto error; + tmp->data_type = type; + tmp->data_blk = str_to_val(type, value, &tmp->data_len); + + /* Now, add to val list */ + + if (key->values->val_count >= key->values->max_vals) { + /* + * Allocate some more space + */ + + if ((key->values = (VAL_LIST *)realloc(key->values, sizeof(VAL_LIST) + + key->values->val_count - 1 + + REG_KEY_LIST_SIZE))) { + key->values->max_vals += REG_KEY_LIST_SIZE; + } + else goto error; + } + + i = key->values->val_count; + key->values->val_count++; + key->values->vals[i] = tmp; + return tmp; + + error: + if (tmp) nt_delete_val_key(tmp); + return NULL; +} + +/* + * Delete a value. We return the value and let the caller deal with it. + */ +static +VAL_KEY *nt_delete_reg_value(REG_KEY *key, char *name) +{ + int i, j; + + if (!key || !key->values || !name || !*name) return NULL; + + /* FIXME: Allow empty value name */ + for (i = 0; i< key->values->val_count; i++) { + if ((!key->values->vals[i]->has_name && !*name) || + (key->values->vals[i]->has_name && + strcmp(name, key->values->vals[i]->name) == 0)) { + VAL_KEY *val; + + val = key->values->vals[i]; + + /* Shuffle down */ + for (j = i + 1; j < key->values->val_count; j++) + key->values->vals[j - 1] = key->values->vals[j]; + + key->values->val_count--; + + return val; + } + } + return NULL; +} + +/* + * Add a key to the tree ... We walk down the components matching until + * we don't find any. There must be a match on the first component ... + * We return the key structure for the final component as that is + * often where we want to add values ... + */ + +/* + * Convert a string of the form S-1-5-x[-y-z-r] to a SID + */ +static +int sid_string_to_sid(sid_t **sid, const char *sid_str) +{ + int i = 0, auth; + const char *lstr; + + *sid = (sid_t *)malloc(sizeof(sid_t)); + if (!*sid) return 0; + + memset(*sid, 0, sizeof(sid_t)); + + 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)->ver = 1; + (*sid)->auth[5] = 5; + + lstr = sid_str + 5; + + while (1) { + if (!lstr || !lstr[0] || sscanf(lstr, "-%u", &auth) == 0) { + if (i < 1) { + fprintf(stderr, "Not of form -d-d...: %s, %u\n", lstr, i); + return 0; + } + (*sid)->auths=i; + return 1; + } + + (*sid)->sub_auths[i] = auth; + i++; + lstr = strchr(lstr + 1, '-'); + } + + /*return 1; */ /* Not Reached ... */ +} + +/* + * Create an ACE + */ +static +ACE *nt_create_ace(int type, int flags, unsigned int perms, const char *sid) +{ + ACE *ace; + + ace = (ACE *)malloc(sizeof(ACE)); + if (!ace) goto error; + ace->type = type; + ace->flags = flags; + ace->perms = perms; + if (!sid_string_to_sid(&ace->trustee, sid)) + goto error; + return ace; + + error: + if (ace) nt_delete_ace(ace); + return NULL; +} + +/* + * Create a default ACL + */ +static +ACL *nt_create_default_acl(REGF *regf) +{ + ACL *acl; + + acl = (ACL *)malloc(sizeof(ACL) + 7*sizeof(ACE *)); + if (!acl) goto error; + + acl->rev = 2; + acl->refcnt = 1; + acl->num_aces = 8; + + acl->aces[0] = nt_create_ace(0x00, 0x0, 0xF003F, regf->owner_sid_str); + if (!acl->aces[0]) goto error; + acl->aces[1] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-18"); + if (!acl->aces[1]) goto error; + acl->aces[2] = nt_create_ace(0x00, 0x0, 0xF003F, "S-1-5-32-544"); + if (!acl->aces[2]) goto error; + acl->aces[3] = nt_create_ace(0x00, 0x0, 0x20019, "S-1-5-12"); + if (!acl->aces[3]) goto error; + acl->aces[4] = nt_create_ace(0x00, 0x0B, 0x10000000, regf->owner_sid_str); + if (!acl->aces[4]) goto error; + acl->aces[5] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-18"); + if (!acl->aces[5]) goto error; + acl->aces[6] = nt_create_ace(0x00, 0x0B, 0x10000000, "S-1-5-32-544"); + if (!acl->aces[6]) goto error; + acl->aces[7] = nt_create_ace(0x00, 0x0B, 0x80000000, "S-1-5-12"); + if (!acl->aces[7]) goto error; + return acl; + + error: + if (acl) nt_delete_acl(acl); + return NULL; +} + +/* + * Create a default security descriptor. We pull in things from env + * if need be + */ +static +SEC_DESC *nt_create_def_sec_desc(REGF *regf) +{ + SEC_DESC *tmp; + + tmp = (SEC_DESC *)malloc(sizeof(SEC_DESC)); + if (!tmp) return NULL; + + tmp->rev = 1; + tmp->type = 0x8004; + if (!sid_string_to_sid(&tmp->owner, "S-1-5-32-544")) goto error; + if (!sid_string_to_sid(&tmp->group, "S-1-5-18")) goto error; + tmp->sacl = NULL; + tmp->dacl = nt_create_default_acl(regf); + + return tmp; + + error: + if (tmp) nt_delete_sec_desc(tmp); + return NULL; +} + +/* + * We will implement inheritence that is based on what the parent's SEC_DESC + * says, but the Owner and Group SIDs can be overwridden from the command line + * and additional ACEs can be applied from the command line etc. + */ +static +KEY_SEC_DESC *nt_inherit_security(REG_KEY *key) +{ + + if (!key) return NULL; + return key->security; +} + +/* + * Create an initial security descriptor and init other structures, if needed + * We assume that the initial security stuff is empty ... + */ +static +KEY_SEC_DESC *nt_create_init_sec(REGF *regf) +{ + KEY_SEC_DESC *tsec = NULL; + + tsec = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC)); + if (!tsec) return NULL; + + tsec->ref_cnt = 1; + tsec->state = SEC_DESC_NBK; + tsec->offset = 0; + + tsec->sec_desc = regf->def_sec_desc; + + return tsec; +} + +/* + * Add a sub-key + */ +static +REG_KEY *nt_add_reg_key_list(REGF *regf, REG_KEY *key, char * name, int create) +{ + int i; + REG_KEY *ret = NULL, *tmp = NULL; + KEY_LIST *list; + char *lname, *c1, *c2; + + if (!key || !name || !*name) return NULL; + + list = key->sub_keys; + if (!list) { /* Create an empty list */ + + list = (KEY_LIST *)malloc(sizeof(KEY_LIST) + (REG_KEY_LIST_SIZE - 1) * sizeof(REG_KEY *)); + list->key_count = 0; + list->max_keys = REG_KEY_LIST_SIZE; + + } + + lname = strdup(name); + if (!lname) return NULL; + + c1 = lname; + c2 = strchr(c1, '\\'); + if (c2) { /* Split here ... */ + *c2 = 0; + c2++; + } + + for (i = 0; i < list->key_count; i++) { + if (strcmp(list->keys[i]->name, c1) == 0) { + ret = nt_add_reg_key_list(regf, list->keys[i], c2, create); + free(lname); + return ret; + } + } + + /* + * If we reach here we could not find the the first component + * so create it ... + */ + + if (list->key_count < list->max_keys){ + list->key_count++; + } + else { /* Create more space in the list ... */ + if (!(list = (KEY_LIST *)realloc(list, sizeof(KEY_LIST) + + (list->max_keys + REG_KEY_LIST_SIZE - 1) + * sizeof(REG_KEY *)))) + goto error; + + list->max_keys += REG_KEY_LIST_SIZE; + list->key_count++; + } + + /* + * add the new key at the new slot + * FIXME: Sort the list someday + */ + + /* + * We want to create the key, and then do the rest + */ + + tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); + + memset(tmp, 0, sizeof(REG_KEY)); + + tmp->name = strdup(c1); + if (!tmp->name) goto error; + tmp->owner = key; + tmp->type = REG_SUB_KEY; + /* + * Next, pull security from the parent, but override with + * anything passed in on the command line + */ + tmp->security = nt_inherit_security(key); + + list->keys[list->key_count - 1] = tmp; + + if (c2) { + ret = nt_add_reg_key_list(regf, key, c2, True); + } + + if (lname) free(lname); + + return ret; + + error: + if (tmp) free(tmp); + if (lname) free(lname); + return NULL; +} + +/* + * This routine only adds a key from the root down. + * It calls helper functions to handle sub-key lists and sub-keys + */ +static +REG_KEY *nt_add_reg_key(REGF *regf, char *name, int create) +{ + char *lname = NULL, *c1, *c2; + REG_KEY * tmp = NULL; + + /* + * Look until we hit the first component that does not exist, and + * then add from there. However, if the first component does not + * match and the path we are given is the root, then it must match + */ + if (!regf || !name || !*name) return NULL; + + lname = strdup(name); + if (!lname) return NULL; + + c1 = lname; + c2 = strchr(c1, '\\'); + if (c2) { /* Split here ... */ + *c2 = 0; + c2++; + } + + /* + * If the root does not exist, create it and make it equal to the + * first component ... + */ + + if (!regf->root) { + + tmp = (REG_KEY *)malloc(sizeof(REG_KEY)); + if (!tmp) goto error; + memset(tmp, 0, sizeof(REG_KEY)); + tmp->name = strdup(c1); + if (!tmp->name) goto error; + tmp->security = nt_create_init_sec(regf); + if (!tmp->security) goto error; + regf->root = tmp; + + } + else { + /* + * If we don't match, then we have to return error ... + * If we do match on this component, check the next one in the + * list, and if not found, add it ... short circuit, add all the + * way down + */ + + if (strcmp(c1, regf->root->name) != 0) + goto error; + } + + tmp = nt_add_reg_key_list(regf, regf->root, c2, True); + free(lname); + return tmp; + + error: + if (tmp) free(tmp); + if (lname) free(lname); + return NULL; +} + +/* + * 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 + */ + +/* + * 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 + +#define OFF(f) ((f) + REGF_HDR_BLKSIZ + 4) +#define LOCN(base, f) ((base) + OFF(f)) + +const VAL_STR reg_type_names[] = { + { REG_TYPE_REGSZ, "REG_SZ" }, + { REG_TYPE_EXPANDSZ, "REG_EXPAND_SZ" }, + { REG_TYPE_BIN, "REG_BIN" }, + { REG_TYPE_DWORD, "REG_DWORD" }, + { REG_TYPE_MULTISZ, "REG_MULTI_SZ" }, + { 0, NULL }, +}; + +static +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 + */ +static +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 + */ +static +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: + if (verbose) fprintf(stderr, "Len: %d\n", len); + /* FIXME. This has to be fixed. It has to be UNICODE */ + return uni_to_ascii(datap, ascii, len, ascii_max); + break; /*NOTREACHED*/ + + 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; + +} + +static +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent); + +static +int nt_set_regf_input_file(REGF *regf, char *filename) +{ + return ((regf->regfile_name = strdup(filename)) != NULL); +} + +static +int nt_set_regf_output_file(REGF *regf, char *filename) +{ + return ((regf->outfile_name = strdup(filename)) != NULL); +} + +/* Create a regf structure and init it */ + +static +REGF *nt_create_regf(void) +{ + REGF *tmp = (REGF *)malloc(sizeof(REGF)); + if (!tmp) return tmp; + memset(tmp, 0, sizeof(REGF)); + tmp->owner_sid_str = def_owner_sid_str; + 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 */ +static +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); + + nt_delete_reg_key(regf->root, False); /* 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 + */ +static +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 + */ +static +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 + */ +static +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 don't create one if not + * found + */ +static +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 + */ +static +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; + } + memset(tmp, 0, sizeof(KEY_SEC_DESC)); /* Neatly sets offset to 0 */ + 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. + */ +static +sid_t *dup_sid(sid_t *sid) +{ + sid_t *tmp = (sid_t *)malloc(sizeof(sid_t)); + 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 + */ +static +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 + */ +static +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); + if (verbose) fprintf(stdout, "ACL: refcnt: %u, rev: %u\n", tmp->refcnt, + tmp->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; +} + +static +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); + if (verbose) fprintf(stdout, "SEC_DESC Rev: %0X, Type: %0X\n", + tmp->rev, tmp->type); + if (verbose) fprintf(stdout, "SEC_DESC Owner Off: %0X\n", + IVAL(&sec_desc->owner_off)); + if (verbose) fprintf(stdout, "SEC_DESC Group Off: %0X\n", + IVAL(&sec_desc->group_off)); + if (verbose) fprintf(stdout, "SEC_DESC DACL Off: %0X\n", + IVAL(&sec_desc->dacl_off)); + tmp->owner = dup_sid((sid_t *)((char *)sec_desc + IVAL(&sec_desc->owner_off))); + if (!tmp->owner) { + free(tmp); + return NULL; + } + tmp->group = dup_sid((sid_t *)((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; +} + +static +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 future references to this structure + * We could put all this unpleasantness in a function. + */ + + if (!tmp) { + tmp = (KEY_SEC_DESC *)malloc(sizeof(KEY_SEC_DESC)); + if (!tmp) return NULL; + memset(tmp, 0, 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 + */ +static +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; + } + memset(tmp, 0, 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 or type */ + /* + * FIXME. + * Some registry files seem to have wierd fields. If top bit is set, + * but len is 0, the type seems to be the value ... + * Not sure how to handle this last type for the moment ... + */ + 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: + if (tmp) nt_delete_val_key(tmp); + return NULL; + +} + +/* + * Process a VL Header and return a list of values + */ +static +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; + tmp->max_vals = count; + + return tmp; + + error: + /* XXX: FIXME, free the partially allocated structure */ + return NULL; +} + +/* + * Process an LF Header and return a list of sub-keys + */ +static +KEY_LIST *process_lf(REGF *regf, LF_HDR *lf_hdr, int size, REG_KEY *parent) +{ + 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 (verbose) fprintf(stdout, "Key Count: %u\n", 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; + tmp->max_keys = count; + + for (i=0; i<count; i++) { + NK_HDR *nk_hdr; + + nk_off = IVAL(&lf_hdr->hr[i].nk_off); + if (verbose) fprintf(stdout, "NK Offset: %0X\n", 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), parent); + if (!tmp->keys[i]) { + goto error; + } + } + + return tmp; + + error: + if (tmp) nt_delete_key_list(tmp, False); + return NULL; +} + +/* + * This routine is passed an NK_HDR pointer and retrieves the entire tree + * from there down. It returns a REG_KEY *. + */ +static +REG_KEY *nt_get_key_tree(REGF *regf, NK_HDR *nk_hdr, int size, REG_KEY *parent) +{ + REG_KEY *tmp = NULL, *own; + int name_len, clsname_len, lf_off, val_off, val_count, sk_off, own_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; + memset(tmp, 0, 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); + if (verbose) fprintf(stdout, "Class Name Offset: %0X\n", clsnam_off); + + memset(cls_name, 0, 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. + * It will also piss off people who need Unicode/UTF-8 strings. Sorry. + * XXX: FIXME + */ + + tmp->class_name = strdup(cls_name); + if (!tmp->class_name) { + goto error; + } + + if (verbose) fprintf(stdout, " Class Name: %s\n", cls_name); + + } + + /* + * Process the owner offset ... + */ + + own_off = IVAL(&nk_hdr->own_off); + own = (REG_KEY *)LOCN(regf->base, own_off); + if (verbose) fprintf(stdout, "Owner Offset: %0X\n", own_off); + + if (verbose) fprintf(stdout, " Owner locn: %0X, Our locn: %0X\n", + (unsigned int)own, (unsigned int)nk_hdr); + + /* + * We should verify that the owner field is correct ... + * for now, we don't worry ... + */ + + tmp->owner = parent; + + /* + * If there are any values, process them here + */ + + val_count = IVAL(&nk_hdr->val_cnt); + if (verbose) fprintf(stdout, "Val Count: %d\n", val_count); + if (val_count) { + + val_off = IVAL(&nk_hdr->val_off); + vl = (VL_TYPE *)LOCN(regf->base, val_off); + if (verbose) fprintf(stdout, "Val List Offset: %0X\n", 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 (verbose) fprintf(stdout, "SK Offset: %0X\n", 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); + if (verbose) fprintf(stdout, "SubKey list offset: %0X\n", 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), tmp); + if (!tmp->sub_keys){ + goto error; + } + + } + + return tmp; + + error: + if (tmp) nt_delete_reg_key(tmp, False); + return NULL; +} + +static +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 + */ + + if (verbose) fprintf(stdout, "First Key: %0X\n", + IVAL(®f_hdr->first_key)); + + first_key = (NK_HDR *)LOCN(regf->base, IVAL(®f_hdr->first_key)); + if (verbose) fprintf(stdout, "First Key Offset: %0X\n", + IVAL(®f_hdr->first_key)); + + if (verbose) fprintf(stdout, "Data Block Size: %d\n", + IVAL(®f_hdr->dblk_size)); + + if (verbose) fprintf(stdout, "Offset to next hbin block: %0X\n", + IVAL(&hbin_hdr->off_to_next)); + + if (verbose) fprintf(stdout, "HBIN block size: %0X\n", + IVAL(&hbin_hdr->blk_size)); + + /* + * Now, get the registry tree by processing that NK recursively + */ + + regf->root = nt_get_key_tree(regf, first_key, BLK_SIZE(first_key), NULL); + + assert(regf->root != NULL); + + /* + * Unmap the registry file, as we might want to read in another + * tree etc. + */ + + if (regf->base) munmap(regf->base, regf->sbuf.st_size); + regf->base = NULL; + close(regf->fd); /* Ignore the error :-) */ + + return 1; +} + +/* + * Allocate a new hbin block, set up the header for the block etc + */ +static +HBIN_BLK *nt_create_hbin_blk(REGF *regf, int size) +{ + HBIN_BLK *tmp; + HBIN_HDR *hdr; + + if (!regf || !size) return NULL; + + /* Round size up to multiple of REGF_HDR_BLKSIZ */ + + size = (size + (REGF_HDR_BLKSIZ - 1)) & ~(REGF_HDR_BLKSIZ - 1); + + tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK)); + memset(tmp, 0, sizeof(HBIN_BLK)); + + tmp->data = malloc(size); + if (!tmp->data) goto error; + + memset(tmp->data, 0, size); /* Make it pristine */ + + tmp->size = size; + tmp->file_offset = regf->blk_tail->file_offset + regf->blk_tail->size; + + tmp->free_space = size - (sizeof(HBIN_HDR) - sizeof(HBIN_SUB_HDR)); + tmp->fsp_off = size - tmp->free_space; + + /* + * Now, build the header in the data block + */ + hdr = (HBIN_HDR *)tmp->data; + hdr->HBIN_ID = REG_HBIN_ID; + hdr->off_from_first = tmp->file_offset - REGF_HDR_BLKSIZ; + hdr->off_to_next = tmp->size; + hdr->blk_size = tmp->size; + + /* + * Now link it in + */ + + regf->blk_tail->next = tmp; + regf->blk_tail = tmp; + if (!regf->free_space) regf->free_space = tmp; + + return tmp; + error: + if (tmp) free(tmp); + return NULL; +} + +/* + * Allocate a unit of space ... and return a pointer as function param + * and the block's offset as a side effect + */ +static +void *nt_alloc_regf_space(REGF *regf, int size, unsigned int *off) +{ + int tmp = 0; + void *ret = NULL; + HBIN_BLK *blk; + + if (!regf || !size || !off) return NULL; + + assert(regf->blk_head != NULL); + + /* + * round up size to include header and then to 8-byte boundary + */ + size = (size + 4 + 7) & ~7; + + /* + * Check if there is space, if none, grab a block + */ + if (!regf->free_space) { + if (!nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) + return NULL; + } + + /* + * Now, chain down the list of blocks looking for free space + */ + + for (blk = regf->free_space; blk != NULL; blk = blk->next) { + if (blk->free_space <= size) { + tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ; + ret = blk->data + blk->fsp_off; + blk->free_space -= size; + blk->fsp_off += size; + + /* Insert the header */ + ((HBIN_SUB_HDR *)ret)->dblocksize = -size; + + /* + * Fix up the free space ptr + * If it is NULL, we fix it up next time + */ + + if (!blk->free_space) + regf->free_space = blk->next; + + *off = tmp; + return (((char *)ret)+4);/* The pointer needs to be to the data struct */ + } + } + + /* + * If we got here, we need to add another block, which might be + * larger than one block -- deal with that later + */ + if (nt_create_hbin_blk(regf, REGF_HDR_BLKSIZ)) { + blk = regf->free_space; + tmp = blk->file_offset + blk->fsp_off - REGF_HDR_BLKSIZ; + ret = blk->data + blk->fsp_off; + blk->free_space -= size; + blk->fsp_off += size; + + /* Insert the header */ + ((HBIN_SUB_HDR *)ret)->dblocksize = -size; + + /* + * Fix up the free space ptr + * If it is NULL, we fix it up next time + */ + + if (!blk->free_space) + regf->free_space = blk->next; + + *off = tmp; + return (((char *)ret) + 4);/* The pointer needs to be to the data struct */ + } + + return NULL; +} + +/* + * Compute the size of a SID stored ... + */ +static +unsigned int sid_size(sid_t *sid) +{ + unsigned int size; + + if (!sid) return 0; + + size = 8 + (sid->auths * sizeof(unsigned int)); + + return size; +} + +/* + * Compute the size of an ACE on disk from its components + */ +static +unsigned int ace_size(ACE *ace) +{ + unsigned int size; + + if (!ace) return 0; + + size = 8 + sid_size(ace->trustee); + + return size; +} + +/* + * Compute the size of an ACL from its components ... + */ +static +unsigned int acl_size(ACL *acl) +{ + unsigned int size; + int i; + + if (!acl) return 0; + + size = 8; + for (i = 0; i < acl->num_aces; i++) + size += ace_size(acl->aces[i]); + + return size; +} + +/* + * Compute the size of the sec desc as a self-relative SD + */ +static +unsigned int sec_desc_size(SEC_DESC *sd) +{ + unsigned int size; + + if (!sd) return 0; + + size = 20; + + if (sd->owner) size += sid_size(sd->owner); + if (sd->group) size += sid_size(sd->group); + if (sd->sacl) size += acl_size(sd->sacl); + if (sd->dacl) size += acl_size(sd->dacl); + + return size; +} + +/* + * Store a SID at the location provided + */ +static +int nt_store_SID(REGF *regf, sid_t *sid, unsigned char *locn) +{ + int i; + unsigned char *p = locn; + + if (!regf || !sid || !locn) return 0; + + *p = sid->ver; p++; + *p = sid->auths; p++; + + for (i=0; i < 6; i++) { + *p = sid->auth[i]; p++; + } + + for (i=0; i < sid->auths; i++) { + SIVAL(p, sid->sub_auths[i]); p+=4; + } + + return p - locn; + +} + +static +int nt_store_ace(REGF *regf, ACE *ace, unsigned char *locn) +{ + int size = 0; + REG_ACE *reg_ace = (REG_ACE *)locn; + unsigned char *p; + + if (!regf || !ace || !locn) return 0; + + reg_ace->type = ace->type; + reg_ace->flags = ace->flags; + + /* Deal with the length when we have stored the SID */ + + p = (unsigned char *)®_ace->perms; + + SIVAL(p, ace->perms); p += 4; + + size = nt_store_SID(regf, ace->trustee, p); + + size += 8; /* Size of the fixed header */ + + p = (unsigned char *)®_ace->length; + + SSVAL(p, size); + + return size; +} + +/* + * Store an ACL at the location provided + */ +static +int nt_store_acl(REGF *regf, ACL *acl, unsigned char *locn) +{ + int size = 0, i; + unsigned char *p = locn, *s; + + if (!regf || !acl || !locn) return 0; + + /* + * Now store the header and then the ACEs ... + */ + + SSVAL(p, acl->rev); + + p += 2; s = p; /* Save this for the size field */ + + p += 2; + + SIVAL(p, acl->num_aces); + + p += 4; + + for (i = 0; i < acl->num_aces; i++) { + size = nt_store_ace(regf, acl->aces[i], p); + p += size; + } + + size = s - locn; + SSVAL(s, size); + return size; +} + +/* + * Flatten and store the Sec Desc + * Windows lays out the DACL first, but since there is no SACL, it might be + * that first, then the owner, then the group SID. So, we do it that way + * too. + */ +static +unsigned int nt_store_sec_desc(REGF *regf, SEC_DESC *sd, char *locn) +{ + REG_SEC_DESC *rsd = (REG_SEC_DESC *)locn; + unsigned int size = 0, off = 0; + + if (!regf || !sd || !locn) return 0; + + /* + * Now, fill in the first two fields, then lay out the various fields + * as needed + */ + + rsd->rev = 0x01; + /* Self relative, DACL pres, owner and group not defaulted */ + rsd->type = 0x8004; + + off = 4 * sizeof(DWORD) + 4; + + if (sd->sacl){ + size = nt_store_acl(regf, sd->sacl, (char *)(locn + off)); + rsd->sacl_off = off; + } + else + rsd->sacl_off = 0; + + off += size; + + if (sd->dacl) { + rsd->dacl_off = off; + size = nt_store_acl(regf, sd->dacl, (char *)(locn + off)); + } + else { + rsd->dacl_off = 0; + } + + off += size; + + /* Now the owner and group SIDs */ + + if (sd->owner) { + rsd->owner_off = off; + size = nt_store_SID(regf, sd->owner, (char *)(locn + off)); + } + else { + rsd->owner_off = 0; + } + + off += size; + + if (sd->group) { + rsd->group_off = off; + size = nt_store_SID(regf, sd->group, (char *)(locn + off)); + } + else { + rsd->group_off = 0; + } + + off += size; + + return size; +} + +/* + * Store the security information + * + * If it has already been stored, just get its offset from record + * otherwise, store it and record its offset + */ +static +unsigned int nt_store_security(REGF *regf, KEY_SEC_DESC *sec) +{ + int size = 0; + unsigned int sk_off; + SK_HDR *sk_hdr; + + if (sec->offset) return sec->offset; + + /* + * OK, we don't have this one in the file yet. We must compute the + * size taken by the security descriptor as a self-relative SD, which + * means making one pass over each structure and figuring it out + */ + + size = sec_desc_size(sec->sec_desc); + + /* Allocate that much space */ + + sk_hdr = nt_alloc_regf_space(regf, size, &sk_off); + sec->sk_hdr = sk_hdr; + + if (!sk_hdr) return 0; + + /* Now, lay out the sec_desc in the space provided */ + + sk_hdr->SK_ID = REG_SK_ID; + + /* + * We can't deal with the next and prev offset in the SK_HDRs until the + * whole tree has been stored, then we can go and deal with them + */ + + sk_hdr->ref_cnt = sec->ref_cnt; + sk_hdr->rec_size = size; /* Is this correct */ + + /* Now, lay out the sec_desc */ + + if (!nt_store_sec_desc(regf, sec->sec_desc, (char *)&sk_hdr->sec_desc)) + return 0; + + return sk_off; + +} + +/* + * Store a VAL LIST + */ +static +int nt_store_val_list(REGF *regf, VAL_LIST * values) +{ + + return 0; +} + +/* + * Store a KEY in the file ... + * + * We store this depth first, and defer storing the lf struct until + * all the sub-keys have been stored. + * + * We store the NK hdr, any SK header, class name, and VK structure, then + * recurse down the LF structures ... + * + * We return the offset of the NK struct + * FIXME, FIXME, FIXME: Convert to using SIVAL and SSVAL ... + */ +static +int nt_store_reg_key(REGF *regf, REG_KEY *key) +{ + NK_HDR *nk_hdr; + unsigned int nk_off, sk_off, size; + + if (!regf || !key) return 0; + + size = sizeof(NK_HDR) + strlen(key->name) - 1; + nk_hdr = nt_alloc_regf_space(regf, size, &nk_off); + if (!nk_hdr) goto error; + + key->offset = nk_off; /* We will need this later */ + + /* + * Now fill in each field etc ... + */ + + nk_hdr->NK_ID = REG_NK_ID; + if (key->type == REG_ROOT_KEY) + nk_hdr->type = 0x2C; + else + nk_hdr->type = 0x20; + + /* FIXME: Fill in the time of last update */ + + if (key->type != REG_ROOT_KEY) + nk_hdr->own_off = key->owner->offset; + + if (key->sub_keys) + nk_hdr->subk_num = key->sub_keys->key_count; + + /* + * Now, process the Sec Desc and then store its offset + */ + + sk_off = nt_store_security(regf, key->security); + nk_hdr->sk_off = sk_off; + + /* + * Then, store the val list and store its offset + */ + if (key->values) { + nk_hdr->val_cnt = key->values->val_count; + nk_hdr->val_off = nt_store_val_list(regf, key->values); + } + else { + nk_hdr->val_off = -1; + nk_hdr->val_cnt = 0; + } + + /* + * Finally, store the subkeys, and their offsets + */ + + error: + return 0; +} + +/* + * Store the registry header ... + * We actually create the registry header block and link it to the chain + * of output blocks. + */ +static +REGF_HDR *nt_get_reg_header(REGF *regf) +{ + HBIN_BLK *tmp = NULL; + + tmp = (HBIN_BLK *)malloc(sizeof(HBIN_BLK)); + if (!tmp) return 0; + + memset(tmp, 0, sizeof(HBIN_BLK)); + tmp->type = REG_OUTBLK_HDR; + tmp->size = REGF_HDR_BLKSIZ; + tmp->data = malloc(REGF_HDR_BLKSIZ); + if (!tmp->data) goto error; + + memset(tmp->data, 0, REGF_HDR_BLKSIZ); /* Make it pristine, unlike Windows */ + regf->blk_head = regf->blk_tail = tmp; + + return (REGF_HDR *)tmp->data; + + error: + if (tmp) free(tmp); + return NULL; +} + +/* + * Store the registry in the output file + * We write out the header and then each of the keys etc into the file + * We have to flatten the data structure ... + * + * The structures are stored in a depth-first fashion, with all records + * aligned on 8-byte boundaries, with sub-keys and values layed down before + * the lists that contain them. SK records are layed down first, however. + * The lf fields are layed down after all sub-keys have been layed down, it + * seems, including the whole tree associated with each sub-key. + */ +static +int nt_store_registry(REGF *regf) +{ + REGF_HDR *reg; + int fkey, fd; + + /* + * Get a header ... and partially fill it in ... + */ + reg = nt_get_reg_header(regf); + + /* + * Store the first key, which will store the whole thing + */ + fkey = nt_store_reg_key(regf, regf->root); + + /* + * At this point we have the registry as a series of blocks, so + * run down that series of blocks and save them ... + */ + + if (!regf->outfile_name) { + fprintf(stderr, "Cannot write file without a name!\n"); + return 0; + } + + if ((fd = open(regf->outfile_name, O_WRONLY, 0666)) < 0) { + fprintf(stderr, "Unable to create file %s: %s\n", regf->outfile_name, + strerror(errno)); + return 0; + } + + return 1; +} + +/* + * Routines to parse a REGEDIT4 file + * + * The file consists of: + * + * REGEDIT4 + * \[[-]key-path\]\n + * <value-spec>* + * + * Format: + * [cmd:]name=type:value + * + * cmd = a|d|c|add|delete|change|as|ds|cs + * + * 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 + +#define FMT_STRING_REGEDIT4 "REGEDIT4" +#define FMT_STRING_EDITREG1_0 "EDITREG1.0" + +#define CMD_NONE 0 +#define CMD_ADD_KEY 1 +#define CMD_DEL_KEY 2 + +#define CMD_KEY 1 +#define CMD_VAL 2 + +typedef struct val_spec_list { + struct val_spec_list *next; + char *name; + int type; + char *val; /* Kept as a char string, really? */ +} VAL_SPEC_LIST; + +typedef struct command_s { + int cmd; + char *key; + int val_count; + VAL_SPEC_LIST *val_spec_list, *val_spec_last; +} CMD; + +typedef struct cmd_line { + int len, line_len; + char *line; +} CMD_LINE; + +static +void free_val_spec_list(VAL_SPEC_LIST *vl) +{ + if (!vl) return; + if (vl->name) free(vl->name); + if (vl->val) free(vl->val); + free(vl); + +} + +/* + * Some routines to handle lines of info in the command files + */ +static +void skip_to_eol(int fd) +{ + int rc; + char ch = 0; + + while ((rc = read(fd, &ch, 1)) == 1) { + if (ch == 0x0A) return; + } + if (rc < 0) { + fprintf(stderr, "Could not read file descriptor: %d, %s\n", + fd, strerror(errno)); + exit(1); + } +} + +static +void free_cmd(CMD *cmd) +{ + if (!cmd) return; + + while (cmd->val_spec_list) { + VAL_SPEC_LIST *tmp; + + tmp = cmd->val_spec_list; + cmd->val_spec_list = tmp->next; + free(tmp); + } + + free(cmd); + +} + +static +void free_cmd_line(CMD_LINE *cmd_line) +{ + if (cmd_line) { + if (cmd_line->line) free(cmd_line->line); + free(cmd_line); + } +} + +static +void print_line(struct cmd_line *cl) +{ + char *pl; + + if (!cl) return; + + if ((pl = malloc(cl->line_len + 1)) == NULL) { + fprintf(stderr, "Unable to allocate space to print line: %s\n", + strerror(errno)); + exit(1); + } + + strncpy(pl, cl->line, cl->line_len); + pl[cl->line_len] = 0; + + fprintf(stdout, "%s\n", pl); + free(pl); +} + +#define INIT_ALLOC 10 + +/* + * Read a line from the input file. + * NULL returned when EOF and no chars read + * Otherwise we return a cmd_line * + * Exit if other errors + */ +static +struct cmd_line *get_cmd_line(int fd) +{ + struct cmd_line *cl = (CMD_LINE *)malloc(sizeof(CMD_LINE)); + int i = 0, rc; + unsigned char ch; + + if (!cl) { + fprintf(stderr, "Unable to allocate structure for command line: %s\n", + strerror(errno)); + exit(1); + } + + cl->len = INIT_ALLOC; + + /* + * Allocate some space for the line. We extend later if needed. + */ + + if ((cl->line = (char *)malloc(INIT_ALLOC)) == NULL) { + fprintf(stderr, "Unable to allocate initial space for line: %s\n", + strerror(errno)); + exit(1); + } + + /* + * Now read in the chars to EOL. Don't store the EOL in the + * line. What about CR? + */ + + while ((rc = read(fd, &ch, 1)) == 1 && ch != '\n') { + if (ch == '\r') continue; /* skip CR */ + if (i == cl->len) { + /* + * Allocate some more memory + */ + if ((cl->line = realloc(cl->line, cl->len + INIT_ALLOC)) == NULL) { + fprintf(stderr, "Unable to realloc space for line: %s\n", + strerror(errno)); + exit(1); + } + cl->len += INIT_ALLOC; + } + cl->line[i] = ch; + i++; + } + + /* read 0 and we were at loc'n 0, return NULL */ + if (rc == 0 && i == 0) { + free_cmd_line(cl); + return NULL; + } + + cl->line_len = i; + + return cl; + +} + +/* + * parse_value: parse out a value. We pull it apart as: + * + * <value> ::= <value-name>=<type>:<value-string> + * + * <value-name> ::= char-string-without-spaces | '"' char-string '"' + * + * If it parsed OK, return the <value-name> as a string, and the + * value type and value-string in parameters. + * + * The value name can be empty. There can only be one empty name in + * a list of values. A value of - removes the value entirely. + */ +static +char *dup_str(char *s, int len) +{ + char *nstr; + nstr = (char *)malloc(len + 1); + if (nstr) { + memcpy(nstr, s, len); + nstr[len] = 0; + } + return nstr; +} + +static +char *parse_name(char *nstr) +{ + int len = 0, start = 0; + if (!nstr) return NULL; + + len = strlen(nstr); + + while (len && nstr[len - 1] == ' ') len--; + + nstr[len] = 0; /* Trim any spaces ... if there were none, doesn't matter */ + + /* + * Beginning and end should be '"' or neither should be so + */ + if ((nstr[0] == '"' && nstr[len - 1] != '"') || + (nstr[0] != '"' && nstr[len - 1] == '"')) + return NULL; + + if (nstr[0] == '"') { + start = 1; + len -= 2; + } + + return dup_str(&nstr[start], len); +} + +static +int parse_value_type(char *tstr) +{ + int len = strlen(tstr); + + while (len && tstr[len - 1] == ' ') len--; + tstr[len] = 0; + + if (strcmp(tstr, "REG_DWORD") == 0) + return REG_TYPE_DWORD; + else if (strcmp(tstr, "dword") == 0) + return REG_TYPE_DWORD; + else if (strcmp(tstr, "REG_EXPAND_SZ") == 0) + return REG_TYPE_EXPANDSZ; + else if (strcmp(tstr, "REG_BIN") == 0) + return REG_TYPE_BIN; + else if (strcmp(tstr, "REG_SZ") == 0) + return REG_TYPE_REGSZ; + else if (strcmp(tstr, "REG_MULTI_SZ") == 0) + return REG_TYPE_MULTISZ; + else if (strcmp(tstr, "-") == 0) + return REG_TYPE_DELETE; + + return 0; +} + +static +char *parse_val_str(char *vstr) +{ + + return dup_str(vstr, strlen(vstr)); + +} + +static +char *parse_value(struct cmd_line *cl, int *vtype, char **val) +{ + char *p1 = NULL, *p2 = NULL, *nstr = NULL, *tstr = NULL, *vstr = NULL; + + if (!cl || !vtype || !val) return NULL; + if (!cl->line_len) return NULL; + + p1 = dup_str(cl->line, cl->line_len); + /* FIXME: Better return codes etc ... */ + if (!p1) return NULL; + p2 = strchr(p1, '='); + if (!p2) return NULL; + + *p2 = 0; p2++; /* Split into two strings at p2 */ + + /* Now, parse the name ... */ + + nstr = parse_name(p1); + if (!nstr) goto error; + + /* Now, split the remainder and parse on type and val ... */ + + tstr = p2; + while (*tstr == ' ') tstr++; /* Skip leading white space */ + p2 = strchr(p2, ':'); + + if (p2) { + *p2 = 0; p2++; /* split on the : */ + } + + *vtype = parse_value_type(tstr); + + if (!vtype) goto error; + + if (!p2 || !*p2) return nstr; + + /* Now, parse the value string. It should return a newly malloc'd string */ + + while (*p2 == ' ') p2++; /* Skip leading space */ + vstr = parse_val_str(p2); + + if (!vstr) goto error; + + *val = vstr; + + return nstr; + + error: + if (p1) free(p1); + if (nstr) free(nstr); + if (vstr) free(vstr); + return NULL; +} + +/* + * Parse out a key. Look for a correctly formatted key [...] + * and whether it is a delete or add? A delete is signalled + * by a - in front of the key. + * Assumes that there are no leading and trailing spaces + */ + +static +char *parse_key(struct cmd_line *cl, int *cmd) +{ + int start = 1; + char *tmp; + + if (cl->line[0] != '[' || + cl->line[cl->line_len - 1] != ']') return NULL; + if (cl->line_len == 2) return NULL; + *cmd = CMD_ADD_KEY; + if (cl->line[1] == '-') { + if (cl->line_len == 3) return NULL; + start = 2; + *cmd = CMD_DEL_KEY; + } + tmp = malloc(cl->line_len - 1 - start + 1); + if (!tmp) return tmp; /* Bail out on no mem ... FIXME */ + strncpy(tmp, &cl->line[start], cl->line_len - 1 - start); + tmp[cl->line_len - 1 - start] = 0; + return tmp; +} + +/* + * Parse a line to determine if we have a key or a value + * We only check for key or val ... + */ + +static +int parse_line(struct cmd_line *cl) +{ + + if (!cl || cl->len == 0) return 0; + + if (cl->line[0] == '[') /* No further checking for now */ + return CMD_KEY; + else + return CMD_VAL; +} + +/* + * 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 + */ +static +int regedit4_file_type(int fd) +{ + int cur_ofs = 0; + char desc[9]; + + 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); /* FIXME */ + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + if (read(fd, desc, 8) < 8) { + fprintf(stderr, "Unable to read command file format\n"); + exit(2); /* FIXME */ + } + + desc[8] = 0; + + if (strcmp(desc, FMT_STRING_REGEDIT4) == 0) { + if (cur_ofs) { + lseek(fd, cur_ofs, SEEK_SET); + } + else { + skip_to_eol(fd); + } + return FMT_REGEDIT4; + } + + return FMT_UNREC; +} + +/* + * Run though the data in the line and strip anything after a comment + * char. + */ +static +void strip_comment(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = 0; i < cl->line_len; i++) { + if (cl->line[i] == ';') { + cl->line_len = i; + return; + } + } +} + +/* + * trim leading space + */ + +static +void trim_leading_spaces(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = 0; i < cl->line_len; i++) { + if (cl->line[i] != ' '){ + if (i) memcpy(cl->line, &cl->line[i], cl->line_len - i); + return; + } + } +} + +/* + * trim trailing spaces + */ +static +void trim_trailing_spaces(struct cmd_line *cl) +{ + int i; + + if (!cl) return; + + for (i = cl->line_len; i == 0; i--) { + if (cl->line[i-1] != ' ' && + cl->line[i-1] != '\t') { + cl->line_len = i; + } + } +} + +/* + * Get a command ... This consists of possibly multiple lines: + * [key] + * values* + * possibly Empty line + * + * value ::= <value-name>=<value-type>':'<value-string> + * <value-name> is some path, possibly enclosed in quotes ... + * We alctually look for the next key to terminate a previous key + * if <value-type> == '-', then it is a delete type. + */ +static +CMD *regedit4_get_cmd(int fd) +{ + struct command_s *cmd = NULL; + struct cmd_line *cl = NULL; + struct val_spec_list *vl = NULL; + + if ((cmd = (struct command_s *)malloc(sizeof(struct command_s))) == NULL) { + fprintf(stderr, "Unable to malloc space for command: %s\n", + strerror(errno)); + exit(1); + } + + cmd->cmd = CMD_NONE; + cmd->key = NULL; + cmd->val_count = 0; + cmd->val_spec_list = cmd->val_spec_last = NULL; + while ((cl = get_cmd_line(fd))) { + + /* + * If it is an empty command line, and we already have a key + * then exit from here ... FIXME: Clean up the parser + */ + + if (cl->line_len == 0 && cmd->key) { + free_cmd_line(cl); + break; + } + + strip_comment(cl); /* remove anything beyond a comment char */ + trim_trailing_spaces(cl); + trim_leading_spaces(cl); + + if (cl->line_len == 0) { /* An empty line */ + free_cmd_line(cl); + } + else { /* Else, non-empty ... */ + /* + * Parse out the bits ... + */ + switch (parse_line(cl)) { + case CMD_KEY: + if ((cmd->key = parse_key(cl, &cmd->cmd)) == NULL) { + fprintf(stderr, "Error parsing key from line: "); + print_line(cl); + fprintf(stderr, "\n"); + } + break; + + case CMD_VAL: + /* + * We need to add the value stuff to the list + * There could be a \ on the end which we need to + * handle at some time + */ + vl = (struct val_spec_list *)malloc(sizeof(struct val_spec_list)); + if (!vl) goto error; + vl->next = NULL; + vl->val = NULL; + vl->name = parse_value(cl, &vl->type, &vl->val); + if (!vl->name) goto error; + if (cmd->val_spec_list == NULL) { + cmd->val_spec_list = cmd->val_spec_last = vl; + } + else { + cmd->val_spec_last->next = vl; + cmd->val_spec_last = vl; + } + cmd->val_count++; + break; + + default: + fprintf(stderr, "Unrecognized line in command file: \n"); + print_line(cl); + break; + } + } + + } + if (!cmd->cmd) goto error; /* End of file ... */ + + return cmd; + + error: + if (vl) free(vl); + if (cmd) free_cmd(cmd); + return NULL; +} + +static +int regedit4_exec_cmd(CMD *cmd) +{ + + return 0; +} + +static +int editreg_1_0_file_type(int fd) +{ + int cur_ofs = 0; + char desc[11]; + + 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); /* FIXME */ + } + + if (cur_ofs) { + lseek(fd, 0, SEEK_SET); + } + + if (read(fd, desc, 10) < 10) { + fprintf(stderr, "Unable to read command file format\n"); + exit(2); /* FIXME */ + } + + desc[10] = 0; + + if (strcmp(desc, FMT_STRING_EDITREG1_0) == 0) { + lseek(fd, cur_ofs, SEEK_SET); + return FMT_REGEDIT4; + } + + return FMT_UNREC; +} + +static +CMD *editreg_1_0_get_cmd(int fd) +{ + return NULL; +} + +static +int editreg_1_0_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_0_file_type, editreg_1_0_get_cmd, editreg_1_0_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 + */ + +static +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 ... + */ + +static +int print_key(const char *path, char *name, char *class_name, int root, + int terminal, int vals) +{ + + if (full_print || terminal) fprintf(stdout, "[%s%s]\n", path, name); + + return 1; +} + +/* + * Sec Desc print functions + */ + +static +void print_type(unsigned char type) +{ + switch (type) { + case 0x00: + fprintf(stdout, " ALLOW"); + break; + case 0x01: + fprintf(stdout, " DENY"); + break; + case 0x02: + fprintf(stdout, " AUDIT"); + break; + case 0x03: + fprintf(stdout, " ALARM"); + break; + case 0x04: + fprintf(stdout, "ALLOW CPD"); + break; + case 0x05: + fprintf(stdout, "OBJ ALLOW"); + break; + case 0x06: + fprintf(stdout, " OBJ DENY"); + default: + fprintf(stdout, " UNKNOWN"); + break; + } +} + +static +void print_flags(unsigned char flags) +{ + char flg_output[21]; + int some = 0; + + flg_output[0] = 0; + if (!flags) { + fprintf(stdout, " "); + return; + } + if (flags & 0x01) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "OI"); + } + if (flags & 0x02) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "CI"); + } + if (flags & 0x04) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "NP"); + } + if (flags & 0x08) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "IO"); + } + if (flags & 0x10) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "IA"); + } + if (flags == 0xF) { + if (some) strcat(flg_output, ","); + some = 1; + strcat(flg_output, "VI"); + } + fprintf(stdout, " %s", flg_output); +} + +static +void print_perms(int perms) +{ + fprintf(stdout, " %8X", perms); +} + +static +void print_sid(sid_t *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"); +} + +static +void print_acl(ACL *acl, const char *prefix) +{ + int i; + + for (i = 0; i < acl->num_aces; i++) { + fprintf(stdout, ";;%s", prefix); + print_type(acl->aces[i]->type); + print_flags(acl->aces[i]->flags); + print_perms(acl->aces[i]->perms); + fprintf(stdout, " "); + print_sid(acl->aces[i]->trustee); + } +} + +static +int print_sec(SEC_DESC *sec_desc) +{ + if (!print_security) return 1; + fprintf(stdout, ";; SECURITY\n"); + fprintf(stdout, ";; Owner: "); + print_sid(sec_desc->owner); + fprintf(stdout, ";; Group: "); + print_sid(sec_desc->group); + if (sec_desc->sacl) { + fprintf(stdout, ";; SACL:\n"); + print_acl(sec_desc->sacl, " "); + } + if (sec_desc->dacl) { + fprintf(stdout, ";; DACL:\n"); + print_acl(sec_desc->dacl, " "); + } + return 1; +} + +/* + * Value print function here ... + */ +static +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]; + + memset(data_asc, 0, 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; +} + +static +void usage(void) +{ + fprintf(stderr, "Usage: editreg [-f] [-v] [-p] [-k] [-s] [-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-f\t sets full print mode where non-terminals are printed"); + fprintf(stderr, "\n\t-p\t prints the registry"); + fprintf(stderr, "\n\t-s\t prints security descriptors"); + 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, print_keys = 0; + int regf_opt = 1; /* Command name */ + int commands = 0, modified = 0; + char *cmd_file_name = NULL; + char *out_file_name = NULL; + CMD_FILE *cmd_file = NULL; + sid_t *lsid; + + if (argc < 2) { + usage(); + exit(1); + } + + /* + * Now, process the arguments + */ + + while ((opt = getopt(argc, argv, "fspvko:O:c:")) != EOF) { + switch (opt) { + case 'c': + commands = 1; + cmd_file_name = optarg; + regf_opt += 2; + break; + + case 'f': + full_print = 1; + regf_opt++; + break; + + case 'o': + out_file_name = optarg; + regf_opt += 2; + break; + + case 'O': + def_owner_sid_str = strdup(optarg); + regf_opt += 2; + if (!sid_string_to_sid(&lsid, def_owner_sid_str)) { + fprintf(stderr, "Default Owner SID: %s is incorrectly formatted\n", + def_owner_sid_str); + free(&def_owner_sid_str[0]); + def_owner_sid_str = NULL; + } + else + nt_delete_sid(lsid); + break; + + case 'p': + print_keys++; + regf_opt++; + break; + + case 's': + print_security++; + full_print++; + regf_opt++; + break; + + case 'v': + verbose++; + regf_opt++; + break; + + case 'k': + regf_opt++; + break; + + default: + usage(); + exit(1); + break; + } + } + + /* + * We only want to complain about the lack of a default owner SID if + * we need one. This approximates that need + */ + if (!def_owner_sid_str) { + def_owner_sid_str = "S-1-5-21-1-2-3-4"; + if (out_file_name || verbose) + fprintf(stderr, "Warning, default owner SID not set. Setting to %s\n", + def_owner_sid_str); + } + + if ((regf = nt_create_regf()) == NULL) { + fprintf(stderr, "Could not create registry object: %s\n", strerror(errno)); + exit(2); + } + + if (regf_opt < argc) { /* We have a registry file */ + if (!nt_set_regf_input_file(regf, argv[regf_opt])) { + fprintf(stderr, "Could not set name of registry file: %s, %s\n", + argv[regf_opt], 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); + } + } + + if (out_file_name) { + if (!nt_set_regf_output_file(regf, out_file_name)) { + fprintf(stderr, "Could not set name of output registry file: %s, %s\n", + out_file_name, strerror(errno)); + exit(3); + } + + } + + if (commands) { + CMD *cmd; + + cmd_file = cmd_file_create(cmd_file_name); + + while ((cmd = cmd_file->cmd_ops.get_cmd(cmd_file->fd)) != NULL) { + + /* + * Now, apply the requests to the tree ... + */ + switch (cmd->cmd) { + case CMD_ADD_KEY: { + REG_KEY *tmp = NULL; + + tmp = nt_find_key_by_name(regf->root, cmd->key); + + /* If we found it, apply the other bits, else create such a key */ + + if (!tmp) { + tmp = nt_add_reg_key(regf, cmd->key, True); + modified = 1; + } + + while (cmd->val_count) { + VAL_SPEC_LIST *val = cmd->val_spec_list; + VAL_KEY *reg_val = NULL; + + if (val->type == REG_TYPE_DELETE) { + reg_val = nt_delete_reg_value(tmp, val -> name); + if (reg_val) nt_delete_val_key(reg_val); + modified = 1; + } + else { + reg_val = nt_add_reg_value(tmp, val->name, val->type, + val->val); + modified = 1; + } + + cmd->val_spec_list = val->next; + free_val_spec_list(val); + cmd->val_count--; + } + + break; + } + + case CMD_DEL_KEY: + /* + * Any value does not matter ... + * Find the key if it exists, and delete it ... + */ + + nt_delete_key_by_name(regf, cmd->key); + modified = 1; + break; + } + } + free_cmd(cmd); + } + + /* + * At this point, we should have a registry in memory and should be able + * to iterate over it. + */ + + if (print_keys) { + nt_key_iterator(regf, regf->root, 0, "", print_key, print_sec, print_val); + } + + /* + * If there was an out_file_name and the tree was modified, print it + */ + if (modified && out_file_name) + if (!nt_store_registry(regf)) { + fprintf(stdout, "Error storing registry\n"); + } + + return 0; +} diff --git a/source/utils/log2pcaphex.c b/source/utils/log2pcaphex.c new file mode 100644 index 00000000000..4804b993382 --- /dev/null +++ b/source/utils/log2pcaphex.c @@ -0,0 +1,294 @@ +/* + Unix SMB/CIFS implementation. + Utility to extract pcap files from samba (log level 10) log files + + Copyright (C) Jelmer Vernooij 2003 + Thanks to Tim Potter for the genial idea + + Portions (from capconvert.c) (C) Andrew Tridgell 1997 + Portions (from text2pcap.c) (C) Ashok Narayanan 2001 + + Example use with -h parameter: + log2pcaphex < samba-log-file | text2pcap -T 139,139 - foo.pcap + + TODO: Have correct IP and TCP checksums. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 <assert.h> + +int quiet = 0; +int hexformat = 0; + +#define itoa(a) ((a) < 0xa?'0'+(a):'A' + (a-0xa)) + +#include <stdlib.h> +#include <unistd.h> +#include <sys/time.h> +#include <stdio.h> +#include <fcntl.h> + +#define TCPDUMP_MAGIC 0xa1b2c3d4 + +/* tcpdump file format */ +struct tcpdump_file_header { + uint32 magic; + uint16 major; + uint16 minor; + int32 zone; + uint32 sigfigs; + uint32 snaplen; + uint32 linktype; +}; + +struct tcpdump_packet { + struct timeval ts; + uint32 caplen; + uint32 len; +}; + +typedef struct { + uint8 ver_hdrlen; + uint8 dscp; + uint16 packet_length; + uint16 identification; + uint8 flags; + uint8 fragment; + uint8 ttl; + uint8 protocol; + uint16 hdr_checksum; + uint32 src_addr; + uint32 dest_addr; +} hdr_ip_t; + +static hdr_ip_t HDR_IP = {0x45, 0, 0, 0x3412, 0, 0, 0xff, 6, 0, 0x01010101, 0x02020202}; + +typedef struct { + uint16 source_port; + uint16 dest_port; + uint32 seq_num; + uint32 ack_num; + uint8 hdr_length; + uint8 flags; + uint16 window; + uint16 checksum; + uint16 urg; +} hdr_tcp_t; + +static hdr_tcp_t HDR_TCP = {139, 139, 0, 0, 0x50, 0, 0, 0, 0}; + +void print_pcap_header(FILE *out) +{ + struct tcpdump_file_header h; + h.magic = TCPDUMP_MAGIC; + h.major = 2; + h.minor = 4; + h.zone = 0; + h.sigfigs = 0; + h.snaplen = 102400; /* As long packets as possible */ + h.linktype = 101; /* Raw IP */ + fwrite(&h, sizeof(struct tcpdump_file_header), 1, out); +} + +void print_pcap_packet(FILE *out, unsigned char *data, long length, long caplen) +{ + static int i = 0; + struct tcpdump_packet p; + i++; + p.ts.tv_usec = 0; + p.ts.tv_sec = 0; + p.caplen = caplen; + p.len = length; + fwrite(&p, sizeof(struct tcpdump_packet), 1, out); + fwrite(data, sizeof(unsigned char), caplen, out); +} + +void print_hex_packet(FILE *out, unsigned char *data, long length) +{ + long i,cur = 0; + while(cur < length) { + fprintf(out, "%06lX ", cur); + for(i = cur; i < length && i < cur + 16; i++) { + fprintf(out, "%02x ", data[i]); + } + + cur = i; + fprintf(out, "\n"); + } +} + +void print_netbios_packet(FILE *out, unsigned char *data, long length, long actual_length) +{ + unsigned char *newdata; long offset = 0; + long newlen; + + newlen = length+sizeof(HDR_IP)+sizeof(HDR_TCP); + newdata = malloc(newlen); + + HDR_IP.packet_length = htons(newlen); + HDR_TCP.window = htons(0x2000); + HDR_TCP.source_port = HDR_TCP.dest_port = htons(139); + + memcpy(newdata+offset, &HDR_IP, sizeof(HDR_IP));offset+=sizeof(HDR_IP); + memcpy(newdata+offset, &HDR_TCP, sizeof(HDR_TCP));offset+=sizeof(HDR_TCP); + memcpy(newdata+offset,data,length); + + print_pcap_packet(out, newdata, newlen, actual_length+offset); + free(newdata); +} + +unsigned char *curpacket = NULL; +long curpacket_len = 0; + +void read_log_msg(FILE *in, unsigned char **_buffer, long *buffersize, long *data_offset, long *data_length) +{ + unsigned char *buffer; + int tmp; long i; + assert(fscanf(in, " size=%ld\n", buffersize)); + *buffersize+=4; /* for netbios */ + buffer = malloc(*buffersize); + memset(buffer, 0, *buffersize); + /* NetBIOS */ + buffer[0] = 0x00; + buffer[1] = 0x00; + memcpy(buffer+2, &buffersize, 2); + buffer[4] = 0xFF; + buffer[5] = 'S'; + buffer[6] = 'M'; + buffer[7] = 'B'; + assert(fscanf(in, " smb_com=0x%x\n", &tmp)); buffer[smb_com] = tmp; + assert(fscanf(in, " smb_rcls=%d\n", &tmp)); buffer[smb_rcls] = tmp; + assert(fscanf(in, " smb_reh=%d\n", &tmp)); buffer[smb_reh] = tmp; + assert(fscanf(in, " smb_err=%d\n", &tmp)); memcpy(buffer+smb_err, &tmp, 2); + assert(fscanf(in, " smb_flg=%d\n", &tmp)); buffer[smb_flg] = tmp; + assert(fscanf(in, " smb_flg2=%d\n", &tmp)); memcpy(buffer+smb_flg2, &tmp, 2); + assert(fscanf(in, " smb_tid=%d\n", &tmp)); memcpy(buffer+smb_tid, &tmp, 2); + assert(fscanf(in, " smb_pid=%d\n", &tmp)); memcpy(buffer+smb_pid, &tmp, 2); + assert(fscanf(in, " smb_uid=%d\n", &tmp)); memcpy(buffer+smb_uid, &tmp, 2); + assert(fscanf(in, " smb_mid=%d\n", &tmp)); memcpy(buffer+smb_mid, &tmp, 2); + assert(fscanf(in, " smt_wct=%d\n", &tmp)); buffer[smb_wct] = tmp; + for(i = 0; i < buffer[smb_wct]; i++) { + assert(fscanf(in, " smb_vwv[%*2d]=%*5d (0x%X)\n", &tmp)); + memcpy(buffer+smb_vwv+i*2, &tmp, 2); + } + + *data_offset = smb_vwv+buffer[smb_wct]*2; + assert(fscanf(in, " smb_bcc=%ld\n", data_length)); buffer[(*data_offset)] = *data_length; + (*data_offset)+=2; + *_buffer = buffer; +} + +long read_log_data(FILE *in, unsigned char *buffer, long data_length) +{ + long i, addr; char real[2][16]; int ret; + unsigned char tmp; + for(i = 0; i < data_length; i++) { + if(i % 16 == 0){ + if(i != 0) { /* Read data after each line */ + assert(fscanf(in, "%8s %8s", real[0], real[1]) == 2); + } + ret = fscanf(in, " [%03lX]", &addr); + if(!ret) { + if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i); + return i-1; + } + assert(addr == i); + } + if(!fscanf(in, "%02lX", &tmp)) { + if(!quiet)fprintf(stderr, "Only first %ld bytes are logged, packet trace will be incomplete\nTry a higher log level\n", i-1); + return i-1; + } + buffer[i] = tmp; + } + return data_length; +} + +int main (int argc, char **argv) +{ + const char *infile, *outfile; + FILE *out, *in; + int opt; + poptContext pc; + char buffer[4096]; + long data_offset, data_length; + long data_bytes_read; + int in_packet = 0; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, "Be quiet, don't output warnings" }, + { "hex", 'h', POPT_ARG_NONE, &hexformat, 0, "Output format readable by text2pcap" }, + POPT_TABLEEND + }; + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + poptSetOtherOptionHelp(pc, "[<infile> [<outfile>]]"); + + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + } + } + + poptGetArg(pc); /* Drop argv[0], the program name */ + + infile = poptGetArg(pc); + + if(infile) { + in = fopen(infile, "r"); + if(!in) { + perror("fopen"); + return 1; + } + } else in = stdin; + + outfile = poptGetArg(pc); + + if(outfile) { + out = fopen(outfile, "w+"); + if(!out) { + perror("fopen"); + fprintf(stderr, "Can't find %s, using stdout...\n", outfile); + } + } + + if(!outfile) out = stdout; + + if(!hexformat)print_pcap_header(out); + + while(!feof(in)) { + fgets(buffer, sizeof(buffer), in); + if(buffer[0] == '[') { /* Header */ + if(strstr(buffer, "show_msg")) { + in_packet++; + if(in_packet == 1)continue; + read_log_msg(in, &curpacket, &curpacket_len, &data_offset, &data_length); + } else if(in_packet && strstr(buffer, "dump_data")) { + data_bytes_read = read_log_data(in, curpacket+data_offset, data_length); + } else { + if(in_packet){ + if(hexformat) print_hex_packet(out, curpacket, curpacket_len); + else print_netbios_packet(out, curpacket, curpacket_len, data_bytes_read+data_offset); + free(curpacket); + } + in_packet = 0; + } + } + } + + return 0; +} diff --git a/source/utils/net.c b/source/utils/net.c new file mode 100644 index 00000000000..e4484488b61 --- /dev/null +++ b/source/utils/net.c @@ -0,0 +1,796 @@ +/* + 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 = ""; +const char *opt_container = "cn=Users"; +int opt_flags = -1; +int opt_timeout = 0; +const char *opt_target_workgroup = NULL; +int opt_machine_pass = 0; +BOOL opt_localgroup = False; +BOOL opt_domaingroup = False; +const char *opt_newntname = ""; +int opt_rid = 0; + +BOOL opt_have_ip = False; +struct in_addr opt_dest_ip; + +extern BOOL AllowDebugChange; + +uint32 get_sec_channel_type(const char *param) +{ + if (!(param && *param)) { + return get_default_sec_channel(); + } else { + if (strequal(param, "PDC")) { + return SEC_CHAN_BDC; + } else if (strequal(param, "BDC")) { + return SEC_CHAN_BDC; + } else if (strequal(param, "MEMBER")) { + return SEC_CHAN_WKSTA; +#if 0 + } else if (strequal(param, "DOMAIN")) { + return SEC_CHAN_DOMAIN; +#endif + } else { + return get_default_sec_channel(); + } + } +} + +/* + 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 && !opt_machine_pass) { + 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, Undefined, NULL); + + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } else { + d_printf("Could not connect to server %s\n", server_name); + + /* Display a nicer message depending on the result */ + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_LOGON_FAILURE)) + d_printf("The username or password was not correct.\n"); + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_ACCOUNT_LOCKED_OUT)) + d_printf("The account was locked out.\n"); + + if (NT_STATUS_V(nt_status) == + NT_STATUS_V(NT_STATUS_ACCOUNT_DISABLED)) + d_printf("The account was disabled.\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, Undefined, NULL); + + if (NT_STATUS_IS_OK(nt_status)) { + return nt_status; + } else { + DEBUG(1,("Cannot connect to server (anonymously). Error was %s\n", nt_errstr(nt_status))); + return nt_status; + } +} + +/**************************************************************************** + Use the local machine's password for this session +****************************************************************************/ +int net_use_machine_password(void) +{ + char *user_name = NULL; + + if (!secrets_init()) { + d_printf("ERROR: Unable to open secrets database\n"); + exit(1); + } + + user_name = NULL; + opt_password = secrets_fetch_machine_password(opt_target_workgroup, NULL, NULL); + if (asprintf(&user_name, "%s$@%s", global_myname(), lp_realm()) == -1) { + return -1; + } + opt_user_name = user_name; + return 0; +} + +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 ( !name_status_find(opt_target_workgroup, 0x1b, 0x20, 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_pdc(struct in_addr *server_ip, fstring server_name, const char *domain_name) +{ + if (get_pdc_ip(domain_name, server_ip)) { + if (is_zero_ip(*server_ip)) + return False; + + if (!name_status_find(domain_name, 0x1b, 0x20, *server_ip, server_name)) + return False; + + 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, falling back to RPC...\n"); + } + return net_rpc_join(argc, argv); +} + +static int net_changetrustpw(int argc, const char **argv) +{ + if (net_ads_check() == 0) + return net_ads_changetrustpw(argc, argv); + + return net_rpc_changetrustpw(argc, argv); +} + +static int net_changesecretpw(int argc, const char **argv) +{ + char *trust_pw; + uint32 sec_channel_type = SEC_CHAN_WKSTA; + + if(opt_force) { + trust_pw = getpass("Enter machine password: "); + + if (!secrets_store_machine_password(trust_pw, lp_workgroup(), sec_channel_type)) { + d_printf("Unable to write the machine account password in the secrets database"); + return 1; + } + else { + d_printf("Modified trust account password in secrets database\n"); + } + } + else { + d_printf("Machine account password change requires the -f flag.\n"); + d_printf("Do NOT use this function unless you know what it does!\n"); + d_printf("This function will change the ADS Domain member machine account password in the secrets.tdb file!\n"); + } + + return 0; +} + +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 = global_myname(); + } + + if(!initialize_password_db(False)) { + DEBUG(0, ("WARNING: Could not open passdb - local sid may not reflect passdb\n" + "backend knowlege (such as the sid stored in LDAP)\n")); + } + + /* Generate one, if it doesn't exist */ + get_global_sam_sid(); + + if (!secrets_fetch_domain_sid(name, &sid)) { + DEBUG(0, ("Can't fetch domain SID for name: %s\n", name)); + return 1; + } + sid_to_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(global_myname(), &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(!initialize_password_db(False)) { + DEBUG(0, ("WARNING: Could not open passdb - domain sid may not reflect passdb\n" + "backend knowlege (such as the sid stored in LDAP)\n")); + } + + /* Generate one, if it doesn't exist */ + get_global_sam_sid(); + + if (!secrets_fetch_domain_sid(global_myname(), &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", global_myname(), sid_str); + + if (!secrets_fetch_domain_sid(opt_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", opt_workgroup, sid_str); + + return 0; +} + +#ifdef WITH_FAKE_KASERVER + +int net_afskey_usage(int argc, const char **argv) +{ + d_printf(" net afskey filename\n" + "\tImports a OpenAFS KeyFile into our secrets.tdb\n\n"); + return -1; +} + +static int net_afskey(int argc, const char **argv) +{ + int fd; + struct afs_keyfile keyfile; + + if (argc != 2) { + d_printf("usage: 'net afskey <keyfile> cell'\n"); + return -1; + } + + if (!secrets_init()) { + d_printf("Could not open secrets.tdb\n"); + return -1; + } + + if ((fd = open(argv[0], O_RDONLY, 0)) < 0) { + d_printf("Could not open %s\n", argv[0]); + return -1; + } + + if (read(fd, &keyfile, sizeof(keyfile)) != sizeof(keyfile)) { + d_printf("Could not read keyfile\n"); + return -1; + } + + if (!secrets_store_afs_keyfile(argv[1], &keyfile)) { + d_printf("Could not write keyfile to secrets.tdb\n"); + return -1; + } + + return 0; +} + +#endif /* WITH_FAKE_KASERVER */ + +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)) + 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 maxrid\n")); + return 1; + } + + if ((rid = get_maxrid()) == 0) { + DEBUG(0, ("can't get current maximum rid\n")); + return 1; + } + + d_printf("Currently used maximum rid: %d\n", rid); + + return 0; +} + +/* main function table */ +static struct functable net_func[] = { + {"RPC", net_rpc}, + {"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}, + {"GROUPMAP", net_groupmap}, + {"VALIDATE", net_rap_validate}, + {"GROUPMEMBER", net_rap_groupmember}, + {"ADMIN", net_rap_admin}, + {"SERVICE", net_rap_service}, + {"PASSWORD", net_rap_password}, + {"CHANGETRUSTPW", net_changetrustpw}, + {"CHANGESECRETPW", net_changesecretpw}, + {"TIME", net_time}, + {"LOOKUP", net_lookup}, + {"JOIN", net_join}, + {"CACHE", net_cache}, + {"GETLOCALSID", net_getlocalsid}, + {"SETLOCALSID", net_setlocalsid}, + {"GETDOMAINSID", net_getdomainsid}, + {"MAXRID", net_maxrid}, + {"IDMAP", net_idmap}, + {"STATUS", net_status}, +#ifdef WITH_FAKE_KASERVER + {"AFSKEY", net_afskey}, +#endif + {"PRIV", net_priv}, + + {"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; + + struct poptOption long_options[] = { + {"help", 'h', POPT_ARG_NONE, 0, 'h'}, + {"workgroup", 'w', POPT_ARG_STRING, &opt_target_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}, + {"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}, + {"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}, + {"myworkgroup", 'W', POPT_ARG_STRING, &opt_workgroup}, + + /* Options for 'net groupmap set' */ + {"local", 'L', POPT_ARG_NONE, &opt_localgroup}, + {"domain", 'D', POPT_ARG_NONE, &opt_domaingroup}, + {"ntname", 'N', POPT_ARG_STRING, &opt_newntname}, + {"rid", 'R', POPT_ARG_INT, &opt_rid}, + + POPT_COMMON_SAMBA + { 0, 0, 0, 0} + }; + + zero_ip(&opt_dest_ip); + + /* set default debug level to 0 regardless of what smb.conf sets */ + DEBUGLEVEL_CLASS[DBGC_ALL] = 0; + 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); + } + } + + /* + * Don't load debug level from smb.conf. It should be + * set by cmdline arg or remain default (0) + */ + AllowDebugChange = False; + lp_load(dyn_CONFIGFILE,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) { + static fstring myname; + get_myname(myname); + opt_requester_name = myname; + } + + if (!opt_user_name && getenv("LOGNAME")) { + opt_user_name = getenv("LOGNAME"); + } + + if (!opt_workgroup) { + opt_workgroup = smb_xstrdup(lp_workgroup()); + } + + if (!opt_target_workgroup) { + opt_target_workgroup = smb_xstrdup(lp_workgroup()); + } + + if (!init_names()) + exit(1); + + load_interfaces(); + + /* this makes sure that when we do things like call scripts, + that it won't assert becouse we are not root */ + sec_init(); + + if (opt_machine_pass) { + /* it is very useful to be able to make ads queries as the + machine account for testing purposes and for domain leave */ + + net_use_machine_password(); + } + + if (!opt_password) { + opt_password = getenv("PASSWD"); + } + + 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/source/utils/net.h b/source/utils/net.h new file mode 100644 index 00000000000..62d5a742375 --- /dev/null +++ b/source/utils/net.h @@ -0,0 +1,69 @@ +/* + 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 + +/* don't open an RPC pipe */ +#define NET_FLAGS_NO_PIPE 32 + +extern int opt_maxusers; +extern const char *opt_comment; +extern const 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_machine_pass; +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_localgroup; +extern BOOL opt_domaingroup; +extern const char *opt_newntname; +extern int opt_rid; + +extern BOOL opt_have_ip; +extern struct in_addr opt_dest_ip; + +extern const char *share_type[]; + diff --git a/source/utils/net_ads.c b/source/utils/net_ads.c new file mode 100644 index 00000000000..6eec71aedf3 --- /dev/null +++ b/source/utils/net_ads.c @@ -0,0 +1,1328 @@ +/* + 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> <password> -Uadmin_username@realm%%admin_pass"\ +"\n\tchange a user's password using an admin account"\ +"\n\t(note: use realm in UPPERCASE, prompts if password is obmitted)\n"\ +"\nnet ads changetrustpw"\ +"\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, opt_target_workgroup, 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, opt_target_workgroup, 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)); + + d_printf("KDC server: %s\n", ads->auth.kdc_server ); + d_printf("Server time offset: %d\n", ads->auth.time_offset ); + + return 0; +} + +static void use_in_memory_ccache(void) { + /* Use in-memory credentials cache so we do not interfere with + * existing credentials */ + setenv(KRB5_ENV_CCNAME, "MEMORY:net_ads", 1); +} + +static ADS_STRUCT *ads_startup(void) +{ + ADS_STRUCT *ads; + ADS_STATUS status; + BOOL need_password = False; + BOOL second_time = False; + char *cp; + + /* lp_realm() should be handled by a command line param, + However, the join requires that realm be set in smb.conf + and compares our realm with the remote server's so this is + ok until someone needs more flexibility */ + + ads = ads_init(lp_realm(), opt_target_workgroup, opt_host); + + if (!opt_user_name) { + opt_user_name = "administrator"; + } + + if (opt_user_specified) { + need_password = True; + } + +retry: + if (!opt_password && need_password && !opt_machine_pass) { + char *prompt; + asprintf(&prompt,"%s's password: ", opt_user_name); + opt_password = getpass(prompt); + free(prompt); + } + + if (opt_password) { + use_in_memory_ccache(); + ads->auth.password = smb_xstrdup(opt_password); + } + + ads->auth.user_name = smb_xstrdup(opt_user_name); + + /* + * If the username is of the form "name@realm", + * extract the realm and convert to upper case. + * This is only used to establish the connection. + */ + if ((cp = strchr(ads->auth.user_name, '@'))!=0) { + *cp++ = '\0'; + ads->auth.realm = smb_xstrdup(cp); + strupper_m(ads->auth.realm); + } + + 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; + const 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 %s\n", + disp_fields[0], disp_fields[1]); + else + d_printf("%s\n", disp_fields[0]); + } + SAFE_FREE(disp_fields[0]); + SAFE_FREE(disp_fields[1]); + return True; + } + if (!values) /* must be new field, indicate string field */ + return True; + if (StrCaseCmp(field, "sAMAccountName") == 0) { + disp_fields[0] = 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 = ads_krb5_set_password(ads->auth.kdc_server, upn, argv[1], + ads->auth.time_offset); + safe_free(upn); + if (ADS_ERR_OK(status)) { + d_printf("User %s added\n", argv[0]); + rc = 0; + goto done; + } + + /* password didn't set, delete account */ + d_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, global_myname()); + 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", global_myname()); + 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) { + net_use_machine_password(); + } + + if (!(ads = ads_startup())) { + return -1; + } + + rc = ads_leave_realm(ads, global_myname()); + if (!ADS_ERR_OK(rc)) { + d_printf("Failed to delete host '%s' from the '%s' realm.\n", + global_myname(), ads->config.realm); + return -1; + } + + d_printf("Removed '%s' from realm '%s'\n", global_myname(), ads->config.realm); + + return 0; +} + +static int net_ads_join_ok(void) +{ + ADS_STRUCT *ads = NULL; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + net_use_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 *machine_account = NULL; + char *tmp_password; + const char *org_unit = "Computers"; + char *dn; + void *res; + DOM_SID dom_sid; + char *ou_str; + uint32 sec_channel_type = SEC_CHAN_WKSTA; + uint32 account_type = UF_WORKSTATION_TRUST_ACCOUNT; + const char *short_domain_name = NULL; + TALLOC_CTX *ctx = NULL; + + 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; + + if (!*lp_realm()) { + d_printf("realm must be set in in smb.conf for ADS join to succeed.\n"); + return -1; + } + + if (strcmp(ads->config.realm, lp_realm()) != 0) { + d_printf("realm of remote server (%s) and realm in smb.conf (%s) DO NOT match. Aborting join\n", ads->config.realm, lp_realm()); + 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 == ENUM_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, global_myname(), account_type, 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; + } + + if (asprintf(&machine_account, "%s$", global_myname()) == -1) { + d_printf("asprintf failed\n"); + return -1; + } + + rc = ads_set_machine_password(ads, machine_account, password); + if (!ADS_ERR_OK(rc)) { + d_printf("ads_set_machine_password: %s\n", ads_errstr(rc)); + return -1; + } + + /* make sure we get the right workgroup */ + + if ( !(ctx = talloc_init("net ads join")) ) { + d_printf("talloc_init() failed!\n"); + return -1; + } + + rc = ads_workgroup_name(ads, ctx, &short_domain_name); + if ( ADS_ERR_OK(rc) ) { + if ( !strequal(lp_workgroup(), short_domain_name) ) { + d_printf("The workgroup in smb.conf does not match the short\n"); + d_printf("domain name obtained from the server.\n"); + d_printf("Using the name [%s] from the server.\n", short_domain_name); + d_printf("You should set \"workgroup = %s\" in smb.conf.\n", short_domain_name); + } + } + else + short_domain_name = lp_workgroup(); + + d_printf("Using short domain name -- %s\n", short_domain_name); + + /* HACK ALRET! Store the sid and password under bother the lp_workgroup() + value from smb.conf and the string returned from the server. The former is + neede to bootstrap winbindd's first connection to the DC to get the real + short domain name --jerry */ + + 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, lp_workgroup(), sec_channel_type)) { + DEBUG(1,("Failed to save machine password\n")); + return -1; + } + + if (!secrets_store_domain_sid(short_domain_name, &dom_sid)) { + DEBUG(1,("Failed to save domain sid\n")); + return -1; + } + + if (!secrets_store_machine_password(password, short_domain_name, sec_channel_type)) { + DEBUG(1,("Failed to save machine password\n")); + return -1; + } + + d_printf("Joined '%s' to realm '%s'\n", global_myname(), ads->config.realm); + + SAFE_FREE(password); + SAFE_FREE(machine_account); + if ( ctx ) + talloc_destroy(ctx); + return 0; +} + +int net_ads_printer_usage(int argc, const char **argv) +{ + d_printf( +"\nnet ads printer search <printer>" +"\n\tsearch for a printer in the directory" +"\nnet ads printer info <printer> <server>" +"\n\tlookup info in directory for printer on server" +"\n\t(note: printer defaults to \"*\", server defaults to local)\n" +"\nnet ads printer publish <printername>" +"\n\tpublish printer in directory" +"\n\t(note: printer name is required)\n" +"\nnet ads printer remove <printername>" +"\n\tremove printer from directory" +"\n\t(note: printer name is required)\n"); + return -1; +} + +static int net_ads_printer_search(int argc, const char **argv) +{ + ADS_STRUCT *ads; + ADS_STATUS rc; + void *res = NULL; + + if (!(ads = ads_startup())) + return -1; + + rc = ads_find_printers(ads, &res); + + if (!ADS_ERR_OK(rc)) { + d_printf("ads_find_printer: %s\n", ads_errstr(rc)); + ads_msgfree(ads, res); + return -1; + } + + if (ads_count_replies(ads, res) == 0) { + d_printf("No results found\n"); + ads_msgfree(ads, res); + return -1; + } + + ads_dump(ads, res); + ads_msgfree(ads, res); + + return 0; +} + +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 = global_myname(); + + 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, *printername; + 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); + + printername = argv[0]; + + if (argc == 2) + servername = argv[1]; + else + servername = global_myname(); + + /* Get printer data from SPOOLSS */ + + resolve_name(servername, &server_ip, 0x20); + + nt_status = cli_full_connection(&cli, global_myname(), servername, + &server_ip, 0, + "IPC$", "IPC", + opt_user_name, opt_workgroup, + opt_password ? opt_password : "", + CLI_FULL_CONNECTION_USE_KERBEROS, + Undefined, NULL); + + if (NT_STATUS_IS_ERR(nt_status)) { + d_printf("Unable to open a connnection to %s to obtain data " + "for %s\n", servername, printername); + return -1; + } + + /* Publish on AD server */ + + ads_find_machine_acct(ads, &res, servername); + + if (ads_count_replies(ads, res) == 0) { + d_printf("Could not find machine account for server %s\n", + servername); + return -1; + } + + 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], printername, srv_dn); + + cli_nt_session_open(cli, PI_SPOOLSS); + get_remote_printer_publishing_data(cli, mem_ctx, &mods, printername); + + 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 = global_myname(); + + 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[] = { + {"SEARCH", net_ads_printer_search}, + {"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, *prompt; + const char *user; + ADS_STATUS ret; + + if (opt_user_name == NULL || opt_password == NULL) { + d_printf("You must supply an administrator username/password\n"); + return -1; + } + + + if (argc < 1) { + d_printf("ERROR: You must say which username to change password for\n"); + return -1; + } + + user = argv[0]; + if (!strchr(user, '@')) { + asprintf(&c, "%s@%s", argv[0], lp_realm()); + user = c; + } + + use_in_memory_ccache(); + c = strchr(auth_principal, '@'); + if (c) { + realm = ++c; + } else { + realm = lp_realm(); + } + + /* use the realm so we can eventually change passwords for users + in realms other than default */ + if (!(ads = ads_init(realm, NULL, NULL))) return -1; + + /* we don't actually need a full connect, but it's the easy way to + fill in the KDC's addresss */ + ads_connect(ads); + + if (!ads || !ads->config.realm) { + d_printf("Didn't find the kerberos server!\n"); + return -1; + } + + if (argv[1]) { + new_password = (char *)argv[1]; + } else { + asprintf(&prompt, "Enter new password for %s:", user); + new_password = getpass(prompt); + free(prompt); + } + + ret = kerberos_set_password(ads->auth.kdc_server, auth_principal, + auth_password, user, new_password, ads->auth.time_offset); + if (!ADS_ERR_OK(ret)) { + d_printf("Password change failed :-( ...\n"); + ads_destroy(&ads); + return -1; + } + + d_printf("Password change for %s completed.\n", user); + ads_destroy(&ads); + + return 0; +} + + +int net_ads_changetrustpw(int argc, const char **argv) +{ + ADS_STRUCT *ads; + char *host_principal; + char *hostname; + ADS_STATUS ret; + + if (!secrets_init()) { + DEBUG(1,("Failed to initialise secrets database\n")); + return -1; + } + + net_use_machine_password(); + + use_in_memory_ccache(); + + if (!(ads = ads_startup())) { + return -1; + } + + hostname = strdup(global_myname()); + strlower_m(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 *ldap_exp; + const char **attrs; + void *res = NULL; + + if (argc < 1) { + return net_ads_search_usage(argc, argv); + } + + if (!(ads = ads_startup())) { + return -1; + } + + ldap_exp = argv[0]; + attrs = (argv + 1); + + rc = ads_do_search_all(ads, ads->config.bind_path, + LDAP_SCOPE_SUBTREE, + ldap_exp, attrs, &res); + if (!ADS_ERR_OK(rc)) { + d_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}, + {"CHANGETRUSTPW", net_ads_changetrustpw}, +#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}, + {"CHANGETRUSTPW", net_ads_changetrustpw}, + {"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_changetrustpw(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/source/utils/net_ads_cldap.c b/source/utils/net_ads_cldap.c new file mode 100644 index 00000000000..1903172cf75 --- /dev/null +++ b/source/utils/net_ads_cldap.c @@ -0,0 +1,364 @@ +/* + 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 + +#define MAX_DNS_LABEL 255 + 1 + +struct cldap_netlogon_reply { + uint32 type; + uint32 flags; + UUID_FLAT guid; + + char forest[MAX_DNS_LABEL]; + char domain[MAX_DNS_LABEL]; + char hostname[MAX_DNS_LABEL]; + + char netbios_domain[MAX_DNS_LABEL]; + char netbios_hostname[MAX_DNS_LABEL]; + + char unk[MAX_DNS_LABEL]; + char user_name[MAX_DNS_LABEL]; + char site_name[MAX_DNS_LABEL]; + char site_name_2[MAX_DNS_LABEL]; + + uint32 version; + uint16 lmnt_token; + uint16 lm20_token; +}; + +/* + These seem to be strings as described in RFC1035 4.1.4 and can be: + + - a sequence of labels ending in a zero octet + - a pointer + - a sequence of labels ending with a pointer + + A label is a byte where the first two bits must be zero and the remaining + bits represent the length of the label followed by the label itself. + Therefore, the length of a label is at max 64 bytes. Under RFC1035, a + sequence of labels cannot exceed 255 bytes. + + A pointer consists of a 14 bit offset from the beginning of the data. + + struct ptr { + unsigned ident:2; // must be 11 + unsigned offset:14; // from the beginning of data + }; + + This is used as a method to compress the packet by eliminated duplicate + domain components. Since a UDP packet should probably be < 512 bytes and a + DNS name can be up to 255 bytes, this actually makes a lot of sense. +*/ +static unsigned pull_netlogon_string(char *ret, const char *ptr, + const char *data) +{ + char *pret = ret; + int followed_ptr = 0; + unsigned ret_len = 0; + + memset(pret, 0, MAX_DNS_LABEL); + do { + if ((*ptr & 0xc0) == 0xc0) { + uint16 len; + + if (!followed_ptr) { + ret_len += 2; + followed_ptr = 1; + } + len = ((ptr[0] & 0x3f) << 8) | ptr[1]; + ptr = data + len; + } else if (*ptr) { + uint8 len = (uint8)*(ptr++); + + if ((pret - ret + len + 1) >= MAX_DNS_LABEL) { + d_printf("DC returning too long DNS name\n"); + return 0; + } + + if (pret != ret) { + *pret = '.'; + pret++; + } + memcpy(pret, ptr, len); + pret += len; + ptr += len; + + if (!followed_ptr) { + ret_len += (len + 1); + } + } + } while (*ptr); + + return followed_ptr ? ret_len : ret_len + 1; +} + +/* + 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]; +#ifdef CLDAP_USER_QUERY + char aac[4]; + + SIVAL(aac, 0, 0x00000180); +#endif + 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); + +#ifdef CLDAP_USER_QUERY + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "User", 4); + asn1_write_OctetString(&data, "SAMBA$", 6); + asn1_pop_tag(&data); + + asn1_push_tag(&data, ASN1_CONTEXT(3)); + asn1_write_OctetString(&data, "AAC", 4); + asn1_write_OctetString(&data, aac, 4); + asn1_pop_tag(&data); +#endif + + 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) != (ssize_t)data.length) { + d_printf("failed to send cldap query (%s)\n", strerror(errno)); + } + + 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; + + 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; + } + + p = (char *)os3.data; + + reply->type = IVAL(p, 0); p += 4; + reply->flags = IVAL(p, 0); p += 4; + + memcpy(&reply->guid.info, p, UUID_FLAT_SIZE); + p += UUID_FLAT_SIZE; + + p += pull_netlogon_string(reply->forest, p, (const char *)os3.data); + p += pull_netlogon_string(reply->domain, p, (const char *)os3.data); + p += pull_netlogon_string(reply->hostname, p, (const char *)os3.data); + p += pull_netlogon_string(reply->netbios_domain, p, (const char *)os3.data); + p += pull_netlogon_string(reply->netbios_hostname, p, (const char *)os3.data); + p += pull_netlogon_string(reply->unk, p, (const char *)os3.data); + + if (reply->type == SAMLOGON_AD_R) { + p += pull_netlogon_string(reply->user_name, p, (const char *)os3.data); + } else { + *reply->user_name = 0; + } + + p += pull_netlogon_string(reply->site_name, p, (const char *)os3.data); + p += pull_netlogon_string(reply->site_name_2, p, (const char *)os3.data); + + 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; +} + +/* + 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, global_myname(), 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: "); + switch (reply.type) { + case SAMLOGON_AD_UNK_R: + d_printf("SAMLOGON\n"); + break; + case SAMLOGON_AD_R: + d_printf("SAMLOGON_USER\n"); + break; + default: + d_printf("0x%x\n", reply.type); + break; + } + d_printf("GUID: %s\n", + smb_uuid_string_static(smb_uuid_unpack_static(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"); + + printf("Forest:\t\t\t%s\n", reply.forest); + printf("Domain:\t\t\t%s\n", reply.domain); + printf("Domain Controller:\t%s\n", reply.hostname); + + printf("Pre-Win2k Domain:\t%s\n", reply.netbios_domain); + printf("Pre-Win2k Hostname:\t%s\n", reply.netbios_hostname); + + if (*reply.unk) printf("Unk:\t\t\t%s\n", reply.unk); + if (*reply.user_name) printf("User name:\t%s\n", reply.user_name); + + printf("Site Name:\t\t%s\n", reply.site_name); + printf("Site Name (2):\t\t%s\n", reply.site_name_2); + + 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); + + return ret; +} + + +#endif diff --git a/source/utils/net_cache.c b/source/utils/net_cache.c new file mode 100644 index 00000000000..a9559164587 --- /dev/null +++ b/source/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\n", keystr); +} + + +/** + * Parse text representation of timeout value + * + * @param timeout_str string containing text representation of the timeout + * @return numeric timeout of time_t type + **/ +static time_t parse_timeout(const char* timeout_str) +{ + char sign = '\0', *number = NULL, unit = '\0'; + int len, number_begin, number_end; + time_t timeout; + + /* sign detection */ + if (timeout_str[0] == '!' || timeout_str[0] == '+') { + sign = timeout_str[0]; + number_begin = 1; + } else { + number_begin = 0; + } + + /* unit detection */ + len = strlen(timeout_str); + switch (timeout_str[len - 1]) { + case 's': + case 'm': + case 'h': + case 'd': + case 'w': unit = timeout_str[len - 1]; + } + + /* number detection */ + len = (sign) ? strlen(&timeout_str[number_begin]) : len; + number_end = (unit) ? len - 1 : len; + number = 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 del <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/source/utils/net_groupmap.c b/source/utils/net_groupmap.c new file mode 100644 index 00000000000..78e763e1818 --- /dev/null +++ b/source/utils/net_groupmap.c @@ -0,0 +1,767 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-2000, + * Copyright (C) Jean François Micouleau 1998-2001. + * Copyright (C) Gerald Carter 2003. + * + * This program is free software; you can 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" + + +/********************************************************* + utility function to parse an integer parameter from + "parameter = value" +**********************************************************/ +static uint32 get_int_param( const char* param ) +{ + char *p; + + p = strchr( param, '=' ); + if ( !p ) + return 0; + + return atoi(p+1); +} + +/********************************************************* + utility function to parse an integer parameter from + "parameter = value" +**********************************************************/ +static char* get_string_param( const char* param ) +{ + char *p; + + p = strchr( param, '=' ); + if ( !p ) + return NULL; + + return (p+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)) { + printf("NT Group %s doesn't exist in mapping DB\n", input); + return False; + } else { + *sid = map.sid; + } + } else { + if (!string_to_sid(sid, input)) { + printf("converting sid %s from a string failed!\n", input); + return False; + } + } + return True; +} + +/********************************************************* + Dump a GROUP_MAP entry to stdout (long or short listing) +**********************************************************/ + +static void print_map_entry ( GROUP_MAP map, BOOL long_list ) +{ + fstring string_sid; + fstring group_type; + + decode_sid_name_use(group_type, map.sid_name_use); + sid_to_string(string_sid, &map.sid); + + if (!long_list) + d_printf("%s (%s) -> %s\n", map.nt_name, string_sid, gidtoname(map.gid)); + else { + d_printf("%s\n", map.nt_name); + d_printf("\tSID : %s\n", string_sid); + d_printf("\tUnix group: %s\n", gidtoname(map.gid)); + d_printf("\tGroup type: %s\n", group_type); + d_printf("\tComment : %s\n", map.comment); + } + +} +/********************************************************* + List the groups. +**********************************************************/ +static int net_groupmap_list(int argc, const char **argv) +{ + int entries; + BOOL long_list = False; + int i; + fstring ntgroup = ""; + fstring sid_string = ""; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !StrCaseCmp(argv[i], "verbose")) { + long_list = True; + } + else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_printf("must supply a SID\n"); + return -1; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + /* list a single group is given a name */ + if ( ntgroup[0] || sid_string[0] ) { + DOM_SID sid; + GROUP_MAP map; + + if ( sid_string[0] ) + fstrcpy( ntgroup, sid_string); + + if (!get_sid_from_input(&sid, ntgroup)) { + return -1; + } + + /* Get the current mapping from the database */ + if(!pdb_getgrsid(&map, sid)) { + d_printf("Failure to local group SID in the database\n"); + return -1; + } + + print_map_entry( map, long_list ); + } + else { + GROUP_MAP *map=NULL; + /* enumerate all group mappings */ + if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries, ENUM_ALL_MAPPED)) + return -1; + + for (i=0; i<entries; i++) { + print_map_entry( map[i], long_list ); + } + + SAFE_FREE(map); + } + + return 0; +} + +/********************************************************* + Add a new group mapping entry +**********************************************************/ + +static int net_groupmap_add(int argc, const char **argv) +{ + DOM_SID sid; + fstring ntgroup = ""; + fstring unixgrp = ""; + fstring string_sid = ""; + fstring type = ""; + fstring ntcomment = ""; + enum SID_NAME_USE sid_type = SID_NAME_DOM_GRP; + uint32 rid = 0; + gid_t gid; + int i; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !StrnCaseCmp(argv[i], "rid", strlen("rid")) ) { + rid = get_int_param(argv[i]); + if ( rid < DOMAIN_GROUP_RID_ADMINS ) { + d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) { + fstrcpy( unixgrp, get_string_param( argv[i] ) ); + if ( !unixgrp[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) { + fstrcpy( string_sid, get_string_param( argv[i] ) ); + if ( !string_sid[0] ) { + d_printf("must supply a SID\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) { + fstrcpy( ntcomment, get_string_param( argv[i] ) ); + if ( !ntcomment[0] ) { + d_printf("must supply a comment string\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) { + fstrcpy( type, get_string_param( argv[i] ) ); + switch ( type[0] ) { + case 'b': + case 'B': + sid_type = SID_NAME_WKN_GRP; + break; + case 'd': + case 'D': + sid_type = SID_NAME_DOM_GRP; + break; + case 'l': + case 'L': + sid_type = SID_NAME_ALIAS; + break; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if ( !unixgrp[0] ) { + d_printf("Usage: net groupmap add {rid=<int>|sid=<string>} unixgroup=<string> [type=<domain|local|builtin>] [ntgroup=<string>] [comment=<string>]\n"); + return -1; + } + + if ( (gid = nametogid(unixgrp)) == (gid_t)-1 ) { + d_printf("Can't lookup UNIX group %s\n", unixgrp); + return -1; + } + + if ( (rid == 0) && (string_sid[0] == '\0') ) { + d_printf("No rid or sid specified, choosing algorithmic mapping\n"); + rid = pdb_gid_to_group_rid(gid); + } + + /* append the rid to our own domain/machine SID if we don't have a full SID */ + if ( !string_sid[0] ) { + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, rid); + sid_to_string(string_sid, &sid); + } + + if (!ntcomment[0]) + fstrcpy(ntcomment, "Local Unix group"); + + if (!ntgroup[0] ) + fstrcpy( ntgroup, unixgrp ); + + + if (!add_initial_entry(gid, string_sid, sid_type, ntgroup, ntcomment)) { + d_printf("adding entry for group %s failed!\n", ntgroup); + return -1; + } + + d_printf("Successully added group %s to the mapping db\n", ntgroup); + return 0; +} + +static int net_groupmap_modify(int argc, const char **argv) +{ + DOM_SID sid; + GROUP_MAP map; + fstring ntcomment = ""; + fstring type = ""; + fstring ntgroup = ""; + fstring unixgrp = ""; + fstring sid_string = ""; + enum SID_NAME_USE sid_type = SID_NAME_UNKNOWN; + int i; + gid_t gid; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "comment", strlen("comment")) ) { + fstrcpy( ntcomment, get_string_param( argv[i] ) ); + if ( !ntcomment[0] ) { + d_printf("must supply a comment string\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "unixgroup", strlen("unixgroup")) ) { + fstrcpy( unixgrp, get_string_param( argv[i] ) ); + if ( !unixgrp[0] ) { + d_printf("must supply a group name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "type", strlen("type")) ) { + fstrcpy( type, get_string_param( argv[i] ) ); + switch ( type[0] ) { + case 'd': + case 'D': + sid_type = SID_NAME_DOM_GRP; + break; + case 'l': + case 'L': + sid_type = SID_NAME_ALIAS; + break; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if ( !ntgroup[0] && !sid_string[0] ) { + d_printf("Usage: net groupmap modify {ntgroup=<string>|sid=<SID>} [comment=<string>] [unixgroup=<string>] [type=<domain|local>]\n"); + return -1; + } + + /* give preference to the SID; if both the ntgroup name and SID + are defined, use the SID and assume that the group name could be a + new name */ + + if ( sid_string[0] ) { + if (!get_sid_from_input(&sid, sid_string)) { + return -1; + } + } + else { + if (!get_sid_from_input(&sid, ntgroup)) { + return -1; + } + } + + /* Get the current mapping from the database */ + if(!pdb_getgrsid(&map, sid)) { + d_printf("Failure to local group SID in the database\n"); + return -1; + } + + /* + * Allow changing of group type only between domain and local + * We disallow changing Builtin groups !!! (SID problem) + */ + if (sid_type != SID_NAME_UNKNOWN) { + if (map.sid_name_use == SID_NAME_WKN_GRP) { + d_printf("You can only change between domain and local groups.\n"); + return -1; + } + + map.sid_name_use=sid_type; + } + + /* Change comment if new one */ + if ( ntcomment[0] ) + fstrcpy( map.comment, ntcomment ); + + if ( ntgroup[0] ) + fstrcpy( map.nt_name, ntgroup ); + + if ( unixgrp[0] ) { + gid = nametogid( unixgrp ); + if ( gid == -1 ) { + d_printf("Unable to lookup UNIX group %s. Make sure the group exists.\n", + unixgrp); + return -1; + } + + map.gid = gid; + } + + if ( !pdb_update_group_mapping_entry(&map) ) { + d_printf("Could not update group database\n"); + return -1; + } + + d_printf("Updated mapping entry for %s\n", map.nt_name); + + return 0; +} + +static int net_groupmap_delete(int argc, const char **argv) +{ + DOM_SID sid; + fstring ntgroup = ""; + fstring sid_string = ""; + int i; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if ( !StrnCaseCmp(argv[i], "ntgroup", strlen("ntgroup")) ) { + fstrcpy( ntgroup, get_string_param( argv[i] ) ); + if ( !ntgroup[0] ) { + d_printf("must supply a name\n"); + return -1; + } + } + else if ( !StrnCaseCmp(argv[i], "sid", strlen("sid")) ) { + fstrcpy( sid_string, get_string_param( argv[i] ) ); + if ( !sid_string[0] ) { + d_printf("must supply a SID\n"); + return -1; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if ( !ntgroup[0] && !sid_string[0]) { + d_printf("Usage: net groupmap delete {ntgroup=<string>|sid=<SID>}\n"); + return -1; + } + + /* give preference to the SID if we have that */ + + if ( sid_string[0] ) + fstrcpy( ntgroup, sid_string ); + + if ( !get_sid_from_input(&sid, ntgroup) ) { + d_printf("Unable to resolve group %s to a SID\n", ntgroup); + return -1; + } + + if ( !pdb_delete_group_mapping_entry(sid) ) { + printf("Failed to removing group %s from the mapping db!\n", ntgroup); + return -1; + } + + d_printf("Sucessfully removed %s from the mapping db\n", ntgroup); + + return 0; +} + +static int net_groupmap_set(int argc, const char **argv) +{ + const char *ntgroup = NULL; + struct group *grp = NULL; + GROUP_MAP map; + BOOL have_map = False; + + if ((argc < 1) || (argc > 2)) { + d_printf("Usage: net groupmap set \"NT Group\" " + "[\"unix group\"] [-C \"comment\"] [-L] [-D]\n"); + return -1; + } + + if ( opt_localgroup && opt_domaingroup ) { + d_printf("Can only specify -L or -D, not both\n"); + return -1; + } + + ntgroup = argv[0]; + + if (argc == 2) { + grp = getgrnam(argv[1]); + + if (grp == NULL) { + d_printf("Could not find unix group %s\n", argv[1]); + return -1; + } + } + + have_map = pdb_getgrnam(&map, ntgroup); + + if (!have_map) { + DOM_SID sid; + have_map = ( (strncmp(ntgroup, "S-", 2) == 0) && + string_to_sid(&sid, ntgroup) && + pdb_getgrsid(&map, sid) ); + } + + if (!have_map) { + + /* Ok, add it */ + + if (grp == NULL) { + d_printf("Could not find group mapping for %s\n", + ntgroup); + return -1; + } + + map.gid = grp->gr_gid; + + if (opt_rid == 0) { + opt_rid = pdb_gid_to_group_rid(map.gid); + } + + sid_copy(&map.sid, get_global_sam_sid()); + sid_append_rid(&map.sid, opt_rid); + + map.sid_name_use = SID_NAME_DOM_GRP; + fstrcpy(map.nt_name, ntgroup); + fstrcpy(map.comment, ""); + + if (!pdb_add_group_mapping_entry(&map)) { + d_printf("Could not add mapping entry for %s\n", + ntgroup); + return -1; + } + } + + /* Now we have a mapping entry, update that stuff */ + + if ( opt_localgroup || opt_domaingroup ) { + if (map.sid_name_use == SID_NAME_WKN_GRP) { + d_printf("Can't change type of the BUILTIN group %s\n", + map.nt_name); + return -1; + } + } + + if (opt_localgroup) + map.sid_name_use = SID_NAME_ALIAS; + + if (opt_domaingroup) + map.sid_name_use = SID_NAME_DOM_GRP; + + /* The case (opt_domaingroup && opt_localgroup) was tested for above */ + + if (strlen(opt_comment) > 0) + fstrcpy(map.comment, opt_comment); + + if (strlen(opt_newntname) > 0) + fstrcpy(map.nt_name, opt_newntname); + + if (grp != NULL) + map.gid = grp->gr_gid; + + if (!pdb_update_group_mapping_entry(&map)) { + d_printf("Could not update group mapping for %s\n", ntgroup); + return -1; + } + + return 0; +} + +static int net_groupmap_cleanup(int argc, const char **argv) +{ + GROUP_MAP *map = NULL; + int i, entries; + + if (!pdb_enum_group_mapping(SID_NAME_UNKNOWN, &map, &entries, + ENUM_ALL_MAPPED)) { + d_printf("Could not list group mappings\n"); + return -1; + } + + for (i=0; i<entries; i++) { + + if (map[i].sid_name_use == SID_NAME_WKN_GRP) + continue; + + if (map[i].gid == -1) + printf("Group %s is not mapped\n", map[i].nt_name); + + if (!sid_check_is_in_our_domain(&map[i].sid)) { + printf("Deleting mapping for NT Group %s, sid %s\n", + map[i].nt_name, + sid_string_static(&map[i].sid)); + pdb_delete_group_mapping_entry(map[i].sid); + } + } + + SAFE_FREE(map); + + return 0; +} + +static int net_groupmap_addmem(int argc, const char **argv) +{ + DOM_SID alias, member; + + if ( (argc != 2) || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("Usage: net groupmap addmem alias-sid member-sid\n"); + return -1; + } + + if (!pdb_add_aliasmem(&alias, &member)) { + d_printf("Could not add sid %s to alias %s\n", + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_delmem(int argc, const char **argv) +{ + DOM_SID alias, member; + + if ( (argc != 2) || + !string_to_sid(&alias, argv[0]) || + !string_to_sid(&member, argv[1]) ) { + d_printf("Usage: net groupmap delmem alias-sid member-sid\n"); + return -1; + } + + if (!pdb_del_aliasmem(&alias, &member)) { + d_printf("Could not delete sid %s from alias %s\n", + argv[1], argv[0]); + return -1; + } + + return 0; +} + +static int net_groupmap_listmem(int argc, const char **argv) +{ + DOM_SID alias; + DOM_SID *members; + int i, num; + NTSTATUS result; + + if ( (argc != 1) || + !string_to_sid(&alias, argv[0]) ) { + d_printf("Usage: net groupmap listmem alias-sid\n"); + return -1; + } + + if (!pdb_enum_aliasmem(&alias, &members, &num)) { + d_printf("Could not list members for sid %s: %s\n", + argv[0], nt_errstr(result)); + return -1; + } + + for (i = 0; i < num; i++) { + printf("%s\n", sid_string_static(&(members[i]))); + } + + SAFE_FREE(members); + + return 0; +} + +static int net_groupmap_memberships(int argc, const char **argv) +{ + DOM_SID member; + DOM_SID *aliases; + int i, num; + NTSTATUS result; + + if ( (argc != 1) || + !string_to_sid(&member, argv[0]) ) { + d_printf("Usage: net groupmap memberof sid\n"); + return -1; + } + + if (!pdb_enum_alias_memberships(&member, &aliases, &num)) { + d_printf("Could not list memberships for sid %s: %s\n", + argv[0], nt_errstr(result)); + return -1; + } + + for (i = 0; i < num; i++) { + printf("%s\n", sid_string_static(&(aliases[i]))); + } + + SAFE_FREE(aliases); + + return 0; +} + +int net_help_groupmap(int argc, const char **argv) +{ + d_printf("net groupmap add"\ + "\n Create a new group mapping\n"); + d_printf("net groupmap modify"\ + "\n Update a group mapping\n"); + d_printf("net groupmap delete"\ + "\n Remove a group mapping\n"); + d_printf("net groupmap addmember"\ + "\n Add a foreign alias member\n"); + d_printf("net groupmap delmember"\ + "\n Delete a foreign alias member\n"); + d_printf("net groupmap listmembers"\ + "\n List foreign group members\n"); + d_printf("net groupmap memberships"\ + "\n List foreign group memberships\n"); + d_printf("net groupmap list"\ + "\n List current group map\n"); + d_printf("net groupmap set"\ + "\n Set group mapping\n"); + d_printf("net groupmap cleanup"\ + "\n Remove foreign group mapping entries\n"); + + return -1; +} + + +/*********************************************************** + migrated functionality from smbgroupedit + **********************************************************/ +int net_groupmap(int argc, const char **argv) +{ + struct functable func[] = { + {"add", net_groupmap_add}, + {"modify", net_groupmap_modify}, + {"delete", net_groupmap_delete}, + {"set", net_groupmap_set}, + {"cleanup", net_groupmap_cleanup}, + {"addmem", net_groupmap_addmem}, + {"delmem", net_groupmap_delmem}, + {"listmem", net_groupmap_listmem}, + {"memberships", net_groupmap_memberships}, + {"list", net_groupmap_list}, + {"help", net_help_groupmap}, + {NULL, NULL} + }; + + /* we shouldn't have silly checks like this */ +#if 0 + if (getuid() != 0) { + d_printf("You must be root to edit group mappings.\nExiting...\n"); + return -1; + } +#endif + + if ( argc ) + return net_run_function(argc, argv, func, net_help_groupmap); + + return net_help_groupmap( argc, argv ); +} + diff --git a/source/utils/net_help.c b/source/utils/net_help.c new file mode 100644 index 00000000000..38261be90a7 --- /dev/null +++ b/source/utils/net_help.c @@ -0,0 +1,220 @@ +/* + 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 --debuglevel=<level>\tdebug level (0-10)\n"); + d_printf("\t-n or --myname=<name>\t\tclient name\n"); + d_printf("\t-U or --user=<name>\t\tuser name\n"); + d_printf("\t-s or --configfile=<path>\tpathname of smb.conf file\n"); + d_printf("\t-l or --long\t\t\tDisplay full information\n"); + d_printf("\t-V or --version\t\t\tPrint samba version information\n"); + d_printf("\t-P or --machine-pass\t\tAuthenticate as machine account\n"); + 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"\ +" CHANGESCRETPW\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 rpc group LIST [global|local|builtin]* [misc. options]"\ + "\n\tList specific user groups\n\n"); + d_printf("net [<method>] group DELETE <name> "\ + "[misc. options] [targets]"\ + "\n\tDelete specified group\n"); + d_printf("\nnet [<method>] group ADD <name> [-C comment] [-c container]"\ + " [misc. options] [targets]\n\tCreate specified group\n"); + d_printf("\nnet rpc group MEMBERS <name>\n\tList Group Members\n\n"); + 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; +} + +int net_help_status(int argc, const char **argv) +{ + d_printf(" net status sessions [parseable] " + "Show list of open sessions\n"); + d_printf(" net status shares [parseable] " + "Show list of open shares\n"); + return -1; +} + +static int 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 groupmap\t\tto manage group mappings\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"\ + " net changesecretpw\tto change the machine password in the local secrets database only\n"\ + " \tthis requires the -f flag as a safety barrier\n"\ + " net status\t\tShow server status\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}, + {"GROUPMAP", net_help_groupmap}, + {"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}, +#ifdef WITH_FAKE_KASERVER + {"AFSKEY", net_afskey_usage}, +#endif + + {"HELP", help_usage}, + {NULL, NULL}}; + + return net_run_function(argc, argv, func, net_usage); +} diff --git a/source/utils/net_idmap.c b/source/utils/net_idmap.c new file mode 100644 index 00000000000..f5b4bf1b4a7 --- /dev/null +++ b/source/utils/net_idmap.c @@ -0,0 +1,264 @@ +/* + Samba Unix/Linux SMB client library + Distributed SMB/CIFS Server Management Utility + Copyright (C) 2003 Andrew Bartlett (abartlet@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + + +/*********************************************************** + Helper function for net_idmap_dump. Dump one entry. + **********************************************************/ +static int net_idmap_dump_one_entry(TDB_CONTEXT *tdb, + TDB_DATA key, + TDB_DATA data, + void *unused) +{ + if (strcmp(key.dptr, "USER HWM") == 0) { + printf("USER HWM %d\n", IVAL(data.dptr,0)); + return 0; + } + + if (strcmp(key.dptr, "GROUP HWM") == 0) { + printf("GROUP HWM %d\n", IVAL(data.dptr,0)); + return 0; + } + + if (strncmp(key.dptr, "S-", 2) != 0) + return 0; + + printf("%s %s\n", data.dptr, key.dptr); + return 0; +} + +/*********************************************************** + Dump the current idmap + **********************************************************/ +static int net_idmap_dump(int argc, const char **argv) +{ + TDB_CONTEXT *idmap_tdb; + + if ( argc != 1 ) + return net_help_idmap( argc, argv ); + + idmap_tdb = tdb_open_log(argv[0], 0, TDB_DEFAULT, O_RDONLY, 0); + + if (idmap_tdb == NULL) { + d_printf("Could not open idmap: %s\n", argv[0]); + return -1; + } + + tdb_traverse(idmap_tdb, net_idmap_dump_one_entry, NULL); + + tdb_close(idmap_tdb); + + return 0; +} + +/*********************************************************** + Fix up the HWMs after a idmap restore. + **********************************************************/ + +struct hwms { + BOOL ok; + int user_hwm; + int group_hwm; +}; + +static int net_idmap_find_max_id(TDB_CONTEXT *tdb, TDB_DATA key, TDB_DATA data, + void *handle) +{ + struct hwms *hwms = (struct hwms *)handle; + int *idptr = NULL; + int id; + + if (strncmp(key.dptr, "S-", 2) != 0) + return 0; + + if (sscanf(data.dptr, "GID %d", &id) == 1) { + idptr = &hwms->group_hwm; + } + + if (sscanf(data.dptr, "UID %d", &id) == 1) { + idptr = &hwms->user_hwm; + } + + if (idptr == NULL) { + d_printf("Illegal idmap entry: [%s]->[%s]\n", + key.dptr, data.dptr); + hwms->ok = False; + return -1; + } + + if (*idptr <= id) + *idptr = id+1; + + return 0; +} + +static NTSTATUS net_idmap_fixup_hwm(void) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + TDB_CONTEXT *idmap_tdb; + char *tdbfile = NULL; + + struct hwms hwms; + struct hwms highest; + + if (!lp_idmap_uid(&hwms.user_hwm, &highest.user_hwm) || + !lp_idmap_gid(&hwms.group_hwm, &highest.group_hwm)) { + d_printf("idmap range missing\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + tdbfile = strdup(lock_path("winbindd_idmap.tdb")); + if (!tdbfile) { + DEBUG(0, ("idmap_init: out of memory!\n")); + return NT_STATUS_NO_MEMORY; + } + + idmap_tdb = tdb_open_log(tdbfile, 0, TDB_DEFAULT, O_RDWR, 0); + + if (idmap_tdb == NULL) { + d_printf("Could not open idmap: %s\n", tdbfile); + return NT_STATUS_NO_SUCH_FILE; + } + + hwms.ok = True; + + tdb_traverse(idmap_tdb, net_idmap_find_max_id, &hwms); + + if (!hwms.ok) { + goto done; + } + + d_printf("USER HWM: %d GROUP HWM: %d\n", + hwms.user_hwm, hwms.group_hwm); + + if (hwms.user_hwm >= highest.user_hwm) { + d_printf("Highest UID out of uid range\n"); + goto done; + } + + if (hwms.group_hwm >= highest.group_hwm) { + d_printf("Highest GID out of gid range\n"); + goto done; + } + + if ((tdb_store_int32(idmap_tdb, "USER HWM", hwms.user_hwm) != 0) || + (tdb_store_int32(idmap_tdb, "GROUP HWM", hwms.group_hwm) != 0)) { + d_printf("Could not store HWMs\n"); + goto done; + } + + result = NT_STATUS_OK; + done: + tdb_close(idmap_tdb); + return result; +} + +/*********************************************************** + Write entries from stdin to current local idmap + **********************************************************/ +static int net_idmap_restore(int argc, const char **argv) +{ + if (!idmap_init(lp_idmap_backend())) { + d_printf("Could not init idmap\n"); + return -1; + } + + while (!feof(stdin)) { + fstring line, sid_string; + int len; + unid_t id; + int type = ID_EMPTY; + DOM_SID sid; + + if (fgets(line, sizeof(line)-1, stdin) == NULL) + break; + + len = strlen(line); + + if ( (len > 0) && (line[len-1] == '\n') ) + line[len-1] = '\0'; + + /* Yuck - this is broken for sizeof(gid_t) != sizeof(int) */ + + if (sscanf(line, "GID %d %s", &id.gid, sid_string) == 2) { + type = ID_GROUPID; + } + + /* Yuck - this is broken for sizeof(uid_t) != sizeof(int) */ + + if (sscanf(line, "UID %d %s", &id.uid, sid_string) == 2) { + type = ID_USERID; + } + + if (type == ID_EMPTY) { + d_printf("ignoring invalid line [%s]\n", line); + continue; + } + + if (!string_to_sid(&sid, sid_string)) { + d_printf("ignoring invalid sid [%s]\n", sid_string); + continue; + } + + if (!NT_STATUS_IS_OK(idmap_set_mapping(&sid, id, type))) { + d_printf("Could not set mapping of %s %lu to sid %s\n", + (type == ID_GROUPID) ? "GID" : "UID", + (type == ID_GROUPID) ? (unsigned long)id.gid: + (unsigned long)id.uid, + sid_string_static(&sid)); + continue; + } + + } + + idmap_close(); + + return NT_STATUS_IS_OK(net_idmap_fixup_hwm()) ? 0 : -1; +} + +int net_help_idmap(int argc, const char **argv) +{ + d_printf("net idmap dump filename"\ + "\n Dump current id mapping\n"); + + d_printf("net idmap restore"\ + "\n Restore entries from stdin to current local idmap\n"); + + return -1; +} + +/*********************************************************** + Look at the current idmap + **********************************************************/ +int net_idmap(int argc, const char **argv) +{ + struct functable func[] = { + {"dump", net_idmap_dump}, + {"restore", net_idmap_restore}, + {"help", net_help_idmap}, + {NULL, NULL} + }; + + return net_run_function(argc, argv, func, net_help_idmap); +} + + diff --git a/source/utils/net_lookup.c b/source/utils/net_lookup.c new file mode 100644 index 00000000000..cef0ea5fbed --- /dev/null +++ b/source/utils/net_lookup.c @@ -0,0 +1,258 @@ +/* + 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; + const char *name = argv[0]; + char *p; + + if (argc == 0) + return net_lookup_usage(argc, argv); + + p = strchr_m(name,'#'); + if (p) { + *p = '\0'; + sscanf(++p,"%x",&name_type); + } + + if (!resolve_name(name, &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", name, 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 ip_service *ip_list; + struct in_addr addr; + char *pdc_str = NULL; + const char *domain=opt_target_workgroup; + int count, i; + + 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_sorted_dc_list(domain, &ip_list, &count, False)) { + SAFE_FREE(pdc_str); + return 0; + } + for (i=0;i<count;i++) { + char *dc_str = inet_ntoa(ip_list[i].ip); + 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) +{ + int i; + + struct functable table[] = { + {"HOST", net_lookup_host}, + {"LDAP", net_lookup_ldap}, + {"DC", net_lookup_dc}, + {"MASTER", net_lookup_master}, + {"KDC", net_lookup_kdc}, + {NULL, NULL} + }; + + if (argc < 1) { + d_printf("\nUsage: \n"); + return net_lookup_usage(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); + } + + /* Default to lookup a hostname so 'net lookup foo#1b' can be + used instead of 'net lookup host foo#1b'. The host syntax + is a bit confusing as non #00 names can't really be + considered hosts as such. */ + + return net_lookup_host(argc, argv); +} diff --git a/source/utils/net_privileges.c b/source/utils/net_privileges.c new file mode 100644 index 00000000000..95a3326ce3e --- /dev/null +++ b/source/utils/net_privileges.c @@ -0,0 +1,362 @@ +/* + * Unix SMB/CIFS implementation. + * RPC Pipe client / server routines + * Copyright (C) Andrew Tridgell 1992-2000, + * Copyright (C) Jean François Micouleau 1998-2001. + * Copyright (C) Gerald Carter 2003. + * Copyright (C) Simo Sorce 2003. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 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 PRIVS privs[]; + +/********************************************************* + utility function to parse an integer parameter from + "parameter = value" +**********************************************************/ +static uint32 get_int_param( const char* param ) +{ + char *p; + + p = strchr( param, '=' ); + if ( !p ) + return 0; + + return atoi(p+1); +} + +/********************************************************* + utility function to parse an integer parameter from + "parameter = value" +**********************************************************/ +static char* get_string_param( const char* param ) +{ + char *p; + + p = strchr( param, '=' ); + if ( !p ) + return NULL; + + return (p+1); +} + +/********************************************************* + Dump a GROUP_MAP entry to stdout (long or short listing) +**********************************************************/ + +static void print_priv_entry(const char *privname, const char *description, const char *sid_list) +{ + d_printf("%s\n", privname); + + if (description) { + d_printf("\tdescription: %s\n", description); + } + + if (sid_list) { + d_printf("\tSIDs: %s\n", sid_list); + } else { + d_printf("\tNo SIDs in this privilege\n"); + } +} + +/********************************************************* + List the groups. +**********************************************************/ +static int net_priv_list(int argc, const char **argv) +{ + fstring privname = ""; + fstring sid_string = ""; + int i; + BOOL verbose = False; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if (StrnCaseCmp(argv[i], "privname", strlen("privname")) == 0) { + fstrcpy(privname, get_string_param(argv[i])); + if (!privname[0]) { + d_printf("must supply a name\n"); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) { + fstrcpy(sid_string, get_string_param(argv[i])); + if (!sid_string[0]) { + d_printf("must supply a SID\n"); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "verbose", strlen("verbose")) == 0) { + verbose = True; + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if (sid_string[0] != '\0') { + /* list all privileges of a single sid */ + + } else { + char *sid_list = NULL; + + if (privname[0] != '\0') { + const char *description = NULL; + + BOOL found = False; + + for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) { + if (StrCaseCmp(privs[i].priv, privname) == 0) { + description = privs[i].description; + found = True; + break; + } + } + if (!found) { + d_printf("No such privilege!\n"); + return -1; + } + + /* Get the current privilege from the database */ + pdb_get_privilege_entry(privname, &sid_list); + print_priv_entry(privname, description, sid_list); + + SAFE_FREE(sid_list); + + } else for (i=0; privs[i].se_priv != SE_ALL_PRIVS; i++) { + + if (!pdb_get_privilege_entry(privs[i].priv, &sid_list)) { + if (!verbose) + continue; + + sid_list = NULL; + } + + print_priv_entry(privs[i].priv, privs[i].description, sid_list); + + SAFE_FREE(sid_list); + } + } + + return 0; +} + +/********************************************************* + Add a sid to a privilege entry +**********************************************************/ + +static int net_priv_add(int argc, const char **argv) +{ + DOM_SID sid; + fstring privname = ""; + fstring sid_string = ""; + uint32 rid = 0; + int i; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) { + rid = get_int_param(argv[i]); + if (rid < DOMAIN_GROUP_RID_ADMINS) { + d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) { + BOOL found = False; + int j; + + fstrcpy(privname, get_string_param(argv[i])); + if (!privname[0]) { + d_printf("must supply a name\n"); + return -1; + } + for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) { + if (StrCaseCmp(privs[j].priv, privname) == 0) { + found = True; + } + } + if (!found) { + d_printf("unknown privilege name"); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) { + fstrcpy(sid_string, get_string_param(argv[i])); + if (!sid_string[0]) { + d_printf("must supply a SID\n"); + return -1; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if (privname[0] == '\0') { + d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n"); + return -1; + } + + if ((rid == 0) && (sid_string[0] == '\0')) { + d_printf("No rid or sid specified\n"); + d_printf("Usage: net priv add {rid=<int>|sid=<string>} privilege=<string>\n"); + return -1; + } + + /* append the rid to our own domain/machine SID if we don't have a full SID */ + if (sid_string[0] == '\0') { + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, rid); + sid_to_string(sid_string, &sid); + } else { + string_to_sid(&sid, sid_string); + } + + if (!pdb_add_sid_to_privilege(privname, &sid)) { + d_printf("adding sid %s to privilege %s failed!\n", sid_string, privname); + return -1; + } + + d_printf("Successully added SID %s to privilege %s\n", sid_string, privname); + return 0; +} + +/********************************************************* + Remove a SID froma privilege entry +**********************************************************/ + +static int net_priv_remove(int argc, const char **argv) +{ + DOM_SID sid; + fstring privname = ""; + fstring sid_string = ""; + uint32 rid = 0; + int i; + + /* get the options */ + for ( i=0; i<argc; i++ ) { + if (StrnCaseCmp(argv[i], "rid", strlen("rid")) == 0) { + rid = get_int_param(argv[i]); + if (rid < DOMAIN_GROUP_RID_ADMINS) { + d_printf("RID must be greater than %d\n", (uint32)DOMAIN_GROUP_RID_ADMINS-1); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "privilege", strlen("privilege")) == 0) { + BOOL found = False; + int j; + + fstrcpy(privname, get_string_param(argv[i])); + if (!privname[0]) { + d_printf("must supply a name\n"); + return -1; + } + for (j=0; privs[j].se_priv != SE_ALL_PRIVS; j++) { + if (StrCaseCmp(privs[j].priv, privname) == 0) { + found = True; + } + } + if (!found) { + d_printf("unknown privilege name"); + return -1; + } + } + else if (StrnCaseCmp(argv[i], "sid", strlen("sid")) == 0) { + fstrcpy(sid_string, get_string_param(argv[i])); + if (!sid_string[0]) { + d_printf("must supply a SID\n"); + return -1; + } + } + else { + d_printf("Bad option: %s\n", argv[i]); + return -1; + } + } + + if (privname[0] == '\0') { + d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n"); + return -1; + } + + if ((rid == 0) && (sid_string[0] == '\0')) { + d_printf("No rid or sid specified\n"); + d_printf("Usage: net priv remove {rid=<int>|sid=<string>} privilege=<string>\n"); + return -1; + } + + /* append the rid to our own domain/machine SID if we don't have a full SID */ + if (sid_string[0] == '\0') { + sid_copy(&sid, get_global_sam_sid()); + sid_append_rid(&sid, rid); + sid_to_string(sid_string, &sid); + } else { + string_to_sid(&sid, sid_string); + } + + if (!pdb_remove_sid_from_privilege(privname, &sid)) { + d_printf("removing sid %s from privilege %s failed!\n", sid_string, privname); + return -1; + } + + d_printf("Successully removed SID %s from privilege %s\n", sid_string, privname); + return 0; +} + +int net_help_priv(int argc, const char **argv) +{ + d_printf("net priv add sid\n" \ + " Add sid to privilege\n"); + d_printf("net priv remove sid\n"\ + " Remove sid from privilege\n"); + d_printf("net priv list\n"\ + " List sids per privilege\n"); + + return -1; +} + + +/*********************************************************** + migrated functionality from smbgroupedit + **********************************************************/ +int net_priv(int argc, const char **argv) +{ + struct functable func[] = { + {"add", net_priv_add}, + {"remove", net_priv_remove}, + {"list", net_priv_list}, + {"help", net_help_priv}, + {NULL, NULL} + }; + + /* we shouldn't have silly checks like this */ + if (getuid() != 0) { + d_printf("You must be root to edit privilege mappings.\nExiting...\n"); + return -1; + } + + if ( argc ) + return net_run_function(argc, argv, func, net_help_priv); + + return net_help_priv(argc, argv); +} + diff --git a/source/utils/net_rap.c b/source/utils/net_rap.c new file mode 100644 index 00000000000..39254641abf --- /dev/null +++ b/source/utils/net_rap.c @@ -0,0 +1,1054 @@ +/* + 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("%-12s %-8.8s %-50s\n", + share_name, share_type[type], comment); +} + +static void share_fn(const char *share_name, uint32 type, + const char *comment, void *state) +{ + d_printf("%s\n", share_name); +} + +static int rap_share_delete(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); + } else { + 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"); + d_printf( + "\nnet rap session INFO <client_name>"\ + "\n\tEnumerates all open files in specified session\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); + + 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 %s\n", + user_name, comment); +} + +static void group_member_fn(const char *user_name, void *state) +{ + d_printf("%-21.21s\n", user_name); +} + +static int rap_user_delete(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)-1); + 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 %s\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)-1); + 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/source/utils/net_rpc.c b/source/utils/net_rpc.c new file mode 100644 index 00000000000..afb94a616a3 --- /dev/null +++ b/source/utils/net_rpc.c @@ -0,0 +1,3396 @@ +/* + 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 *, const char *, + 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, TALLOC_CTX *mem_ctx, char **domain_name) +{ + DOM_SID *domain_sid; + POLICY_HND pol; + NTSTATUS result = NT_STATUS_OK; + uint32 info_class = 5; + + 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, False, + 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)) { + 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); + } + + cli_lsa_close(cli, mem_ctx, &pol); + cli_nt_session_close(cli); + + return domain_sid; +} + +/** + * 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; + char *domain_name; + + /* 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; + } + + /* Create mem_ctx */ + + if (!(mem_ctx = talloc_init("run_rpc_command"))) { + DEBUG(0, ("talloc_init() failed\n")); + cli_shutdown(cli); + return -1; + } + + domain_sid = net_get_remote_domain_sid(cli, mem_ctx, &domain_name); + + if (!(conn_flags & NET_FLAGS_NO_PIPE)) { + if (!cli_nt_session_open(cli, pipe_idx)) { + DEBUG(0, ("Could not initialise pipe\n")); + } + } + + nt_status = fn(domain_sid, domain_name, 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 (!(conn_flags & NET_FLAGS_NO_PIPE)) { + 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, const char *domain_name, + 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) + **/ + +int net_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 equiv 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_oldjoin_internals(const DOM_SID *domain_sid, const char *domain_name, + 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; + uint32 sec_channel_type; + + /* + check what type of join - if the user want's to join as + a BDC, the server must agree that we are a BDC. + */ + if (argc >= 0) { + sec_channel_type = get_sec_channel_type(argv[0]); + } else { + sec_channel_type = get_sec_channel_type(NULL); + } + + fstrcpy(trust_passwd, global_myname()); + strlower_m(trust_passwd); + + /* + * Machine names can be 15 characters, but the max length on + * a password is 14. --jerry + */ + + trust_passwd[14] = '\0'; + + E_md4hash(trust_passwd, orig_trust_passwd_hash); + + result = trust_pw_change_and_store_it(cli, mem_ctx, opt_target_workgroup, + orig_trust_passwd_hash, + sec_channel_type); + + if (NT_STATUS_IS_OK(result)) + printf("Joined domain %s.\n",opt_target_workgroup); + + + if (!secrets_store_domain_sid(opt_target_workgroup, domain_sid)) { + DEBUG(0, ("error storing domain sid for %s\n", opt_target_workgroup)); + result = NT_STATUS_UNSUCCESSFUL; + } + + 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_perform_oldjoin(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_NETLOGON, + NET_FLAGS_ANONYMOUS | NET_FLAGS_PDC, + rpc_oldjoin_internals, + argc, argv); +} + +/** + * Join a domain, the old way. This function exists to allow + * the message to be displayed when oldjoin was explicitly + * requested, but not when it was implied by "net rpc join" + * + * @param argc Standard main() style argc + * @param argc Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int net_rpc_oldjoin(int argc, const char **argv) +{ + int rc = net_rpc_perform_oldjoin(argc, argv); + + if (rc) { + d_printf("Failed to join domain\n"); + } + + return rc; +} + +/** + * 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] <type>[options]\n"\ + "\t to join a domain with admin username & password\n"\ + "\t\t password will be prompted if needed and none is specified\n"\ + "\t <type> can be (default MEMBER)\n"\ + "\t\t BDC - Join as a BDC\n"\ + "\t\t PDC - Join as a PDC\n"\ + "\t\t MEMBER - Join as a MEMBER server\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 + * Try to just change the password, but if that doesn't work, use/prompt + * for a username/password. + **/ + +int net_rpc_join(int argc, const char **argv) +{ + if ((net_rpc_perform_oldjoin(argc, argv) == 0)) + return 0; + + return net_rpc_join_newstyle(argc, argv); +} + + + +/** + * display info about a rpc domain + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, 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, const char *domain_name, + 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, const char *domain_name, + 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, domain_name); + + if (!secrets_store_domain_sid(domain_name, domain_sid)) { + DEBUG(0,("Can't store domain SID\n")); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + + +/** + * 'net rpc getsid' entrypoint. + * @param argc Standard main() style argc + * @param 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, const char *domain_name, + 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, + const char *domain_name, + 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); +} + +/** + * Set a password for a user 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_password_internals(const DOM_SID *domain_sid, + const char *domain_name, + 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; + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + uchar pwbuf[516]; + const char *user; + const char *new_password; + char *prompt = NULL; + + if (argc < 1) { + d_printf("User must be specified\n"); + rpc_user_usage(argc, argv); + return NT_STATUS_OK; + } + + user = argv[0]; + + if (argv[1]) { + new_password = argv[1]; + } else { + asprintf(&prompt, "Enter new password for %s:", user); + new_password = getpass(prompt); + SAFE_FREE(prompt); + } + + /* 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, &user, + &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; + } + } + + /* Set password on account */ + + ZERO_STRUCT(ctr); + ZERO_STRUCT(p24); + + encode_pw_buffer(pwbuf, new_password, STR_UNICODE); + + init_sam_user_info24(&p24, (char *)pwbuf,24); + + ctr.switch_value = 24; + ctr.info.id24 = &p24; + + result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24, + &cli->user_session_key, &ctr); + + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + /* Display results */ + + done: + return result; + +} + +/** + * Set a user's password on a remote RPC server + * + * @param argc Standard main() style argc + * @param argv Standard main() style argv. Initial components are already + * stripped + * + * @return A shell status integer (0 for success) + **/ + +static int rpc_user_password(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, rpc_user_password_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, const char *domain_name, + 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, const char *domain_name, + 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 %s\n", user, desc); + else + printf("%s\n", user); + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + 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}, + {"password", rpc_user_password}, + {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); +} + +static NTSTATUS +rpc_group_add_internals(const DOM_SID *domain_sid, const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol, group_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + GROUP_INFO_CTR group_info; + + if (argc != 1) { + d_printf("Group name must be specified\n"); + rpc_group_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; + + /* Create the group */ + + result = cli_samr_create_dom_group(cli, mem_ctx, &domain_pol, + argv[0], MAXIMUM_ALLOWED_ACCESS, + &group_pol); + if (!NT_STATUS_IS_OK(result)) goto done; + + if (strlen(opt_comment) == 0) goto done; + + /* We've got a comment to set */ + + group_info.switch_value1 = 4; + init_samr_group_info4(&group_info.group.info4, opt_comment); + + result = cli_samr_set_groupinfo(cli, mem_ctx, &group_pol, &group_info); + if (!NT_STATUS_IS_OK(result)) goto done; + + done: + if (NT_STATUS_IS_OK(result)) + DEBUG(5, ("add group succeeded\n")); + else + d_printf("add group failed: %s\n", nt_errstr(result)); + + return result; +} + +static NTSTATUS +rpc_alias_add_internals(const DOM_SID *domain_sid, const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + POLICY_HND connect_pol, domain_pol, alias_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + ALIAS_INFO_CTR alias_info; + + if (argc != 1) { + d_printf("Group name must be specified\n"); + rpc_group_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; + + /* Create the group */ + + result = cli_samr_create_dom_alias(cli, mem_ctx, &domain_pol, + argv[0], &alias_pol); + if (!NT_STATUS_IS_OK(result)) goto done; + + if (strlen(opt_comment) == 0) goto done; + + /* We've got a comment to set */ + + alias_info.switch_value1 = 3; + alias_info.switch_value2 = 3; + init_samr_alias_info3(&alias_info.alias.info3, opt_comment); + + result = cli_samr_set_aliasinfo(cli, mem_ctx, &alias_pol, &alias_info); + if (!NT_STATUS_IS_OK(result)) goto done; + + done: + if (NT_STATUS_IS_OK(result)) + DEBUG(5, ("add group succeeded\n")); + else + d_printf("add group failed: %s\n", nt_errstr(result)); + + return result; +} + +static int rpc_group_add(int argc, const char **argv) +{ + if (opt_localgroup) + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_alias_add_internals, + argc, argv); + + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_add_internals, + argc, argv); +} + +static NTSTATUS +get_sid_from_name(struct cli_state *cli, TALLOC_CTX *mem_ctx, const char *name, + DOM_SID *sid, enum SID_NAME_USE *type) +{ + int current_pipe = cli->pipe_idx; + + DOM_SID *sids = NULL; + uint32 *types = NULL; + POLICY_HND lsa_pol; + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + if (current_pipe != PI_LSARPC) { + + if (current_pipe != -1) + cli_nt_session_close(cli); + + if (!cli_nt_session_open(cli, PI_LSARPC)) + goto done; + } + + result = cli_lsa_open_policy(cli, mem_ctx, False, + SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = cli_lsa_lookup_names(cli, mem_ctx, &lsa_pol, 1, + &name, &sids, &types); + + if (NT_STATUS_IS_OK(result)) { + sid_copy(sid, &sids[0]); + *type = types[0]; + } + + cli_lsa_close(cli, mem_ctx, &lsa_pol); + + done: + if (current_pipe != PI_LSARPC) { + cli_nt_session_close(cli); + if (current_pipe != -1) + cli_nt_session_open(cli, current_pipe); + } + + if (!NT_STATUS_IS_OK(result) && (StrnCaseCmp(name, "S-", 2) == 0)) { + + /* Try as S-1-5-whatever */ + + DOM_SID tmp_sid; + + if (string_to_sid(&tmp_sid, name)) { + sid_copy(sid, &tmp_sid); + *type = SID_NAME_UNKNOWN; + result = NT_STATUS_OK; + } + } + + return result; +} + +static NTSTATUS +rpc_add_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, const char *member) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result; + uint32 group_rid; + POLICY_HND group_pol; + + uint32 num_rids; + uint32 *rids = NULL; + uint32 *rid_types = NULL; + + DOM_SID sid; + + sid_copy(&sid, group_sid); + + if (!sid_split_rid(&sid, &group_rid)) + return NT_STATUS_UNSUCCESSFUL; + + /* Get sam policy handle */ + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Get domain policy handle */ + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000, + 1, &member, + &num_rids, &rids, &rid_types); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not lookup up group member %s\n", member); + goto done; + } + + result = cli_samr_open_group(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rid, &group_pol); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = cli_samr_add_groupmem(cli, mem_ctx, &group_pol, rids[0]); + + done: + cli_samr_close(cli, mem_ctx, &connect_pol); + return result; +} + +static NTSTATUS +rpc_add_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const DOM_SID *alias_sid, const char *member) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result; + uint32 alias_rid; + POLICY_HND alias_pol; + + DOM_SID member_sid; + enum SID_NAME_USE member_type; + + DOM_SID sid; + + sid_copy(&sid, alias_sid); + + if (!sid_split_rid(&sid, &alias_rid)) + return NT_STATUS_UNSUCCESSFUL; + + result = get_sid_from_name(cli, mem_ctx, member, + &member_sid, &member_type); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not lookup up group member %s\n", member); + return result; + } + + /* 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, + &sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = cli_samr_open_alias(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + alias_rid, &alias_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_add_aliasmem(cli, mem_ctx, &alias_pol, &member_sid); + + if (!NT_STATUS_IS_OK(result)) + return result; + + done: + cli_samr_close(cli, mem_ctx, &connect_pol); + return result; +} + +static NTSTATUS +rpc_group_addmem_internals(const DOM_SID *domain_sid, const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + DOM_SID group_sid; + enum SID_NAME_USE group_type; + + if (argc != 2) { + d_printf("Usage: 'net rpc group addmem <group> <member>\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], + &group_sid, &group_type))) { + d_printf("Could not lookup group name %s\n", argv[0]); + return NT_STATUS_UNSUCCESSFUL; + } + + if (group_type == SID_NAME_DOM_GRP) { + NTSTATUS result = rpc_add_groupmem(cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not add %s to %s: %s\n", + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + if (group_type == SID_NAME_ALIAS) { + NTSTATUS result = rpc_add_aliasmem(cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not add %s to %s: %s\n", + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + d_printf("Can only add members to global or local groups which " + "%s is not\n", argv[0]); + + return NT_STATUS_UNSUCCESSFUL; +} + +static int rpc_group_addmem(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_addmem_internals, + argc, argv); +} + +static NTSTATUS +rpc_del_groupmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const DOM_SID *group_sid, const char *member) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result; + uint32 group_rid; + POLICY_HND group_pol; + + uint32 num_rids; + uint32 *rids = NULL; + uint32 *rid_types = NULL; + + DOM_SID sid; + + sid_copy(&sid, group_sid); + + if (!sid_split_rid(&sid, &group_rid)) + return NT_STATUS_UNSUCCESSFUL; + + /* Get sam policy handle */ + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + /* Get domain policy handle */ + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000, + 1, &member, + &num_rids, &rids, &rid_types); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not lookup up group member %s\n", member); + goto done; + } + + result = cli_samr_open_group(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + group_rid, &group_pol); + + if (!NT_STATUS_IS_OK(result)) + goto done; + + result = cli_samr_del_groupmem(cli, mem_ctx, &group_pol, rids[0]); + + done: + cli_samr_close(cli, mem_ctx, &connect_pol); + return result; +} + +static NTSTATUS +rpc_del_aliasmem(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const DOM_SID *alias_sid, const char *member) +{ + POLICY_HND connect_pol, domain_pol; + NTSTATUS result; + uint32 alias_rid; + POLICY_HND alias_pol; + + DOM_SID member_sid; + enum SID_NAME_USE member_type; + + DOM_SID sid; + + sid_copy(&sid, alias_sid); + + if (!sid_split_rid(&sid, &alias_rid)) + return NT_STATUS_UNSUCCESSFUL; + + result = get_sid_from_name(cli, mem_ctx, member, + &member_sid, &member_type); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not lookup up group member %s\n", member); + return result; + } + + /* 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, + &sid, &domain_pol); + if (!NT_STATUS_IS_OK(result)) { + goto done; + } + + result = cli_samr_open_alias(cli, mem_ctx, &domain_pol, + MAXIMUM_ALLOWED_ACCESS, + alias_rid, &alias_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_del_aliasmem(cli, mem_ctx, &alias_pol, &member_sid); + + if (!NT_STATUS_IS_OK(result)) + return result; + + done: + cli_samr_close(cli, mem_ctx, &connect_pol); + return result; +} + +static NTSTATUS +rpc_group_delmem_internals(const DOM_SID *domain_sid, const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + DOM_SID group_sid; + enum SID_NAME_USE group_type; + + if (argc != 2) { + d_printf("Usage: 'net rpc group delmem <group> <member>\n"); + return NT_STATUS_UNSUCCESSFUL; + } + + if (!NT_STATUS_IS_OK(get_sid_from_name(cli, mem_ctx, argv[0], + &group_sid, &group_type))) { + d_printf("Could not lookup group name %s\n", argv[0]); + return NT_STATUS_UNSUCCESSFUL; + } + + if (group_type == SID_NAME_DOM_GRP) { + NTSTATUS result = rpc_del_groupmem(cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not del %s from %s: %s\n", + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + if (group_type == SID_NAME_ALIAS) { + NTSTATUS result = rpc_del_aliasmem(cli, mem_ctx, + &group_sid, argv[1]); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Could not del %s from %s: %s\n", + argv[1], argv[0], nt_errstr(result)); + } + return result; + } + + d_printf("Can only delete members from global or local groups which " + "%s is not\n", argv[0]); + + return NT_STATUS_UNSUCCESSFUL; +} + +static int rpc_group_delmem(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_delmem_internals, + argc, argv); +} + +/** + * List groups on a remote RPC server + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passes through. + * + * @param domain_sid The domain sid acquired from the remote server + * @param cli A cli_state connected to the server. + * @param mem_ctx Talloc context, 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, const char *domain_name, + 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, loop_count = 0; + struct acct_info *groups; + DOM_SID global_sid_Builtin; + BOOL global = False; + BOOL local = False; + BOOL builtin = False; + + if (argc == 0) { + global = True; + local = True; + builtin = True; + } + + for (i=0; i<argc; i++) { + if (strequal(argv[i], "global")) + global = True; + + if (strequal(argv[i], "local")) + local = True; + + if (strequal(argv[i], "builtin")) + builtin = True; + } + + 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 { + SAM_DISPINFO_CTR ctr; + SAM_DISPINFO_3 info3; + uint32 max_size; + + ZERO_STRUCT(ctr); + ZERO_STRUCT(info3); + ctr.sam.info3 = &info3; + + if (!global) break; + + get_query_dispinfo_params( + loop_count, &max_entries, &max_size); + + result = cli_samr_query_dispinfo(cli, mem_ctx, &domain_pol, + &start_idx, 3, &num_entries, + max_entries, max_size, &ctr); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + for (i = 0; i < num_entries; i++) { + + fstring group, desc; + + unistr2_to_ascii(group, &(&ctr.sam.info3->str[i])->uni_grp_name, sizeof(group)-1); + unistr2_to_ascii(desc, &(&ctr.sam.info3->str[i])->uni_grp_desc, sizeof(desc)-1); + + if (opt_long_list_entries) + printf("%-21.21s %-50.50s\n", + group, desc); + else + printf("%s\n", group); + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + /* query domain aliases */ + start_idx = 0; + do { + if (!local) break; + + result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol, + &start_idx, max_entries, + &groups, &num_entries); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + for (i = 0; i < num_entries; i++) { + + char *description = NULL; + + if (opt_long_list_entries) { + + POLICY_HND alias_pol; + ALIAS_INFO_CTR ctr; + + if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx, + &domain_pol, + 0x8, + groups[i].rid, + &alias_pol))) && + (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx, + &alias_pol, 3, + &ctr))) && + (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx, + &alias_pol)))) { + description = unistr2_tdup(mem_ctx, + &ctr.alias.info3.uni_acct_desc); + } + } + + if (description != NULL) { + printf("%-21.21s %-50.50s\n", + groups[i].acct_name, + description); + } else { + printf("%s\n", groups[i].acct_name); + } + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + 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 */ + start_idx = 0; + do { + if (!builtin) break; + + result = cli_samr_enum_als_groups(cli, mem_ctx, &domain_pol, + &start_idx, max_entries, + &groups, &num_entries); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) + break; + + for (i = 0; i < num_entries; i++) { + + char *description = NULL; + + if (opt_long_list_entries) { + + POLICY_HND alias_pol; + ALIAS_INFO_CTR ctr; + + if ((NT_STATUS_IS_OK(cli_samr_open_alias(cli, mem_ctx, + &domain_pol, + 0x8, + groups[i].rid, + &alias_pol))) && + (NT_STATUS_IS_OK(cli_samr_query_alias_info(cli, mem_ctx, + &alias_pol, 3, + &ctr))) && + (NT_STATUS_IS_OK(cli_samr_close(cli, mem_ctx, + &alias_pol)))) { + description = unistr2_tdup(mem_ctx, + &ctr.alias.info3.uni_acct_desc); + } + } + + if (description != NULL) { + printf("%-21.21s %-50.50s\n", + groups[i].acct_name, + description); + } else { + printf("%s\n", groups[i].acct_name); + } + } + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + done: + return result; +} + +static int rpc_group_list(int argc, const char **argv) +{ + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_list_internals, + argc, argv); +} + +static NTSTATUS +rpc_list_group_members(struct cli_state *cli, TALLOC_CTX *mem_ctx, + const char *domain_name, const DOM_SID *domain_sid, + POLICY_HND *domain_pol, uint32 rid) +{ + NTSTATUS result; + POLICY_HND group_pol; + uint32 num_members, *group_rids, *group_attrs; + uint32 num_names; + char **names; + uint32 *name_types; + int i; + + fstring sid_str; + sid_to_string(sid_str, domain_sid); + + result = cli_samr_open_group(cli, mem_ctx, domain_pol, + MAXIMUM_ALLOWED_ACCESS, + rid, &group_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_query_groupmem(cli, mem_ctx, &group_pol, + &num_members, &group_rids, + &group_attrs); + + if (!NT_STATUS_IS_OK(result)) + return result; + + while (num_members > 0) { + int this_time = 512; + + if (num_members < this_time) + this_time = num_members; + + result = cli_samr_lookup_rids(cli, mem_ctx, domain_pol, 1000, + this_time, group_rids, + &num_names, &names, &name_types); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* We only have users as members, but make the output + the same as the output of alias members */ + + for (i = 0; i < this_time; i++) { + + if (opt_long_list_entries) { + printf("%s-%d %s\\%s %d\n", sid_str, + group_rids[i], domain_name, names[i], + SID_NAME_USER); + } else { + printf("%s\\%s\n", domain_name, names[i]); + } + } + + num_members -= this_time; + group_rids += 512; + } + + return NT_STATUS_OK; +} + +static NTSTATUS +rpc_list_alias_members(struct cli_state *cli, TALLOC_CTX *mem_ctx, + POLICY_HND *domain_pol, uint32 rid) +{ + NTSTATUS result; + POLICY_HND alias_pol, lsa_pol; + uint32 num_members; + DOM_SID *alias_sids; + char **domains; + char **names; + uint32 *types; + int i; + + result = cli_samr_open_alias(cli, mem_ctx, domain_pol, + MAXIMUM_ALLOWED_ACCESS, rid, &alias_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + result = cli_samr_query_aliasmem(cli, mem_ctx, &alias_pol, + &num_members, &alias_sids); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Couldn't list alias members\n"); + return result; + } + + if (num_members == 0) { + return NT_STATUS_OK; + } + + cli_nt_session_close(cli); + + if (!cli_nt_session_open(cli, PI_LSARPC)) { + d_printf("Couldn't open LSA pipe\n"); + return result; + } + + result = cli_lsa_open_policy(cli, mem_ctx, True, + SEC_RIGHTS_MAXIMUM_ALLOWED, &lsa_pol); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Couldn't open LSA policy handle\n"); + return result; + } + + result = cli_lsa_lookup_sids(cli, mem_ctx, &lsa_pol, num_members, + alias_sids, + &domains, &names, &types); + + if (!NT_STATUS_IS_OK(result) && + !NT_STATUS_EQUAL(result, STATUS_SOME_UNMAPPED)) { + d_printf("Couldn't lookup SIDs\n"); + return result; + } + + for (i = 0; i < num_members; i++) { + fstring sid_str; + sid_to_string(sid_str, &alias_sids[i]); + + if (opt_long_list_entries) { + printf("%s %s\\%s %d\n", sid_str, + domains[i] ? domains[i] : "*unknown*", + names[i] ? names[i] : "*unknown*", types[i]); + } else { + if (domains[i]) + printf("%s\\%s\n", domains[i], names[i]); + else + printf("%s\n", sid_str); + } + } + + return NT_STATUS_OK; +} + +static NTSTATUS +rpc_group_members_internals(const DOM_SID *domain_sid, + const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, int argc, const char **argv) +{ + NTSTATUS result; + POLICY_HND connect_pol, domain_pol; + uint32 num_rids, *rids, *rid_types; + + /* Get sam policy handle */ + + result = cli_samr_connect(cli, mem_ctx, MAXIMUM_ALLOWED_ACCESS, + &connect_pol); + + if (!NT_STATUS_IS_OK(result)) + return result; + + /* 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)) + return result; + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000, + 1, argv, &num_rids, &rids, &rid_types); + + if (!NT_STATUS_IS_OK(result)) { + + /* Ok, did not find it in the global sam, try with builtin */ + + DOM_SID sid_Builtin; + + cli_samr_close(cli, mem_ctx, &domain_pol); + + string_to_sid(&sid_Builtin, "S-1-5-32"); + + result = cli_samr_open_domain(cli, mem_ctx, &connect_pol, + MAXIMUM_ALLOWED_ACCESS, + &sid_Builtin, &domain_pol); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Couldn't find group %s\n", argv[0]); + return result; + } + + result = cli_samr_lookup_names(cli, mem_ctx, &domain_pol, 1000, + 1, argv, &num_rids, + &rids, &rid_types); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Couldn't find group %s\n", argv[0]); + return result; + } + } + + if (num_rids != 1) { + d_printf("Couldn't find group %s\n", argv[0]); + return result; + } + + if (rid_types[0] == SID_NAME_DOM_GRP) { + return rpc_list_group_members(cli, mem_ctx, domain_name, + domain_sid, &domain_pol, + rids[0]); + } + + if (rid_types[0] == SID_NAME_ALIAS) { + return rpc_list_alias_members(cli, mem_ctx, &domain_pol, + rids[0]); + } + + return NT_STATUS_NO_SUCH_GROUP; +} + +static int rpc_group_members(int argc, const char **argv) +{ + if (argc != 1) { + return rpc_group_usage(argc, argv); + } + + return run_rpc_command(NULL, PI_SAMR, 0, + rpc_group_members_internals, + argc, argv); +} + +/** + * '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[] = { + {"add", rpc_group_add}, + {"addmem", rpc_group_addmem}, + {"delmem", rpc_group_delmem}, +#if 0 + {"delete", rpc_group_delete}, +#endif + {"list", rpc_group_list}, + {"members", rpc_group_members}, + {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, const char *domain_name, + 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, const char *domain_name, + 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("%-12s %-8.8s %-50s\n", + netname, share_type[info1->info_1.type], remark); + } else { + d_printf("%s\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, const char *domain_name, + 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, const char *domain_name, + 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, const char *domain_name, + 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 over, initshutdown pipe + * + * 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, + const char *domain_name, + struct cli_state *cli, + TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS result = NT_STATUS_UNSUCCESSFUL; + + result = cli_shutdown_abort(cli, mem_ctx); + + if (NT_STATUS_IS_OK(result)) + DEBUG(5,("cmd_shutdown_abort: query succeeded\n")); + else + DEBUG(5,("cmd_shutdown_abort: query failed\n")); + + return result; +} + + +/** + * ABORT the shutdown of a remote RPC Server, over winreg pipe + * + * All parameters are provided by the run_rpc_command function, except for + * argc, argv which are passed through. + * + * @param 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_reg_shutdown_abort_internals(const DOM_SID *domain_sid, + const char *domain_name, + 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) +{ + int rc = run_rpc_command(NULL, PI_SHUTDOWN, 0, + rpc_shutdown_abort_internals, + argc, argv); + + if (rc == 0) + return rc; + + DEBUG(1, ("initshutdown pipe didn't work, trying winreg pipe\n")); + + return run_rpc_command(NULL, PI_WINREG, 0, + rpc_reg_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, + const char *domain_name, + 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, + const char *domain_name, + 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 != 2) { + d_printf("Usage: net rpc trustdom add <domain_name> <pw>\n"); + return NT_STATUS_INVALID_PARAMETER; + } + + /* + * Make valid trusting domain account (ie. uppercased and with '$' appended) + */ + + if (asprintf(&acct_name, "%s$", argv[0]) < 0) { + return NT_STATUS_NO_MEMORY; + } + + strupper_m(acct_name); + + /* 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 = 0xe00500b0; /* 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; + } + + { + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + uchar pwbuf[516]; + + encode_pw_buffer((char *)pwbuf, argv[1], STR_UNICODE); + + ZERO_STRUCT(ctr); + ZERO_STRUCT(p24); + + init_sam_user_info24(&p24, (char *)pwbuf, 24); + + ctr.switch_value = 24; + ctr.info.id24 = &p24; + + result = cli_samr_set_userinfo(cli, mem_ctx, &user_pol, 24, + &cli->user_session_key, &ctr); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0,("Could not set trust account password: %s\n", + nt_errstr(result))); + goto done; + } + } + + done: + SAFE_FREE(acct_name); + return result; +} + +/** + * Create interdomain trust account for a remote domain. + * + * @param argc standard argc + * @param argv standard argv without initial components + * + * @return Integer status (0 means success) + **/ + +static int rpc_trustdom_add(int argc, const char **argv) +{ + if (argc > 0) { + return run_rpc_command(NULL, PI_SAMR, 0, rpc_trustdom_add_internals, + argc, argv); + } else { + d_printf("Usage: net rpc trustdom add <domain>\n"); + return -1; + } +} + + +/** + * 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"); + d_printf("Use 'smbpasswd -x -i' instead.\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* domain_name_pol; + 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_m(domain_name); + + /* account name used at first is our domain's name with '$' */ + asprintf(&acct_name, "%s$", lp_workgroup()); + strupper_m(acct_name); + + /* + * opt_workgroup will be used by connection functions further, + * hence it should be set to remote domain name instead of ours + */ + if (opt_workgroup) { + opt_workgroup = smb_xstrdup(domain_name); + }; + + opt_user_name = acct_name; + + /* find the domain controller */ + if (!net_find_pdc(&server_ip, pdc_name, domain_name)) { + DEBUG(0, ("Couldn't find domain controller for domain %s\n", domain_name)); + return -1; + } + + /* connect to ipc$ as username/password */ + nt_status = connect_to_ipc(&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_pol, + &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); + + d_printf("Trust to domain %s established\n", domain_name); + 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_m(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, + const char *domain_name, + 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 */ + unsigned int num_domains, enum_ctx = 0; + int i, pad_len, col_len = 20; + DOM_SID *domain_sids; + char **trusted_dom_names; + fstring pdc_name; + char *dummy; + + /* 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, global_myname()); + 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, False, SEC_RIGHTS_QUERY_VALUE, + &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) { + DEBUG(0, ("Couldn't open policy handle. Error was %s\n", + nt_errstr(nt_status))); + 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 */, + &dummy, &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_m(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, global_myname(), + 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; +} + +/* dump sam database via samsync rpc calls */ +static int rpc_samdump(int argc, const char **argv) { + return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_samdump_internals, + argc, argv); +} + +/* syncronise sam database via samsync rpc calls */ +static int rpc_vampire(int argc, const char **argv) { + return run_rpc_command(NULL, PI_NETLOGON, NET_FLAGS_ANONYMOUS, rpc_vampire_internals, + argc, argv); +} +/****************************************************************************/ + + +/** + * 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 oldjoin \t\t\tto join a domain created in server manager\n\n\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 password <username> [<password>] -Uadmin_username%%admin_pass"); + 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}, + {"oldjoin", net_rpc_oldjoin}, + {"testjoin", net_rpc_testjoin}, + {"user", net_rpc_user}, + {"password", rpc_user_password}, + {"group", net_rpc_group}, + {"share", net_rpc_share}, + {"file", net_rpc_file}, + {"changetrustpw", net_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/source/utils/net_rpc_join.c b/source/utils/net_rpc_join.c new file mode 100644 index 00000000000..52e295949e0 --- /dev/null +++ b/source/utils/net_rpc_join.c @@ -0,0 +1,389 @@ +/* + 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) + * + **/ +static 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; + + /* 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, &channel)) { + DEBUG(0,("Could not retreive domain trust secret")); + goto done; + } + + /* ensure that schannel uses the right domain */ + fstrcpy(cli->domain, domain); + if (! NT_STATUS_IS_OK(result = cli_nt_establish_netlogon(cli, channel, stored_md4_trust_password))) { + DEBUG(0,("Error in domain join verfication (fresh connection)\n")); + goto done; + } + + 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 = ACB_WSTRUST; + uint32 sec_channel_type; + + /* 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; + uchar pwbuf[516]; + SAM_USERINFO_CTR ctr; + SAM_USER_INFO_24 p24; + SAM_USER_INFO_10 p10; + uchar md4_trust_password[16]; + + /* Misc */ + + NTSTATUS result; + int retval = 1; + char *domain; + uint32 num_rids, *name_types, *user_rids; + uint32 flags = 0x3e8; + char *acct_name; + const char *const_acct_name; + + /* check what type of join */ + if (argc >= 0) { + sec_channel_type = get_sec_channel_type(argv[0]); + } else { + sec_channel_type = get_sec_channel_type(NULL); + } + + switch (sec_channel_type) { + case SEC_CHAN_WKSTA: + acb_info = ACB_WSTRUST; + break; + case SEC_CHAN_BDC: + acb_info = ACB_SVRTRUST; + break; +#if 0 + case SEC_CHAN_DOMAIN: + acb_info = ACB_DOMTRUST; + break; +#endif + } + + /* 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 LSA 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$", global_myname()); + strlower_m(acct_name); + const_acct_name = acct_name; + + 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 (type=%d)\n", acct_name, name_types[0])); + 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); + E_md4hash(clear_trust_password, md4_trust_password); + } + + encode_pw_buffer(pwbuf, clear_trust_password, STR_UNICODE); + + /* 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, + &cli->user_session_key, &ctr); + + /* 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 */ + + if (!cli_nt_session_open(cli, PI_NETLOGON)) { + DEBUG(0,("Error connecting to NETLOGON pipe\n")); + goto done; + } + + /* ensure that schannel uses the right domain */ + fstrcpy(cli->domain, domain); + + result = cli_nt_establish_netlogon(cli, sec_channel_type, + md4_trust_password); + + if (!NT_STATUS_IS_OK(result)) { + DEBUG(0, ("Error domain join verification (reused connection): %s\n\n", + nt_errstr(result))); + + if ( NT_STATUS_EQUAL(result, NT_STATUS_ACCESS_DENIED) && + (sec_channel_type == SEC_CHAN_BDC) ) { + d_printf("Please make sure that no computer account\n" + "named like this machine (%s) exists in the domain\n", + global_myname()); + } + + goto done; + } + + /* Now store the secret in the secrets database */ + + strupper_m(domain); + + if (!secrets_store_domain_sid(domain, domain_sid)) { + DEBUG(0, ("error storing domain sid for %s\n", domain)); + goto done; + } + + if (!secrets_store_machine_password(clear_trust_password, domain, sec_channel_type)) { + DEBUG(0, ("error storing plaintext domain secrets for %s\n", domain)); + } + + /* double-check, connection from scratch */ + 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) { + 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(opt_target_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/source/utils/net_rpc_samsync.c b/source/utils/net_rpc_samsync.c new file mode 100644 index 00000000000..882f3a02bc2 --- /dev/null +++ b/source/utils/net_rpc_samsync.c @@ -0,0 +1,1080 @@ +/* + 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); + pdb_sethexpwd(hex_lm_passwd, lm_passwd, a->acb_info); + } else { + pdb_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); + pdb_sethexpwd(hex_nt_passwd, nt_passwd, a->acb_info); + } else { + pdb_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, + pdb_encode_acct_ctrl(a->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN)); +} + +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; + /* The following types are recognised but not handled */ + case SAM_DELTA_RENAME_GROUP: + d_printf("SAM_DELTA_RENAME_GROUP not handled\n"); + break; + case SAM_DELTA_RENAME_USER: + d_printf("SAM_DELTA_RENAME_USER not handled\n"); + break; + case SAM_DELTA_RENAME_ALIAS: + d_printf("SAM_DELTA_RENAME_ALIAS not handled\n"); + break; + case SAM_DELTA_POLICY_INFO: + d_printf("SAM_DELTA_POLICY_INFO not handled\n"); + break; + case SAM_DELTA_TRUST_DOMS: + d_printf("SAM_DELTA_TRUST_DOMS not handled\n"); + break; + case SAM_DELTA_PRIVS_INFO: + d_printf("SAM_DELTA_PRIVS_INFO not handled\n"); + break; + case SAM_DELTA_SECRET_INFO: + d_printf("SAM_DELTA_SECRET_INFO not handled\n"); + break; + case SAM_DELTA_DELETE_GROUP: + d_printf("SAM_DELTA_DELETE_GROUP not handled\n"); + break; + case SAM_DELTA_DELETE_USER: + d_printf("SAM_DELTA_DELETE_USER not handled\n"); + break; + case SAM_DELTA_MODIFIED_COUNT: + d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n"); + 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; + } + + switch( db_type ) { + case SAM_DATABASE_DOMAIN: + d_printf("Dumping DOMAIN database\n"); + break; + case SAM_DATABASE_BUILTIN: + d_printf("Dumping BUILTIN database\n"); + break; + case SAM_DATABASE_PRIVS: + d_printf("Dumping PRIVS databases\n"); + break; + default: + d_printf("Dumping unknown database type %u\n", db_type ); + break; + } + + do { + result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, db_type, + sync_context, + &num_deltas, &hdr_deltas, &deltas); + if (NT_STATUS_IS_ERR(result)) + break; + + 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 */ +NTSTATUS rpc_samdump_internals(const DOM_SID *domain_sid, + const char *domain_name, + struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + uchar trust_password[16]; + DOM_CRED ret_creds; + uint32 sec_channel; + + ZERO_STRUCT(ret_creds); + + fstrcpy(cli->domain, domain_name); + + if (!secrets_fetch_trust_account_password(domain_name, + trust_password, + NULL, &sec_channel)) { + DEBUG(0,("Could not fetch trust account password\n")); + goto fail; + } + + if (!NT_STATUS_IS_OK(nt_status = cli_nt_establish_netlogon(cli, sec_channel, + trust_password))) { + DEBUG(0,("Error connecting to NETLOGON pipe\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); + + nt_status = NT_STATUS_OK; + +fail: + cli_nt_session_close(cli); + return nt_status; +} + +/* Convert a SAM_ACCOUNT_DELTA to a SAM_ACCOUNT. */ +#define STRING_CHANGED (old_string && !new_string) ||\ + (!old_string && new_string) ||\ + (old_string && new_string && (strcmp(old_string, new_string) != 0)) + +static NTSTATUS +sam_account_from_delta(SAM_ACCOUNT *account, SAM_ACCOUNT_INFO *delta) +{ + const char *old_string, *new_string; + time_t unix_time, stored_time; + uchar lm_passwd[16], nt_passwd[16]; + static uchar zero_buf[16]; + + /* Username, fullname, home dir, dir drive, logon script, acct + desc, workstations, profile. */ + + if (delta->hdr_acct_name.buffer) { + old_string = pdb_get_nt_username(account); + new_string = unistr2_static(&delta->uni_acct_name); + + if (STRING_CHANGED) { + pdb_set_nt_username(account, new_string, PDB_CHANGED); + + } + + /* Unix username is the same - for sanity */ + old_string = pdb_get_username( account ); + if (STRING_CHANGED) { + pdb_set_username(account, new_string, PDB_CHANGED); + } + } + + if (delta->hdr_full_name.buffer) { + old_string = pdb_get_fullname(account); + new_string = unistr2_static(&delta->uni_full_name); + + if (STRING_CHANGED) + pdb_set_fullname(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_home_dir.buffer) { + old_string = pdb_get_homedir(account); + new_string = unistr2_static(&delta->uni_home_dir); + + if (STRING_CHANGED) + pdb_set_homedir(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_dir_drive.buffer) { + old_string = pdb_get_dir_drive(account); + new_string = unistr2_static(&delta->uni_dir_drive); + + if (STRING_CHANGED) + pdb_set_dir_drive(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_logon_script.buffer) { + old_string = pdb_get_logon_script(account); + new_string = unistr2_static(&delta->uni_logon_script); + + if (STRING_CHANGED) + pdb_set_logon_script(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_acct_desc.buffer) { + old_string = pdb_get_acct_desc(account); + new_string = unistr2_static(&delta->uni_acct_desc); + + if (STRING_CHANGED) + pdb_set_acct_desc(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_workstations.buffer) { + old_string = pdb_get_workstations(account); + new_string = unistr2_static(&delta->uni_workstations); + + if (STRING_CHANGED) + pdb_set_workstations(account, new_string, PDB_CHANGED); + } + + if (delta->hdr_profile.buffer) { + old_string = pdb_get_profile_path(account); + new_string = unistr2_static(&delta->uni_profile); + + if (STRING_CHANGED) + pdb_set_profile_path(account, new_string, PDB_CHANGED); + } + + /* User and group sid */ + if (pdb_get_user_rid(account) != delta->user_rid) + pdb_set_user_sid_from_rid(account, delta->user_rid, PDB_CHANGED); + if (pdb_get_group_rid(account) != delta->group_rid) + pdb_set_group_sid_from_rid(account, delta->group_rid, PDB_CHANGED); + + /* Logon and password information */ + if (!nt_time_is_zero(&delta->logon_time)) { + unix_time = nt_time_to_unix(&delta->logon_time); + stored_time = pdb_get_logon_time(account); + if (stored_time != unix_time) + pdb_set_logon_time(account, unix_time, PDB_CHANGED); + } + + if (!nt_time_is_zero(&delta->logoff_time)) { + unix_time = nt_time_to_unix(&delta->logoff_time); + stored_time = pdb_get_logoff_time(account); + if (stored_time != unix_time) + pdb_set_logoff_time(account, unix_time,PDB_CHANGED); + } + + if (pdb_get_logon_divs(account) != delta->logon_divs) + pdb_set_logon_divs(account, delta->logon_divs, PDB_CHANGED); + + /* TODO: logon hours */ + /* TODO: bad password count */ + /* TODO: logon count */ + + if (!nt_time_is_zero(&delta->pwd_last_set_time)) { + unix_time = nt_time_to_unix(&delta->pwd_last_set_time); + stored_time = pdb_get_pass_last_set_time(account); + if (stored_time != unix_time) + pdb_set_pass_last_set_time(account, unix_time, PDB_CHANGED); + } + +#if 0 +/* No kickoff time in the delta? */ + if (!nt_time_is_zero(&delta->kickoff_time)) { + unix_time = nt_time_to_unix(&delta->kickoff_time); + stored_time = pdb_get_kickoff_time(account); + if (stored_time != unix_time) + pdb_set_kickoff_time(account, unix_time, PDB_CHANGED); + } +#endif + + /* 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 */ + + if (pdb_get_acct_ctrl(account) != delta->acb_info) + pdb_set_acct_ctrl(account, delta->acb_info, PDB_CHANGED); + + pdb_set_domain(account, lp_workgroup(), 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 user_sid; + DOM_SID group_sid; + struct passwd *passwd; + fstring sid_string; + + 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 (!(passwd = Get_Pwnam(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) || + (delta->acb_info & ACB_DOMTRUST) ) { + pstrcpy(add_script, lp_addmachine_script()); + } else { + DEBUG(1, ("Unknown user type: %s\n", + pdb_encode_acct_ctrl(delta->acb_info, NEW_PW_FORMAT_SPACE_PADDED_LEN))); + nt_ret = NT_STATUS_UNSUCCESSFUL; + goto done; + } + 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)); + } else { + DEBUG(8,("fetch_account_info: no add user/machine script. Asking winbindd\n")); + + /* don't need a RID allocated since the user already has a SID */ + if ( !winbind_create_user( account, NULL ) ) + DEBUG(4,("fetch_account_info: winbind_create_user() failed\n")); + } + + /* try and find the possible unix account again */ + if ( !(passwd = Get_Pwnam(account)) ) { + d_printf("Could not create posix account info for '%s'\n", account); + nt_ret = NT_STATUS_NO_SUCH_USER; + goto done; + } + } + + sid_copy(&user_sid, get_global_sam_sid()); + sid_append_rid(&user_sid, delta->user_rid); + + DEBUG(3, ("Attempting to find SID %s for user %s in the passdb\n", sid_to_string(sid_string, &user_sid), account)); + if (!pdb_getsampwsid(sam_account, &user_sid)) { + sam_account_from_delta(sam_account, delta); + DEBUG(3, ("Attempting to add user SID %s for user %s in the passdb\n", + sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account))); + if (!pdb_add_sam_account(sam_account)) { + DEBUG(1, ("SAM Account for %s failed to be added to the passdb!\n", + account)); + return NT_STATUS_ACCESS_DENIED; + } + } else { + sam_account_from_delta(sam_account, delta); + DEBUG(3, ("Attempting to update user SID %s for user %s in the passdb\n", + sid_to_string(sid_string, &user_sid), pdb_get_username(sam_account))); + if (!pdb_update_sam_account(sam_account)) { + DEBUG(1, ("SAM Account for %s failed to be updated in the passdb!\n", + account)); + pdb_free_sam(&sam_account); + return NT_STATUS_ACCESS_DENIED; + } + } + + group_sid = *pdb_get_group_sid(sam_account); + + if (!pdb_getgrsid(&map, group_sid)) { + DEBUG(0, ("Primary group of %s has no mapping!\n", + pdb_get_username(sam_account))); + } else { + if (map.gid != passwd->pw_gid) { + if (!(grp = getgrgid(map.gid))) { + DEBUG(0, ("Could not find unix group %lu for user %s (group SID=%s)\n", + (unsigned long)map.gid, pdb_get_username(sam_account), sid_string_static(&group_sid))); + } else { + smb_set_primary_group(grp->gr_name, pdb_get_username(sam_account)); + } + } + } + + if ( !passwd ) { + DEBUG(1, ("No unix user for this account (%s), cannot adjust mappings\n", + pdb_get_username(sam_account))); + } + + done: + pdb_free_sam(&sam_account); + return nt_ret; +} + +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)) { + if ( map.gid != -1 ) + 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 = getgrnam(name)) == 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); + if (delta->hdr_grp_desc.buffer) { + fstrcpy(map.comment, comment); + } else { + fstrcpy(map.comment, ""); + } + + 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)) { + 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 %lu\n", (unsigned long)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)) { + 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); + + 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) +{ +#if 0 /* + * commented out right now after talking to Volker. Can't + * do much with the membership but seemed a shame to waste + * somewhat working code. Needs testing because the membership + * that shows up surprises me. Also can't do much with groups + * in groups (e.g. Domain Admins being a member of Adminsitrators). + * --jerry + */ + + int i; + TALLOC_CTX *t = NULL; + char **nt_members = NULL; + char **unix_members; + DOM_SID group_sid; + GROUP_MAP map; + struct group *grp; + enum SID_NAME_USE sid_type; + + if (delta->num_members == 0) { + return NT_STATUS_OK; + } + + sid_copy(&group_sid, &dom_sid); + sid_append_rid(&group_sid, rid); + + if (sid_equal(&dom_sid, &global_sid_Builtin)) { + sid_type = SID_NAME_WKN_GRP; + if (!get_builtin_group_from_sid(&group_sid, &map, False)) { + DEBUG(0, ("Could not find builtin group %s\n", sid_string_static(&group_sid))); + return NT_STATUS_NO_SUCH_GROUP; + } + } else { + sid_type = SID_NAME_ALIAS; + if (!get_local_group_from_sid(&group_sid, &map, False)) { + DEBUG(0, ("Could not find local group %s\n", sid_string_static(&group_sid))); + 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, &delta->sids[i].sid); + + if (!pdb_getsampwsid(member, &member_sid)) { + DEBUG(1, ("Found bogus group member: (member_sid=%s group=%s)\n", + 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); + +#endif /* end of fetch_alias_mem() */ + + 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; + /* The following types are recognised but not handled */ + case SAM_DELTA_DOMAIN_INFO: + d_printf("SAM_DELTA_DOMAIN_INFO not handled\n"); + break; + case SAM_DELTA_RENAME_GROUP: + d_printf("SAM_DELTA_RENAME_GROUP not handled\n"); + break; + case SAM_DELTA_RENAME_USER: + d_printf("SAM_DELTA_RENAME_USER not handled\n"); + break; + case SAM_DELTA_RENAME_ALIAS: + d_printf("SAM_DELTA_RENAME_ALIAS not handled\n"); + break; + case SAM_DELTA_POLICY_INFO: + d_printf("SAM_DELTA_POLICY_INFO not handled\n"); + break; + case SAM_DELTA_TRUST_DOMS: + d_printf("SAM_DELTA_TRUST_DOMS not handled\n"); + break; + case SAM_DELTA_PRIVS_INFO: + d_printf("SAM_DELTA_PRIVS_INFO not handled\n"); + break; + case SAM_DELTA_SECRET_INFO: + d_printf("SAM_DELTA_SECRET_INFO not handled\n"); + break; + case SAM_DELTA_DELETE_GROUP: + d_printf("SAM_DELTA_DELETE_GROUP not handled\n"); + break; + case SAM_DELTA_DELETE_USER: + d_printf("SAM_DELTA_DELETE_USER not handled\n"); + break; + case SAM_DELTA_MODIFIED_COUNT: + d_printf("SAM_DELTA_MODIFIED_COUNT not handled\n"); + break; + default: + d_printf("Unknown delta record type %d\n", hdr_delta->type); + break; + } +} + +static NTSTATUS +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 NT_STATUS_NO_MEMORY; + + switch( db_type ) { + case SAM_DATABASE_DOMAIN: + d_printf("Fetching DOMAIN database\n"); + break; + case SAM_DATABASE_BUILTIN: + d_printf("Fetching BUILTIN database\n"); + break; + case SAM_DATABASE_PRIVS: + d_printf("Fetching PRIVS databases\n"); + break; + default: + d_printf("Fetching unknown database type %u\n", db_type ); + break; + } + + do { + result = cli_netlogon_sam_sync(cli, mem_ctx, ret_creds, + db_type, sync_context, + &num_deltas, + &hdr_deltas, &deltas); + + if (NT_STATUS_IS_OK(result) || + NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)) { + + 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); + } + } else + return result; + + sync_context += 1; + } while (NT_STATUS_EQUAL(result, STATUS_MORE_ENTRIES)); + + talloc_destroy(mem_ctx); + + return result; +} + +/* dump sam database via samsync rpc calls */ +NTSTATUS rpc_vampire_internals(const DOM_SID *domain_sid, + const char *domain_name, + struct cli_state *cli, TALLOC_CTX *mem_ctx, + int argc, const char **argv) +{ + NTSTATUS result; + uchar trust_password[16]; + DOM_CRED ret_creds; + fstring my_dom_sid_str; + fstring rem_dom_sid_str; + uint32 sec_channel; + + ZERO_STRUCT(ret_creds); + + if (!sid_equal(domain_sid, get_global_sam_sid())) { + d_printf("Cannot import users from %s at this time, " + "as the current domain:\n\t%s: %s\nconflicts " + "with the remote domain\n\t%s: %s\n" + "Perhaps you need to set: \n\n\tsecurity=user\n\tworkgroup=%s\n\n in your smb.conf?\n", + domain_name, + get_global_sam_name(), sid_to_string(my_dom_sid_str, + get_global_sam_sid()), + domain_name, sid_to_string(rem_dom_sid_str, domain_sid), + domain_name); + return NT_STATUS_UNSUCCESSFUL; + } + + fstrcpy(cli->domain, domain_name); + + if (!secrets_fetch_trust_account_password(domain_name, + trust_password, NULL, + &sec_channel)) { + result = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + d_printf("Could not retrieve domain trust secret\n"); + goto fail; + } + + result = cli_nt_establish_netlogon(cli, sec_channel, trust_password); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to setup BDC creds\n"); + goto fail; + } + + result = fetch_database(cli, SAM_DATABASE_DOMAIN, &ret_creds, *domain_sid); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to fetch domain database: %s\n", + nt_errstr(result)); + if (NT_STATUS_EQUAL(result, NT_STATUS_NOT_SUPPORTED)) + d_printf("Perhaps %s is a Windows 2000 native mode " + "domain?\n", domain_name); + goto fail; + } + + result = fetch_database(cli, SAM_DATABASE_BUILTIN, &ret_creds, + global_sid_Builtin); + + if (!NT_STATUS_IS_OK(result)) { + d_printf("Failed to fetch builtin database: %s\n", + nt_errstr(result)); + goto fail; + } + + /* Currently we crash on PRIVS somewhere in unmarshalling */ + /* Dump_database(cli, SAM_DATABASE_PRIVS, &ret_creds); */ + +fail: + return result; +} diff --git a/source/utils/net_status.c b/source/utils/net_status.c new file mode 100644 index 00000000000..0543f457cfc --- /dev/null +++ b/source/utils/net_status.c @@ -0,0 +1,257 @@ +/* + Samba Unix/Linux SMB client library + net status command -- possible replacement for smbstatus + Copyright (C) 2003 Volker Lendecke (vl@samba.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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" + +static int show_session(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + BOOL *parseable = (BOOL *)state; + struct sessionid sessionid; + + if (dbuf.dsize != sizeof(sessionid)) + return 0; + + memcpy(&sessionid, dbuf.dptr, sizeof(sessionid)); + + if (!process_exists(sessionid.pid)) { + return 0; + } + + if (*parseable) { + d_printf("%d\\%s\\%s\\%s\\%s\n", + (int)sessionid.pid, uidtoname(sessionid.uid), + gidtoname(sessionid.gid), + sessionid.remote_machine, sessionid.hostname); + } else { + d_printf("%5d %-12s %-12s %-12s (%s)\n", + (int)sessionid.pid, uidtoname(sessionid.uid), + gidtoname(sessionid.gid), + sessionid.remote_machine, sessionid.hostname); + } + + return 0; +} + +static int net_status_sessions(int argc, const char **argv) +{ + TDB_CONTEXT *tdb; + BOOL parseable; + + if (argc == 0) { + parseable = False; + } else if ((argc == 1) && strequal(argv[0], "parseable")) { + parseable = True; + } else { + return net_help_status(argc, argv); + } + + if (!parseable) { + d_printf("PID Username Group Machine" + " \n"); + d_printf("-------------------------------------------" + "------------------------\n"); + } + + tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + + if (tdb == NULL) { + d_printf("%s not initialised\n", lock_path("sessionid.tdb")); + return -1; + } + + tdb_traverse(tdb, show_session, &parseable); + tdb_close(tdb); + + return 0; +} + +static int show_share(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)) { + return 0; + } + + d_printf("%-10.10s %5d %-12s %s", + crec.name,(int)crec.pid, + crec.machine, + asctime(LocalTime(&crec.start))); + + return 0; +} + +struct sessionids { + int num_entries; + struct sessionid *entries; +}; + +static int collect_pid(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + struct sessionids *ids = (struct sessionids *)state; + struct sessionid sessionid; + + if (dbuf.dsize != sizeof(sessionid)) + return 0; + + memcpy(&sessionid, dbuf.dptr, sizeof(sessionid)); + + if (!process_exists(sessionid.pid)) + return 0; + + ids->num_entries += 1; + ids->entries = Realloc(ids->entries, + sizeof(struct sessionid) * ids->num_entries); + ids->entries[ids->num_entries-1] = sessionid; + + return 0; +} + +static int show_share_parseable(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, + void *state) +{ + struct sessionids *ids = (struct sessionids *)state; + struct connections_data crec; + int i; + BOOL guest = True; + + if (dbuf.dsize != sizeof(crec)) + return 0; + + memcpy(&crec, dbuf.dptr, sizeof(crec)); + + if (crec.cnum == -1) + return 0; + + if (!process_exists(crec.pid)) { + return 0; + } + + for (i=0; i<ids->num_entries; i++) { + if (ids->entries[i].pid == crec.pid) { + guest = False; + break; + } + } + + d_printf("%s\\%d\\%s\\%s\\%s\\%s\\%s", + crec.name,(int)crec.pid, + guest ? "" : uidtoname(ids->entries[i].uid), + guest ? "" : gidtoname(ids->entries[i].gid), + crec.machine, + guest ? "" : ids->entries[i].hostname, + asctime(LocalTime(&crec.start))); + + return 0; +} + +static int net_status_shares_parseable(int argc, const char **argv) +{ + struct sessionids ids; + TDB_CONTEXT *tdb; + + ids.num_entries = 0; + ids.entries = NULL; + + tdb = tdb_open_log(lock_path("sessionid.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + + if (tdb == NULL) { + d_printf("%s not initialised\n", lock_path("sessionid.tdb")); + return -1; + } + + tdb_traverse(tdb, collect_pid, &ids); + tdb_close(tdb); + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + + if (tdb == NULL) { + d_printf("%s not initialised\n", lock_path("connections.tdb")); + d_printf("This is normal if no SMB client has ever connected " + "to your server.\n"); + return -1; + } + + tdb_traverse(tdb, show_share_parseable, &ids); + tdb_close(tdb); + + SAFE_FREE(ids.entries); + + return 0; +} + +static int net_status_shares(int argc, const char **argv) +{ + TDB_CONTEXT *tdb; + + if (argc == 0) { + + d_printf("\nService pid machine " + "Connected at\n"); + d_printf("-------------------------------------" + "------------------\n"); + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, + TDB_DEFAULT, O_RDONLY, 0); + + if (tdb == NULL) { + d_printf("%s not initialised\n", + lock_path("connections.tdb")); + d_printf("This is normal if no SMB client has ever " + "connected to your server.\n"); + return -1; + } + + tdb_traverse(tdb, show_share, NULL); + tdb_close(tdb); + + return 0; + } + + if ((argc != 1) || !strequal(argv[0], "parseable")) { + return net_help_status(argc, argv); + } + + return net_status_shares_parseable(argc, argv); +} + +int net_status(int argc, const char **argv) +{ + struct functable func[] = { + {"sessions", net_status_sessions}, + {"shares", net_status_shares}, + {NULL, NULL} + }; + return net_run_function(argc, argv, func, net_help_status); +} diff --git a/source/utils/net_time.c b/source/utils/net_time.c new file mode 100644 index 00000000000..45c17838055 --- /dev/null +++ b/source/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, global_myname(), 0x0); + if (host) { + make_nmb_name(&called, host, 0x20); + } else { + make_nmb_name(&called, "*SMBSERVER", 0x20); + } + + if (!cli_session_request(cli, &calling, &called)) { + fprintf(stderr,"Session request failed\n"); + goto done; + } + if (!cli_negprot(cli)) { + fprintf(stderr,"Protocol negotiation failed\n"); + goto done; + } + + ret = cli->servertime; + if (zone) *zone = cli->serverzone; + +done: + if (cli) cli_shutdown(cli); + return ret; +} + +/* find the servers time on the opt_host host */ +static time_t nettime(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 fstring s; + struct tm *tm; + + tm = localtime(&t); + + fstr_sprintf(s, "%02d%02d%02d%02d%04d.%02d", + tm->tm_mon+1, tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_year + 1900, tm->tm_sec); + return s; +} + +int net_time_usage(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/source/utils/nmblookup.c b/source/utils/nmblookup.c new file mode 100644 index 00000000000..3c5a22841ea --- /dev/null +++ b/source/utils/nmblookup.c @@ -0,0 +1,292 @@ +/* + Unix SMB/CIFS implementation. + NBT client - used to lookup netbios names + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Jelmer Vernooij 2003 (Conversion to popt) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. + +*/ + +#define NO_SYSLOG + +#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; +} + +/**************************************************************************** +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++) { + pull_ascii_fstring(cleanname, status[i].name); + for (j=0;cleanname[j];j++) { + if (!isprint((int)cleanname[j])) cleanname[j] = '.'; + } + d_printf("\t%-15s <%02x> - %s\n", + cleanname,status[i].type, + node_status_flags(status[i].flags)); + } + 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; + static BOOL find_master=False; + static BOOL lookup_by_ip = False; + poptContext pc; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "broadcast", 'B', POPT_ARG_STRING, NULL, 'B', "Specify address to use for broadcasts", "BROADCAST-ADDRESS" }, + { "flags", 'f', POPT_ARG_VAL, &give_flags, True, "List the NMB flags returned" }, + { "unicast", 'U', POPT_ARG_STRING, NULL, 'U', "Specify address to use for unicast" }, + { "master-browser", 'M', POPT_ARG_VAL, &find_master, True, "Search for a master browser" }, + { "recursion", 'R', POPT_ARG_VAL, &recursion_desired, True, "Set recursion desired in package" }, + { "status", 'S', POPT_ARG_VAL, &find_status, True, "Lookup node status as well" }, + { "translate", 'T', POPT_ARG_NONE, NULL, 'T', "Translate IP addresses into names" }, + { "root-port", 'r', POPT_ARG_VAL, &RootPort, True, "Use root port 137 (Win95 only replies to this)" }, + { "lookup-by-ip", 'A', POPT_ARG_VAL, &lookup_by_ip, True, "Do a node status on <name> as an IP Address" }, + POPT_COMMON_SAMBA + POPT_COMMON_CONNECTION + { 0, 0, 0, 0 } + }; + + *lookup = 0; + + setup_logging(argv[0],True); + + pc = poptGetContext("nmblookup", argc, (const char **)argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + poptSetOtherOptionHelp(pc, "<NODE> ..."); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'B': + bcast_addr = *interpret_addr2(poptGetOptArg(pc)); + got_bcast = True; + use_bcast = True; + break; + case 'U': + bcast_addr = *interpret_addr2(poptGetOptArg(pc)); + got_bcast = True; + use_bcast = False; + break; + case 'T': + translate_addresses = !translate_addresses; + break; + } + } + + poptGetArg(pc); /* Remove argv[0] */ + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + if (!lp_load(dyn_CONFIGFILE,True,False,False)) { + fprintf(stderr, "Can't load %s - run testparm to debug it\n", dyn_CONFIGFILE); + } + + load_interfaces(); + if (!open_sockets()) return(1); + + while(poptPeekArg(pc)) + { + char *p; + struct in_addr ip; + + fstrcpy(lookup,poptGetArg(pc)); + + if(lookup_by_ip) + { + ip = *interpret_addr2(lookup); + fstrcpy(lookup,"*"); + 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" ); + } + } + + poptFreeContext(pc); + + return(0); +} diff --git a/source/utils/ntlm_auth.c b/source/utils/ntlm_auth.c new file mode 100644 index 00000000000..2213a9bae37 --- /dev/null +++ b/source/utils/ntlm_auth.c @@ -0,0 +1,2182 @@ +/* + Unix SMB/CIFS implementation. + + Winbind status program. + + Copyright (C) Tim Potter 2000-2002 + Copyright (C) Andrew Bartlett <abartlet@samba.org> 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 stdio_helper_mode { + SQUID_2_4_BASIC, + SQUID_2_5_BASIC, + SQUID_2_5_NTLMSSP, + NTLMSSP_CLIENT_1, + GSS_SPNEGO, + GSS_SPNEGO_CLIENT, + NUM_HELPER_MODES +}; + +enum ntlm_break { + BREAK_NONE, + BREAK_LM, + BREAK_NT, + NO_LM, + NO_NT +}; + +typedef void (*stdio_helper_function)(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_basic_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_squid_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_client_ntlmssp_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static void manage_gss_spnego_client_request (enum stdio_helper_mode stdio_helper_mode, + char *buf, int length); + +static const struct { + enum stdio_helper_mode mode; + const char *name; + stdio_helper_function fn; +} stdio_helper_protocols[] = { + { SQUID_2_4_BASIC, "squid-2.4-basic", manage_squid_basic_request}, + { SQUID_2_5_BASIC, "squid-2.5-basic", manage_squid_basic_request}, + { SQUID_2_5_NTLMSSP, "squid-2.5-ntlmssp", manage_squid_ntlmssp_request}, + { NTLMSSP_CLIENT_1, "ntlmssp-client-1", manage_client_ntlmssp_request}, + { GSS_SPNEGO, "gss-spnego", manage_gss_spnego_request}, + { GSS_SPNEGO_CLIENT, "gss-spnego-client", manage_gss_spnego_client_request}, + { NUM_HELPER_MODES, NULL, NULL} +}; + +extern int winbindd_fd; + +static const char *opt_username; +static const char *opt_domain; +static const char *opt_workstation; +static const char *opt_password; +static DATA_BLOB opt_challenge; +static DATA_BLOB opt_lm_response; +static DATA_BLOB opt_nt_response; +static int request_lm_key; +static int request_nt_key; + + +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) { + DEBUG(0, ("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) { + DEBUG(0, ("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: %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.error_string, + response.data.auth.nt_status); + } else { + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + DEBUG(1, ("Reading winbind reply failed! (0x01)\n")); + } + + DEBUG(3, ("%s: %s (0x%x)\n", + response.data.auth.nt_status_string, + response.data.auth.error_string, + response.data.auth.nt_status)); + } + + return (result == NSS_STATUS_SUCCESS); +} + +/* authenticate a user with an encrypted username/password */ + +static NTSTATUS contact_winbind_auth_crap(const char *username, + const char *domain, + const char *workstation, + const DATA_BLOB *challenge, + const DATA_BLOB *lm_response, + const DATA_BLOB *nt_response, + uint32 flags, + uint8 lm_key[8], + uint8 nt_key[16], + char **error_string, + char **unix_name) +{ + NTSTATUS nt_status; + NSS_STATUS result; + struct winbindd_request request; + struct winbindd_response response; + + static uint8 zeros[16]; + + ZERO_STRUCT(request); + ZERO_STRUCT(response); + + request.flags = flags; + + if (push_utf8_fstring(request.data.auth_crap.user, username) == -1) { + *error_string = smb_xstrdup( + "unable to create utf8 string for username"); + return NT_STATUS_UNSUCCESSFUL; + } + + if (push_utf8_fstring(request.data.auth_crap.domain, domain) == -1) { + *error_string = smb_xstrdup( + "unable to create utf8 string for domain"); + return NT_STATUS_UNSUCCESSFUL; + } + + if (push_utf8_fstring(request.data.auth_crap.workstation, + workstation) == -1) { + *error_string = smb_xstrdup( + "unable to create utf8 string for workstation"); + return NT_STATUS_UNSUCCESSFUL; + } + + memcpy(request.data.auth_crap.chal, challenge->data, MIN(challenge->length, 8)); + + if (lm_response && lm_response->length) { + memcpy(request.data.auth_crap.lm_resp, lm_response->data, MIN(lm_response->length, sizeof(request.data.auth_crap.lm_resp))); + request.data.auth_crap.lm_resp_len = lm_response->length; + } + + if (nt_response && nt_response->length) { + memcpy(request.data.auth_crap.nt_resp, nt_response->data, MIN(nt_response->length, sizeof(request.data.auth_crap.nt_resp))); + request.data.auth_crap.nt_resp_len = nt_response->length; + } + + result = winbindd_request(WINBINDD_PAM_AUTH_CRAP, &request, &response); + + /* Display response */ + + if ((result != NSS_STATUS_SUCCESS) && (response.data.auth.nt_status == 0)) { + nt_status = NT_STATUS_UNSUCCESSFUL; + if (error_string) + *error_string = smb_xstrdup("Reading winbind reply failed!"); + return nt_status; + } + + nt_status = (NT_STATUS(response.data.auth.nt_status)); + if (!NT_STATUS_IS_OK(nt_status)) { + if (error_string) + *error_string = smb_xstrdup(response.data.auth.error_string); + return nt_status; + } + + if ((flags & WBFLAG_PAM_LMKEY) && lm_key + && (memcmp(zeros, response.data.auth.first_8_lm_hash, + sizeof(response.data.auth.first_8_lm_hash)) != 0)) { + memcpy(lm_key, response.data.auth.first_8_lm_hash, + sizeof(response.data.auth.first_8_lm_hash)); + } + if ((flags & WBFLAG_PAM_NTKEY) && nt_key + && (memcmp(zeros, response.data.auth.nt_session_key, + sizeof(response.data.auth.nt_session_key)) != 0)) { + memcpy(nt_key, response.data.auth.nt_session_key, + sizeof(response.data.auth.nt_session_key)); + } + + if (flags & WBFLAG_PAM_UNIX_NAME) { + if (pull_utf8_allocate(unix_name, (char *)response.extra_data) == -1) { + return NT_STATUS_NO_MEMORY; + } + } + + return nt_status; +} + +static NTSTATUS winbind_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) +{ + static const char zeros[16]; + NTSTATUS nt_status; + char *error_string; + uint8 lm_key[8]; + uint8 nt_key[16]; + char *unix_name; + + nt_status = contact_winbind_auth_crap(ntlmssp_state->user, ntlmssp_state->domain, + ntlmssp_state->workstation, + &ntlmssp_state->chal, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + WBFLAG_PAM_LMKEY | WBFLAG_PAM_NTKEY | WBFLAG_PAM_UNIX_NAME, + lm_key, nt_key, + &error_string, &unix_name); + + if (NT_STATUS_IS_OK(nt_status)) { + if (memcmp(lm_key, zeros, 8) != 0) { + *lm_session_key = data_blob(NULL, 16); + memcpy(lm_session_key->data, lm_key, 8); + memset(lm_session_key->data+8, '\0', 8); + } + + if (memcmp(nt_key, zeros, 16) != 0) { + *nt_session_key = data_blob(nt_key, 16); + } + ntlmssp_state->auth_context = talloc_strdup(ntlmssp_state->mem_ctx, unix_name); + SAFE_FREE(unix_name); + } else { + DEBUG(NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED) ? 0 : 3, + ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", + ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, error_string ? error_string : "unknown error (NULL)")); + ntlmssp_state->auth_context = NULL; + } + return nt_status; +} + +static NTSTATUS local_pw_check(struct ntlmssp_state *ntlmssp_state, DATA_BLOB *nt_session_key, DATA_BLOB *lm_session_key) +{ + static const char zeros[16]; + NTSTATUS nt_status; + uint8 lm_key[8]; + uint8 nt_key[16]; + uint8 lm_pw[16], nt_pw[16]; + + nt_lm_owf_gen (opt_password, nt_pw, lm_pw); + + nt_status = ntlm_password_check(ntlmssp_state->mem_ctx, + &ntlmssp_state->chal, + &ntlmssp_state->lm_resp, + &ntlmssp_state->nt_resp, + ntlmssp_state->user, + ntlmssp_state->user, + ntlmssp_state->domain, + lm_pw, nt_pw, nt_session_key, lm_session_key); + + if (NT_STATUS_IS_OK(nt_status)) { + if (memcmp(lm_key, zeros, 8) != 0) { + *lm_session_key = data_blob(NULL, 16); + memcpy(lm_session_key->data, lm_key, 8); + memset(lm_session_key->data+8, '\0', 8); + } + + if (memcmp(nt_key, zeros, 16) != 0) { + *nt_session_key = data_blob(nt_key, 16); + } + ntlmssp_state->auth_context = talloc_asprintf(ntlmssp_state->mem_ctx, "%s%c%s", ntlmssp_state->domain, *lp_winbind_separator(), ntlmssp_state->user); + } else { + DEBUG(3, ("Login for user [%s]\\[%s]@[%s] failed due to [%s]\n", + ntlmssp_state->domain, ntlmssp_state->user, ntlmssp_state->workstation, + nt_errstr(nt_status))); + ntlmssp_state->auth_context = NULL; + } + return nt_status; +} + +static NTSTATUS ntlm_auth_start_ntlmssp_client(NTLMSSP_STATE **client_ntlmssp_state) +{ + NTSTATUS status; + if ( (opt_username == NULL) || (opt_domain == NULL) ) { + DEBUG(1, ("Need username and domain for NTLMSSP\n")); + return status; + } + + status = ntlmssp_client_start(client_ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_username(*client_ntlmssp_state, opt_username); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set username: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_domain(*client_ntlmssp_state, opt_domain); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set domain: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + + status = ntlmssp_set_password(*client_ntlmssp_state, opt_password); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not set password: %s\n", + nt_errstr(status))); + ntlmssp_end(client_ntlmssp_state); + return status; + } + return NT_STATUS_OK; +} + +static NTSTATUS ntlm_auth_start_ntlmssp_server(NTLMSSP_STATE **ntlmssp_state) +{ + NTSTATUS status = ntlmssp_server_start(ntlmssp_state); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(1, ("Could not start NTLMSSP client: %s\n", + nt_errstr(status))); + return status; + } + + /* Have we been given a local password, or should we ask winbind? */ + if (opt_password) { + (*ntlmssp_state)->check_password = local_pw_check; + (*ntlmssp_state)->get_domain = lp_workgroup; + (*ntlmssp_state)->get_global_myname = global_myname; + } else { + (*ntlmssp_state)->check_password = winbind_pw_check; + (*ntlmssp_state)->get_domain = get_winbind_domain; + (*ntlmssp_state)->get_global_myname = get_winbind_netbios_name; + } + return NT_STATUS_OK; +} + +static void manage_squid_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state = NULL; + DATA_BLOB request, reply; + NTSTATUS nt_status; + + if (strlen(buf) < 2) { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strlen(buf) > 3) { + request = base64_decode_data_blob(buf + 3); + } else { + request = data_blob(NULL, 0); + } + + if ((strncmp(buf, "PW ", 3) == 0)) { + /* The calling application wants us to use a local password (rather than winbindd) */ + + opt_password = strndup((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; + } + + if (strncmp(buf, "YR", 2) == 0) { + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else if (strncmp(buf, "KK", 2) == 0) { + + } else { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (!ntlmssp_state) { + if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + return; + } + } + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, (const char *)request.data, request.length); + + nt_status = ntlmssp_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); + DEBUG(10, ("NTLMSSP challenge\n")); + } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); + + ntlmssp_end(&ntlmssp_state); + } else if (!NT_STATUS_IS_OK(nt_status)) { + x_fprintf(x_stdout, "NA %s\n", nt_errstr(nt_status)); + DEBUG(10, ("NTLMSSP %s\n", nt_errstr(nt_status))); + } else { + x_fprintf(x_stdout, "AF %s\n", (char *)ntlmssp_state->auth_context); + DEBUG(10, ("NTLMSSP OK!\n")); + } + + data_blob_free(&request); +} + +static void manage_client_ntlmssp_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state = NULL; + DATA_BLOB request, reply; + NTSTATUS nt_status; + BOOL first = False; + + if (strlen(buf) < 2) { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strlen(buf) > 3) { + request = base64_decode_data_blob(buf + 3); + } else { + request = data_blob(NULL, 0); + } + + if (strncmp(buf, "PW ", 3) == 0) { + /* We asked for a password and obviously got it :-) */ + + opt_password = strndup((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; + } + + if (opt_password == NULL) { + + /* Request a password from the calling process. After + sending it, the calling process should retry asking for the negotiate. */ + + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return; + } + + if (strncmp(buf, "YR", 2) == 0) { + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else if (strncmp(buf, "TT", 2) == 0) { + + } else { + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (!ntlmssp_state) { + if (!NT_STATUS_IS_OK(nt_status = ntlm_auth_start_ntlmssp_client(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + return; + } + first = True; + } + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, (const char *)request.data, request.length); + + nt_status = ntlmssp_update(ntlmssp_state, request, &reply); + + if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + char *reply_base64 = base64_encode_data_blob(reply); + if (first) { + x_fprintf(x_stdout, "YR %s\n", reply_base64); + } else { + x_fprintf(x_stdout, "KK %s\n", reply_base64); + } + SAFE_FREE(reply_base64); + data_blob_free(&reply); + DEBUG(10, ("NTLMSSP challenge\n")); + } else if (NT_STATUS_IS_OK(nt_status)) { + x_fprintf(x_stdout, "AF\n"); + DEBUG(10, ("NTLMSSP OK!\n")); + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } else { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(nt_status)); + DEBUG(0, ("NTLMSSP BH: %s\n", nt_errstr(nt_status))); + if (ntlmssp_state) + ntlmssp_end(&ntlmssp_state); + } + + data_blob_free(&request); +} + +static void manage_squid_basic_request(enum stdio_helper_mode stdio_helper_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 (stdio_helper_mode == SQUID_2_5_BASIC) { + rfc1738_unescape(user); + rfc1738_unescape(pass); + } + + if (check_plaintext_auth(user, pass, False)) { + x_fprintf(x_stdout, "OK\n"); + } else { + x_fprintf(x_stdout, "ERR\n"); + } +} + +static void offer_gss_spnego_mechs(void) { + + DATA_BLOB token; + SPNEGO_DATA spnego; + ssize_t len; + char *reply_base64; + + pstring principal; + pstring myname_lower; + + ZERO_STRUCT(spnego); + + pstrcpy(myname_lower, global_myname()); + strlower_m(myname_lower); + + pstr_sprintf(principal, "%s$@%s", myname_lower, lp_realm()); + + /* Server negTokenInit (mech offerings) */ + spnego.type = SPNEGO_NEG_TOKEN_INIT; + spnego.negTokenInit.mechTypes = smb_xmalloc(sizeof(char *) * 3); +#ifdef HAVE_KRB5 + spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_KERBEROS5_OLD); + spnego.negTokenInit.mechTypes[1] = smb_xstrdup(OID_NTLMSSP); + spnego.negTokenInit.mechTypes[2] = NULL; +#else + spnego.negTokenInit.mechTypes[0] = smb_xstrdup(OID_NTLMSSP); + spnego.negTokenInit.mechTypes[1] = NULL; +#endif + + + spnego.negTokenInit.mechListMIC = data_blob(principal, + strlen(principal)); + + len = write_spnego_data(&token, &spnego); + free_spnego_data(&spnego); + + if (len == -1) { + DEBUG(1, ("Could not write SPNEGO data blob\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + reply_base64 = base64_encode_data_blob(token); + x_fprintf(x_stdout, "TT %s *\n", reply_base64); + + SAFE_FREE(reply_base64); + data_blob_free(&token); + DEBUG(10, ("sent SPNEGO negTokenInit\n")); + return; +} + +static void manage_gss_spnego_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) +{ + static NTLMSSP_STATE *ntlmssp_state = NULL; + SPNEGO_DATA request, response; + DATA_BLOB token; + NTSTATUS status; + ssize_t len; + + char *user = NULL; + char *domain = NULL; + + const char *reply_code; + char *reply_base64; + pstring reply_argument; + + if (strlen(buf) < 2) { + + if (ntlmssp_state != NULL) { + DEBUG(1, ("Request for initial SPNEGO request where " + "we already have a state\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + DEBUG(1, ("NTLMSSP query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if ( (strlen(buf) == 2) && (strcmp(buf, "YR") == 0) ) { + + /* Initial request, get the negTokenInit offering + mechanisms */ + + offer_gss_spnego_mechs(); + return; + } + + /* All subsequent requests are "KK" (Knock, Knock ;)) and have + a blob. This might be negTokenInit or negTokenTarg */ + + if ( (strlen(buf) <= 3) || (strncmp(buf, "KK", 2) != 0) ) { + DEBUG(1, ("GSS-SPNEGO query [%s] invalid\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + token = base64_decode_data_blob(buf + 3); + len = read_spnego_data(token, &request); + data_blob_free(&token); + + if (len == -1) { + DEBUG(1, ("GSS-SPNEGO query [%s] invalid", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (request.type == SPNEGO_NEG_TOKEN_INIT) { + + /* Second request from Client. This is where the + client offers its mechanism to use. */ + + if ( (request.negTokenInit.mechTypes == NULL) || + (request.negTokenInit.mechTypes[0] == NULL) ) { + DEBUG(1, ("Client did not offer any mechanism")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (strcmp(request.negTokenInit.mechTypes[0], OID_NTLMSSP) == 0) { + + if ( request.negTokenInit.mechToken.data == NULL ) { + DEBUG(1, ("Client did not provide NTLMSSP data\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if ( ntlmssp_state != NULL ) { + DEBUG(1, ("Client wants a new NTLMSSP challenge, but " + "already got one\n")); + x_fprintf(x_stdout, "BH\n"); + ntlmssp_end(&ntlmssp_state); + return; + } + + if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_server(&ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(status)); + return; + } + + DEBUG(10, ("got NTLMSSP packet:\n")); + dump_data(10, (const char *)request.negTokenInit.mechToken.data, + request.negTokenInit.mechToken.length); + + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); + + status = ntlmssp_update(ntlmssp_state, + request.negTokenInit.mechToken, + &response.negTokenTarg.responseToken); + } + +#ifdef HAVE_KRB5 + if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) { + + char *principal; + DATA_BLOB auth_data; + DATA_BLOB ap_rep; + DATA_BLOB session_key; + + if ( request.negTokenInit.mechToken.data == NULL ) { + DEBUG(1, ("Client did not provide Kerberos data\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = strdup(OID_KERBEROS5_OLD); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); + response.negTokenTarg.responseToken = data_blob(NULL, 0); + + status = ads_verify_ticket(lp_realm(), + &request.negTokenInit.mechToken, + &principal, &auth_data, &ap_rep, + &session_key); + + /* Now in "principal" we have the name we are + authenticated as. */ + + if (NT_STATUS_IS_OK(status)) { + + domain = strchr(principal, '@'); + + if (domain == NULL) { + DEBUG(1, ("Did not get a valid principal " + "from ads_verify_ticket\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + *domain++ = '\0'; + domain = strdup(domain); + user = strdup(principal); + + data_blob_free(&ap_rep); + data_blob_free(&auth_data); + + SAFE_FREE(principal); + } + } +#endif + + } else { + + if ( (request.negTokenTarg.supportedMech == NULL) || + ( strcmp(request.negTokenTarg.supportedMech, OID_NTLMSSP) != 0 ) ) { + /* Kerberos should never send a negTokenTarg, OID_NTLMSSP + is the only one we support that sends this stuff */ + DEBUG(1, ("Got a negTokenTarg for something non-NTLMSSP: %s\n", + request.negTokenTarg.supportedMech)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (request.negTokenTarg.responseToken.data == NULL) { + DEBUG(1, ("Got a negTokenTarg without a responseToken!\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + status = ntlmssp_update(ntlmssp_state, + request.negTokenTarg.responseToken, + &response.negTokenTarg.responseToken); + + response.type = SPNEGO_NEG_TOKEN_TARG; + response.negTokenTarg.supportedMech = strdup(OID_NTLMSSP); + response.negTokenTarg.mechListMIC = data_blob(NULL, 0); + + if (NT_STATUS_IS_OK(status)) { + user = strdup(ntlmssp_state->user); + domain = strdup(ntlmssp_state->domain); + ntlmssp_end(&ntlmssp_state); + } + } + + free_spnego_data(&request); + + if (NT_STATUS_IS_OK(status)) { + response.negTokenTarg.negResult = SPNEGO_ACCEPT_COMPLETED; + reply_code = "AF"; + pstr_sprintf(reply_argument, "%s\\%s", domain, user); + } else if (NT_STATUS_EQUAL(status, + NT_STATUS_MORE_PROCESSING_REQUIRED)) { + response.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + reply_code = "TT"; + pstr_sprintf(reply_argument, "*"); + } else { + response.negTokenTarg.negResult = SPNEGO_REJECT; + reply_code = "NA"; + pstrcpy(reply_argument, nt_errstr(status)); + } + + SAFE_FREE(user); + SAFE_FREE(domain); + + len = write_spnego_data(&token, &response); + free_spnego_data(&response); + + if (len == -1) { + DEBUG(1, ("Could not write SPNEGO data blob\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + reply_base64 = base64_encode_data_blob(token); + + x_fprintf(x_stdout, "%s %s %s\n", + reply_code, reply_base64, reply_argument); + + SAFE_FREE(reply_base64); + data_blob_free(&token); + + return; +} + +static NTLMSSP_STATE *client_ntlmssp_state = NULL; + +static BOOL manage_client_ntlmssp_init(SPNEGO_DATA spnego) +{ + NTSTATUS status; + DATA_BLOB null_blob = data_blob(NULL, 0); + DATA_BLOB to_server; + char *to_server_base64; + const char *my_mechs[] = {OID_NTLMSSP, NULL}; + + DEBUG(10, ("Got spnego negTokenInit with NTLMSSP\n")); + + if (client_ntlmssp_state != NULL) { + DEBUG(1, ("Request for initial SPNEGO request where " + "we already have a state\n")); + return False; + } + + if (!client_ntlmssp_state) { + if (!NT_STATUS_IS_OK(status = ntlm_auth_start_ntlmssp_client(&client_ntlmssp_state))) { + x_fprintf(x_stdout, "BH %s\n", nt_errstr(status)); + return False; + } + } + + + if (opt_password == NULL) { + + /* Request a password from the calling process. After + sending it, the calling process should retry with + the negTokenInit. */ + + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return True; + } + + spnego.type = SPNEGO_NEG_TOKEN_INIT; + spnego.negTokenInit.mechTypes = my_mechs; + spnego.negTokenInit.reqFlags = 0; + spnego.negTokenInit.mechListMIC = null_blob; + + status = ntlmssp_update(client_ntlmssp_state, null_blob, + &spnego.negTokenInit.mechToken); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED, got: %s\n", + nt_errstr(status))); + ntlmssp_end(&client_ntlmssp_state); + return False; + } + + write_spnego_data(&to_server, &spnego); + data_blob_free(&spnego.negTokenInit.mechToken); + + to_server_base64 = base64_encode_data_blob(to_server); + data_blob_free(&to_server); + x_fprintf(x_stdout, "KK %s\n", to_server_base64); + SAFE_FREE(to_server_base64); + return True; +} + +static void manage_client_ntlmssp_targ(SPNEGO_DATA spnego) +{ + NTSTATUS status; + DATA_BLOB null_blob = data_blob(NULL, 0); + DATA_BLOB request; + DATA_BLOB to_server; + char *to_server_base64; + + DEBUG(10, ("Got spnego negTokenTarg with NTLMSSP\n")); + + if (client_ntlmssp_state == NULL) { + DEBUG(1, ("Got NTLMSSP tArg without a client state\n")); + x_fprintf(x_stdout, "BH\n"); + ntlmssp_end(&client_ntlmssp_state); + return; + } + + if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) { + x_fprintf(x_stdout, "NA\n"); + ntlmssp_end(&client_ntlmssp_state); + return; + } + + if (spnego.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) { + x_fprintf(x_stdout, "AF\n"); + ntlmssp_end(&client_ntlmssp_state); + return; + } + + status = ntlmssp_update(client_ntlmssp_state, + spnego.negTokenTarg.responseToken, + &request); + + if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { + DEBUG(1, ("Expected MORE_PROCESSING_REQUIRED from " + "ntlmssp_client_update, got: %s\n", + nt_errstr(status))); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + ntlmssp_end(&client_ntlmssp_state); + return; + } + + spnego.type = SPNEGO_NEG_TOKEN_TARG; + spnego.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE; + spnego.negTokenTarg.supportedMech = OID_NTLMSSP; + spnego.negTokenTarg.responseToken = request; + spnego.negTokenTarg.mechListMIC = null_blob; + + write_spnego_data(&to_server, &spnego); + data_blob_free(&request); + + to_server_base64 = base64_encode_data_blob(to_server); + data_blob_free(&to_server); + x_fprintf(x_stdout, "KK %s\n", to_server_base64); + SAFE_FREE(to_server_base64); + return; +} + +#ifdef HAVE_KRB5 + +static BOOL manage_client_krb5_init(SPNEGO_DATA spnego) +{ + char *principal; + DATA_BLOB tkt, to_server; + DATA_BLOB session_key_krb5; + SPNEGO_DATA reply; + char *reply_base64; + int retval; + + const char *my_mechs[] = {OID_KERBEROS5_OLD, NULL}; + ssize_t len; + + if ( (spnego.negTokenInit.mechListMIC.data == NULL) || + (spnego.negTokenInit.mechListMIC.length == 0) ) { + DEBUG(1, ("Did not get a principal for krb5\n")); + return False; + } + + principal = malloc(spnego.negTokenInit.mechListMIC.length+1); + + if (principal == NULL) { + DEBUG(1, ("Could not malloc principal\n")); + return False; + } + + memcpy(principal, spnego.negTokenInit.mechListMIC.data, + spnego.negTokenInit.mechListMIC.length); + principal[spnego.negTokenInit.mechListMIC.length] = '\0'; + + retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5); + + if (retval) { + + pstring user; + + /* Let's try to first get the TGT, for that we need a + password. */ + + if (opt_password == NULL) { + DEBUG(10, ("Requesting password\n")); + x_fprintf(x_stdout, "PW\n"); + return True; + } + + pstr_sprintf(user, "%s@%s", opt_username, opt_domain); + + if ((retval = kerberos_kinit_password(user, opt_password, + 0, NULL))) { + DEBUG(10, ("Requesting TGT failed: %s\n", error_message(retval))); + x_fprintf(x_stdout, "NA\n"); + return True; + } + + retval = cli_krb5_get_ticket(principal, 0, &tkt, &session_key_krb5); + + if (retval) { + DEBUG(10, ("Kinit suceeded, but getting a ticket failed: %s\n", error_message(retval))); + } + } + + data_blob_free(&session_key_krb5); + + ZERO_STRUCT(reply); + + reply.type = SPNEGO_NEG_TOKEN_INIT; + reply.negTokenInit.mechTypes = my_mechs; + reply.negTokenInit.reqFlags = 0; + reply.negTokenInit.mechToken = tkt; + reply.negTokenInit.mechListMIC = data_blob(NULL, 0); + + len = write_spnego_data(&to_server, &reply); + data_blob_free(&tkt); + + if (len == -1) { + DEBUG(1, ("Could not write SPNEGO data blob\n")); + return False; + } + + reply_base64 = base64_encode_data_blob(to_server); + x_fprintf(x_stdout, "KK %s *\n", reply_base64); + + SAFE_FREE(reply_base64); + data_blob_free(&to_server); + DEBUG(10, ("sent GSS-SPNEGO KERBEROS5 negTokenInit\n")); + return True; +} + +static void manage_client_krb5_targ(SPNEGO_DATA spnego) +{ + switch (spnego.negTokenTarg.negResult) { + case SPNEGO_ACCEPT_INCOMPLETE: + DEBUG(1, ("Got a Kerberos negTokenTarg with ACCEPT_INCOMPLETE\n")); + x_fprintf(x_stdout, "BH\n"); + break; + case SPNEGO_ACCEPT_COMPLETED: + DEBUG(10, ("Accept completed\n")); + x_fprintf(x_stdout, "AF\n"); + break; + case SPNEGO_REJECT: + DEBUG(10, ("Rejected\n")); + x_fprintf(x_stdout, "NA\n"); + break; + default: + DEBUG(1, ("Got an invalid negTokenTarg\n")); + x_fprintf(x_stdout, "AF\n"); + } +} + +#endif + +static void manage_gss_spnego_client_request(enum stdio_helper_mode stdio_helper_mode, + char *buf, int length) +{ + DATA_BLOB request; + SPNEGO_DATA spnego; + ssize_t len; + + if (strlen(buf) <= 3) { + DEBUG(1, ("SPNEGO query [%s] too short\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + request = base64_decode_data_blob(buf+3); + + if (strncmp(buf, "PW ", 3) == 0) { + + /* We asked for a password and obviously got it :-) */ + + opt_password = strndup((const char *)request.data, request.length); + + if (opt_password == NULL) { + DEBUG(1, ("Out of memory\n")); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + x_fprintf(x_stdout, "OK\n"); + data_blob_free(&request); + return; + } + + if ( (strncmp(buf, "TT ", 3) != 0) && + (strncmp(buf, "AF ", 3) != 0) && + (strncmp(buf, "NA ", 3) != 0) ) { + DEBUG(1, ("SPNEGO request [%s] invalid\n", buf)); + x_fprintf(x_stdout, "BH\n"); + data_blob_free(&request); + return; + } + + /* So we got a server challenge to generate a SPNEGO + client-to-server request... */ + + len = read_spnego_data(request, &spnego); + data_blob_free(&request); + + if (len == -1) { + DEBUG(1, ("Could not read SPNEGO data for [%s]\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (spnego.type == SPNEGO_NEG_TOKEN_INIT) { + + /* The server offers a list of mechanisms */ + + const char **mechType = spnego.negTokenInit.mechTypes; + + while (*mechType != NULL) { + +#ifdef HAVE_KRB5 + if ( (strcmp(*mechType, OID_KERBEROS5_OLD) == 0) || + (strcmp(*mechType, OID_KERBEROS5) == 0) ) { + if (manage_client_krb5_init(spnego)) + goto out; + } +#endif + + if (strcmp(*mechType, OID_NTLMSSP) == 0) { + if (manage_client_ntlmssp_init(spnego)) + goto out; + } + + mechType++; + } + + DEBUG(1, ("Server offered no compatible mechanism\n")); + x_fprintf(x_stdout, "BH\n"); + return; + } + + if (spnego.type == SPNEGO_NEG_TOKEN_TARG) { + + if (spnego.negTokenTarg.supportedMech == NULL) { + /* On accept/reject Windows does not send the + mechanism anymore. Handle that here and + shut down the mechanisms. */ + + switch (spnego.negTokenTarg.negResult) { + case SPNEGO_ACCEPT_COMPLETED: + x_fprintf(x_stdout, "AF\n"); + break; + case SPNEGO_REJECT: + x_fprintf(x_stdout, "NA\n"); + break; + default: + DEBUG(1, ("Got a negTokenTarg with no mech and an " + "unknown negResult: %d\n", + spnego.negTokenTarg.negResult)); + x_fprintf(x_stdout, "BH\n"); + } + + ntlmssp_end(&client_ntlmssp_state); + goto out; + } + + if (strcmp(spnego.negTokenTarg.supportedMech, + OID_NTLMSSP) == 0) { + manage_client_ntlmssp_targ(spnego); + goto out; + } + +#if HAVE_KRB5 + if (strcmp(spnego.negTokenTarg.supportedMech, + OID_KERBEROS5_OLD) == 0) { + manage_client_krb5_targ(spnego); + goto out; + } +#endif + + } + + DEBUG(1, ("Got an SPNEGO token I could not handle [%s]!\n", buf)); + x_fprintf(x_stdout, "BH\n"); + return; + + out: + free_spnego_data(&spnego); + return; +} + +static void manage_squid_request(enum stdio_helper_mode helper_mode, stdio_helper_function fn) +{ + char buf[SQUID_BUFFER_SIZE+1]; + int length; + char *c; + static BOOL err; + + /* this is not a typo - x_fgets doesn't work too well under squid */ + if (fgets(buf, sizeof(buf)-1, stdin) == NULL) { + DEBUG(1, ("fgets() failed! dying..... errno=%d (%s)\n", ferror(stdin), + strerror(ferror(stdin)))); + 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; + } + + fn(helper_mode, buf, length); +} + + +static void squid_stream(enum stdio_helper_mode stdio_mode, stdio_helper_function fn) { + /* initialize FDescs */ + x_setbuf(x_stdout, NULL); + x_setbuf(x_stderr, NULL); + while(1) { + manage_squid_request(stdio_mode, fn); + } +} + + +/* Authenticate a user with a challenge/response */ + +static BOOL check_auth_crap(void) +{ + NTSTATUS nt_status; + uint32 flags = 0; + char lm_key[8]; + char nt_key[16]; + char *hex_lm_key; + char *hex_nt_key; + char *error_string; + static uint8 zeros[16]; + + x_setbuf(x_stdout, NULL); + + if (request_lm_key) + flags |= WBFLAG_PAM_LMKEY; + + if (request_nt_key) + flags |= WBFLAG_PAM_NTKEY; + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &opt_challenge, + &opt_lm_response, + &opt_nt_response, + flags, + (unsigned char *)lm_key, + (unsigned char *)nt_key, + &error_string, NULL); + + if (!NT_STATUS_IS_OK(nt_status)) { + x_fprintf(x_stdout, "%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (request_lm_key + && (memcmp(zeros, lm_key, + sizeof(lm_key)) != 0)) { + hex_encode((const unsigned char *)lm_key, + sizeof(lm_key), + &hex_lm_key); + x_fprintf(x_stdout, "LM_KEY: %s\n", hex_lm_key); + SAFE_FREE(hex_lm_key); + } + if (request_nt_key + && (memcmp(zeros, nt_key, + sizeof(nt_key)) != 0)) { + hex_encode((const unsigned char *)nt_key, + sizeof(nt_key), + &hex_nt_key); + x_fprintf(x_stdout, "NT_KEY: %s\n", hex_nt_key); + SAFE_FREE(hex_nt_key); + } + + return True; +} + +/* + Authenticate a user with a challenge/response, checking session key + and valid authentication types +*/ + +static DATA_BLOB get_challenge(void) +{ + static DATA_BLOB chal; + if (opt_challenge.length) + return opt_challenge; + + chal = data_blob(NULL, 8); + + generate_random_buffer(chal.data, chal.length, False); + return chal; +} + +/* + * Test the normal 'LM and NTLM' combination + */ + +static BOOL test_lm_ntlm_broken(enum ntlm_break break_which) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB lm_response = data_blob(NULL, 24); + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + + uchar lm_key[8]; + uchar nt_key[16]; + uchar lm_hash[16]; + uchar nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(nt_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_NTKEY; + + SMBencrypt(opt_password,chall.data,lm_response.data); + E_deshash(opt_password, lm_hash); + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_md4hash(opt_password, nt_hash); + SMBsesskeygen_ntv1(nt_hash, NULL, session_key.data); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lm_response); + break; + case NO_NT: + data_blob_free(&nt_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, + lm_key, + nt_key, + &error_string, NULL); + + data_blob_free(&lm_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, (const char *)lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + + if (break_which == NO_NT) { + if (memcmp(lm_hash, nt_key, + 8) != 0) { + DEBUG(1, ("NT Session Key does not match expectations (should be LM hash)!\n")); + DEBUG(1, ("nt_key:\n")); + dump_data(1, (const char *)nt_key, sizeof(nt_key)); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, sizeof(lm_hash)); + pass = False; + } + } else { + if (memcmp(session_key.data, nt_key, + sizeof(nt_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("nt_key:\n")); + dump_data(1, (const char *)nt_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)session_key.data, session_key.length); + pass = False; + } + } + return pass; +} + +/* + * Test LM authentication, no NT response supplied + */ + +static BOOL test_lm(void) +{ + + return test_lm_ntlm_broken(NO_NT); +} + +/* + * Test the NTLM response only, no LM. + */ + +static BOOL test_ntlm(void) +{ + return test_lm_ntlm_broken(NO_LM); +} + +/* + * Test the NTLM response only, but in the LM field. + */ + +static BOOL test_ntlm_in_lm(void) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + + uchar lm_key[8]; + uchar lm_hash[16]; + uchar nt_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(nt_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_NTKEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + + E_deshash(opt_password, lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + NULL, + flags, + lm_key, + nt_key, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, (const char *)lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + if (memcmp(lm_hash, nt_key, 8) != 0) { + DEBUG(1, ("Session Key (first 8 lm hash) does not match expectations!\n")); + DEBUG(1, ("nt_key:\n")); + dump_data(1, (const char *)nt_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)lm_hash, 8); + pass = False; + } + return pass; +} + +/* + * Test the NTLM response only, but in the both the NT and LM fields. + */ + +static BOOL test_ntlm_in_both(void) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 24); + DATA_BLOB session_key = data_blob(NULL, 16); + + char lm_key[8]; + char lm_hash[16]; + char nt_key[16]; + char nt_hash[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(lm_key); + ZERO_STRUCT(nt_key); + + flags |= WBFLAG_PAM_LMKEY; + flags |= WBFLAG_PAM_NTKEY; + + SMBNTencrypt(opt_password,chall.data,nt_response.data); + E_md4hash(opt_password, (unsigned char *)nt_hash); + SMBsesskeygen_ntv1((const unsigned char *)nt_hash, NULL, session_key.data); + + E_deshash(opt_password, (unsigned char *)lm_hash); + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &nt_response, + &nt_response, + flags, + (unsigned char *)lm_key, + (unsigned char *)nt_key, + &error_string, NULL); + + data_blob_free(&nt_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return False; + } + + if (memcmp(lm_hash, lm_key, + sizeof(lm_key)) != 0) { + DEBUG(1, ("LM Key does not match expectations!\n")); + DEBUG(1, ("lm_key:\n")); + dump_data(1, lm_key, 8); + DEBUG(1, ("expected:\n")); + dump_data(1, lm_hash, 8); + pass = False; + } + if (memcmp(session_key.data, nt_key, + sizeof(nt_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("nt_key:\n")); + dump_data(1, nt_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)session_key.data, session_key.length); + pass = False; + } + + + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static BOOL test_lmv2_ntlmv2_broken(enum ntlm_break break_which) +{ + BOOL pass = True; + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB ntlmv2_response = data_blob(NULL, 0); + DATA_BLOB lmv2_response = data_blob(NULL, 0); + DATA_BLOB nt_session_key = data_blob(NULL, 0); + DATA_BLOB names_blob = NTLMv2_generate_names_blob(get_winbind_netbios_name(), get_winbind_domain()); + + uchar nt_key[16]; + DATA_BLOB chall = get_challenge(); + char *error_string; + + ZERO_STRUCT(nt_key); + + flags |= WBFLAG_PAM_NTKEY; + + if (!SMBNTLMv2encrypt(opt_username, opt_domain, opt_password, &chall, + &names_blob, + &lmv2_response, &ntlmv2_response, + &nt_session_key)) { + data_blob_free(&names_blob); + return False; + } + data_blob_free(&names_blob); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lmv2_response.data[0]++; + break; + case BREAK_NT: + ntlmv2_response.data[0]++; + break; + case NO_LM: + data_blob_free(&lmv2_response); + break; + case NO_NT: + data_blob_free(&ntlmv2_response); + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lmv2_response, + &ntlmv2_response, + flags, + NULL, + nt_key, + &error_string, NULL); + + data_blob_free(&lmv2_response); + data_blob_free(&ntlmv2_response); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + if (break_which != NO_NT && break_which != BREAK_NT && memcmp(nt_session_key.data, nt_key, + sizeof(nt_key)) != 0) { + DEBUG(1, ("NT Session Key does not match expectations!\n")); + DEBUG(1, ("nt_key:\n")); + dump_data(1, (const char *)nt_key, 16); + DEBUG(1, ("expected:\n")); + dump_data(1, (const char *)nt_session_key.data, nt_session_key.length); + pass = False; + } + return pass; +} + +/* + * Test the NTLMv2 and LMv2 responses + */ + +static BOOL test_lmv2_ntlmv2(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_NONE); +} + +/* + * Test the LMv2 response only + */ + +static BOOL test_lmv2(void) +{ + return test_lmv2_ntlmv2_broken(NO_NT); +} + +/* + * Test the NTLMv2 response only + */ + +static BOOL test_ntlmv2(void) +{ + return test_lmv2_ntlmv2_broken(NO_LM); +} + +static BOOL test_lm_ntlm(void) +{ + return test_lm_ntlm_broken(BREAK_NONE); +} + +static BOOL test_ntlm_lm_broken(void) +{ + return test_lm_ntlm_broken(BREAK_LM); +} + +static BOOL test_ntlm_ntlm_broken(void) +{ + return test_lm_ntlm_broken(BREAK_NT); +} + +static BOOL test_ntlmv2_lmv2_broken(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_LM); +} + +static BOOL test_ntlmv2_ntlmv2_broken(void) +{ + return test_lmv2_ntlmv2_broken(BREAK_NT); +} + +static BOOL test_plaintext(enum ntlm_break break_which) +{ + NTSTATUS nt_status; + uint32 flags = 0; + DATA_BLOB nt_response = data_blob(NULL, 0); + DATA_BLOB lm_response = data_blob(NULL, 0); + char *password; + + uchar nt_key[16]; + uchar lm_key[16]; + static const uchar zeros[8]; + DATA_BLOB chall = data_blob(zeros, sizeof(zeros)); + char *error_string; + + ZERO_STRUCT(nt_key); + + flags |= WBFLAG_PAM_NTKEY; + flags |= WBFLAG_PAM_LMKEY; + + if ((push_ucs2_allocate((smb_ucs2_t **)&nt_response.data, opt_password)) == -1) { + DEBUG(0, ("push_ucs2_allocate failed!\n")); + exit(1); + } + + nt_response.length = strlen_w(((void *)nt_response.data))*sizeof(smb_ucs2_t); + + password = strdup_upper(opt_password); + + if ((convert_string_allocate(NULL, CH_UNIX, + CH_DOS, password, + strlen(password)+1, + (void**)&lm_response.data,True)) == -1) { + DEBUG(0, ("push_ascii_allocate failed!\n")); + exit(1); + } + + SAFE_FREE(password); + + lm_response.length = strlen(lm_response.data); + + switch (break_which) { + case BREAK_NONE: + break; + case BREAK_LM: + lm_response.data[0]++; + break; + case BREAK_NT: + nt_response.data[0]++; + break; + case NO_LM: + SAFE_FREE(lm_response.data); + lm_response.length = 0; + break; + case NO_NT: + SAFE_FREE(nt_response.data); + nt_response.length = 0; + break; + } + + nt_status = contact_winbind_auth_crap(opt_username, opt_domain, + opt_workstation, + &chall, + &lm_response, + &nt_response, + flags, + lm_key, + nt_key, + &error_string, NULL); + + SAFE_FREE(nt_response.data); + SAFE_FREE(lm_response.data); + data_blob_free(&chall); + + if (!NT_STATUS_IS_OK(nt_status)) { + d_printf("%s (0x%x)\n", + error_string, + NT_STATUS_V(nt_status)); + SAFE_FREE(error_string); + return break_which == BREAK_NT; + } + + return break_which != BREAK_NT; +} + +static BOOL test_plaintext_none_broken(void) { + return test_plaintext(BREAK_NONE); +} + +static BOOL test_plaintext_lm_broken(void) { + return test_plaintext(BREAK_LM); +} + +static BOOL test_plaintext_nt_broken(void) { + return test_plaintext(BREAK_NT); +} + +static BOOL test_plaintext_nt_only(void) { + return test_plaintext(NO_LM); +} + +static BOOL test_plaintext_lm_only(void) { + return test_plaintext(NO_NT); +} + +/* + Tests: + + - LM only + - NT and LM + - NT + - NT in LM field + - NT in both fields + - NTLMv2 + - NTLMv2 and LMv2 + - LMv2 + - plaintext tests (in challenge-response feilds) + + check we get the correct session key in each case + check what values we get for the LM session key + +*/ + +static const struct ntlm_tests { + BOOL (*fn)(void); + const char *name; +} test_table[] = { + {test_lm, "LM"}, + {test_lm_ntlm, "LM and NTLM"}, + {test_ntlm, "NTLM"}, + {test_ntlm_in_lm, "NTLM in LM"}, + {test_ntlm_in_both, "NTLM in both"}, + {test_ntlmv2, "NTLMv2"}, + {test_lmv2_ntlmv2, "NTLMv2 and LMv2"}, + {test_lmv2, "LMv2"}, + {test_ntlmv2_lmv2_broken, "NTLMv2 and LMv2, LMv2 broken"}, + {test_ntlmv2_ntlmv2_broken, "NTLMv2 and LMv2, NTLMv2 broken"}, + {test_ntlm_lm_broken, "NTLM and LM, LM broken"}, + {test_ntlm_ntlm_broken, "NTLM and LM, NTLM broken"}, + {test_plaintext_none_broken, "Plaintext"}, + {test_plaintext_lm_broken, "Plaintext LM broken"}, + {test_plaintext_nt_broken, "Plaintext NT broken"}, + {test_plaintext_nt_only, "Plaintext NT only"}, + {test_plaintext_lm_only, "Plaintext LM only"} +}; + +static BOOL diagnose_ntlm_auth(void) +{ + unsigned int i; + BOOL pass = True; + + for (i=0; test_table[i].fn; i++) { + if (!test_table[i].fn()) { + DEBUG(1, ("Test %s failed!\n", test_table[i].name)); + pass = False; + } + } + + return pass; +} + +/* Main program */ + +enum { + OPT_USERNAME = 1000, + OPT_DOMAIN, + OPT_WORKSTATION, + OPT_CHALLENGE, + OPT_RESPONSE, + OPT_LM, + OPT_NT, + OPT_PASSWORD, + OPT_LM_KEY, + OPT_NT_KEY, + OPT_DIAGNOSTICS +}; + + int main(int argc, const char **argv) +{ + int opt; + static const char *helper_protocol; + static int diagnostics; + + static const char *hex_challenge; + static const char *hex_lm_response; + static const char *hex_nt_response; + char *challenge; + char *lm_response; + char *nt_response; + size_t challenge_len; + size_t lm_response_len; + size_t nt_response_len; + + poptContext pc; + + /* NOTE: DO NOT change this interface without considering the implications! + This is an external interface, which other programs will use to interact + with this helper. + */ + + /* We do not use single-letter command abbreviations, because they harm future + interface stability. */ + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "helper-protocol", 0, POPT_ARG_STRING, &helper_protocol, OPT_DOMAIN, "operate as a stdio-based helper", "helper protocol to use"}, + { "username", 0, POPT_ARG_STRING, &opt_username, OPT_USERNAME, "username"}, + { "domain", 0, POPT_ARG_STRING, &opt_domain, OPT_DOMAIN, "domain name"}, + { "workstation", 0, POPT_ARG_STRING, &opt_workstation, OPT_WORKSTATION, "workstation"}, + { "challenge", 0, POPT_ARG_STRING, &hex_challenge, OPT_CHALLENGE, "challenge (HEX encoded)"}, + { "lm-response", 0, POPT_ARG_STRING, &hex_lm_response, OPT_LM, "LM Response to the challenge (HEX encoded)"}, + { "nt-response", 0, POPT_ARG_STRING, &hex_nt_response, OPT_NT, "NT or NTLMv2 Response to the challenge (HEX encoded)"}, + { "password", 0, POPT_ARG_STRING, &opt_password, OPT_PASSWORD, "User's plaintext password"}, + { "request-lm-key", 0, POPT_ARG_NONE, &request_lm_key, OPT_LM_KEY, "Retreive LM session key"}, + { "request-nt-key", 0, POPT_ARG_NONE, &request_nt_key, OPT_NT_KEY, "Retreive NT session key"}, + { "diagnostics", 0, POPT_ARG_NONE, &diagnostics, OPT_DIAGNOSTICS, "Perform diagnostics on the authentictaion chain"}, + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + + /* Samba client initialisation */ + + dbf = x_stderr; + + /* Samba client initialisation */ + + if (!lp_load(dyn_CONFIGFILE, True, False, False)) { + d_fprintf(stderr, "wbinfo: error opening config file %s. Error was %s\n", + dyn_CONFIGFILE, strerror(errno)); + exit(1); + } + + /* 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: + challenge = smb_xmalloc((strlen(hex_challenge))/2+1); + if ((challenge_len = strhex_to_str(challenge, + strlen(hex_challenge), + hex_challenge)) != 8) { + x_fprintf(x_stderr, "hex decode of %s failed (only got %lu bytes)!\n", + hex_challenge, (unsigned long)challenge_len); + exit(1); + } + opt_challenge = data_blob(challenge, challenge_len); + SAFE_FREE(challenge); + break; + case OPT_LM: + lm_response = smb_xmalloc((strlen(hex_lm_response))/2+1); + lm_response_len = strhex_to_str(lm_response, + strlen(hex_lm_response), + hex_lm_response); + if (lm_response_len != 24) { + x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_lm_response); + exit(1); + } + opt_lm_response = data_blob(lm_response, lm_response_len); + SAFE_FREE(lm_response); + break; + case OPT_NT: + nt_response = smb_xmalloc((strlen(hex_nt_response)+2)/2+1); + nt_response_len = strhex_to_str(nt_response, + strlen(hex_nt_response), + hex_nt_response); + if (nt_response_len < 24) { + x_fprintf(x_stderr, "hex decode of %s failed!\n", hex_nt_response); + exit(1); + } + opt_nt_response = data_blob(nt_response, nt_response_len); + SAFE_FREE(nt_response); + break; + } + } + + if (helper_protocol) { + int i; + for (i=0; i<NUM_HELPER_MODES; i++) { + if (strcmp(helper_protocol, stdio_helper_protocols[i].name) == 0) { + squid_stream(stdio_helper_protocols[i].mode, stdio_helper_protocols[i].fn); + exit(0); + } + } + x_fprintf(x_stderr, "unknown helper protocol [%s]\n\nValid helper protools:\n\n", helper_protocol); + + for (i=0; i<NUM_HELPER_MODES; i++) { + x_fprintf(x_stderr, "%s\n", stdio_helper_protocols[i].name); + } + + exit(1); + } + + if (!opt_username) { + x_fprintf(x_stderr, "username must be specified!\n\n"); + poptPrintHelp(pc, stderr, 0); + exit(1); + } + + if (opt_domain == NULL) { + opt_domain = get_winbind_domain(); + } + + if (opt_workstation == NULL) { + opt_workstation = ""; + } + + if (opt_challenge.length) { + if (!check_auth_crap()) { + exit(1); + } + exit(0); + } + + if (!opt_password) { + opt_password = getpass("password: "); + } + + if (diagnostics) { + if (!diagnose_ntlm_auth()) { + exit(1); + } + } else { + fstring user; + + fstr_sprintf(user, "%s%c%s", opt_domain, winbind_separator(), opt_username); + if (!check_plaintext_auth(user, opt_password, True)) { + exit(1); + } + } + + /* Exit code */ + + poptFreeContext(pc); + return 0; +} diff --git a/source/utils/pdbedit.c b/source/utils/pdbedit.c new file mode 100644 index 00000000000..af96413c5ae --- /dev/null +++ b/source/utils/pdbedit.c @@ -0,0 +1,1179 @@ +/* + 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 BIT_FIX_INIT 0x04000000 +#define BIT_BADPWRESET 0x08000000 +#define BIT_TRUSTDOM 0x10000000 +#define BIT_TRUSTPW 0x20000000 +#define BIT_TRUSTSID 0x40000000 +#define BIT_TRUSTFLAGS 0x80000000 + +#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; +} + +/********************************************************* + Add all currently available group mappings to another db + ********************************************************/ + +static int export_groups (struct pdb_context *in, struct pdb_context *out) { + GROUP_MAP *maps = NULL; + int i, entries = 0; + + if (NT_STATUS_IS_ERR(in->pdb_enum_group_mapping(in, SID_NAME_UNKNOWN, + &maps, &entries, + False))) { + fprintf(stderr, "Can't get group mappings!\n"); + return 1; + } + + for (i=0; i<entries; i++) { + out->pdb_add_group_mapping_entry(out, &(maps[i])); + } + + SAFE_FREE(maps); + + return 0; +} + +/********************************************************* + Print info from sam structure +**********************************************************/ + +static int print_sam_info (SAM_ACCOUNT *sam_pwent, BOOL verbosity, BOOL smbpwdstyle) +{ + uid_t uid; + time_t tmp; + + /* TODO: chaeck if entry is a user or a workstation */ + if (!sam_pwent) return -1; + + if (verbosity) { + printf ("Unix username: %s\n", pdb_get_username(sam_pwent)); + printf ("NT username: %s\n", pdb_get_nt_username(sam_pwent)); + printf ("Account Flags: %s\n", pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent), NEW_PW_FORMAT_SPACE_PADDED_LEN)); + printf ("User SID: %s\n", + sid_string_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"); + + tmp = pdb_get_bad_password_time(sam_pwent); + printf ("Last bad password : %s\n", tmp ? http_timestring(tmp) : "0"); + printf ("Bad password count : %d\n", + pdb_get_bad_password_count(sam_pwent)); + + } else if (smbpwdstyle) { + char lm_passwd[33]; + char nt_passwd[33]; + + uid = nametouid(pdb_get_username(sam_pwent)); + pdb_sethexpwd(lm_passwd, pdb_get_lanman_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + pdb_sethexpwd(nt_passwd, pdb_get_nt_passwd(sam_pwent), pdb_get_acct_ctrl(sam_pwent)); + + printf("%s:%lu:%s:%s:%s:LCT-%08X:\n", + pdb_get_username(sam_pwent), + (unsigned long)uid, + lm_passwd, + nt_passwd, + pdb_encode_acct_ctrl(pdb_get_acct_ctrl(sam_pwent),NEW_PW_FORMAT_SPACE_PADDED_LEN), + (uint32)pdb_get_pass_last_set_time(sam_pwent)); + } else { + uid = nametouid(pdb_get_username(sam_pwent)); + printf ("%s:%lu:%s\n", pdb_get_username(sam_pwent), (unsigned long)uid, + pdb_get_fullname(sam_pwent)); + } + + return 0; +} + +/********************************************************* + Get an Print User Info +**********************************************************/ + +static int print_user_info (struct pdb_context *in, const char *username, BOOL verbosity, BOOL smbpwdstyle) +{ + SAM_ACCOUNT *sam_pwent=NULL; + BOOL ret; + BOOL updated_autolock = False, updated_badpw = False; + + 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; + } + + if (!pdb_update_autolock_flag(sam_pwent, &updated_autolock)) + DEBUG(2,("pdb_update_autolock_flag failed.\n")); + + if (!pdb_update_bad_password_count(sam_pwent, &updated_badpw)) + DEBUG(2,("pdb_update_bad_password_count failed.\n")); + + if (updated_autolock || updated_badpw) { + become_root(); + if(!pdb_update_sam_account(sam_pwent)) + DEBUG(1, ("Failed to modify entry.\n")); + unbecome_root(); + } + + ret=print_sam_info (sam_pwent, verbosity, smbpwdstyle); + pdb_free_sam(&sam_pwent); + + return ret; +} + + +/** + * Trust password flag name to flag conversion + * + * @param flag_name SAM_TRUST_PASSWD structure flag name + * @return flag value + **/ + +static int trustpw_flag(const char* flag_name) +{ + const int flag_num = 5; + typedef struct { const char *name; int val; } flag_conv; + flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT }, + { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT }, + { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT }, + { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS }, + { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }}; + int i; + + for (i = 0; i < flag_num; i++) { + if (!StrCaseCmp(flags[i].name, flag_name)) { + return flags[i].val; + } + } + + return 0; +} + + +/** + * Trust password flag to flag name conversion + * + * @param val SAM_TRUST_PASSWD structure flag + * @return passed flag name + **/ + +static char* trustpw_flag_name(const int val) +{ + const int flag_num = 5; + typedef struct { const char *name; int val; } flag_conv; + flag_conv flags[] = {{ "PASS_MACHINE_TRUST_NT", PASS_MACHINE_TRUST_NT }, + { "PASS_SERVER_TRUST_NT", PASS_SERVER_TRUST_NT }, + { "PASS_DOMAIN_TRUST_NT", PASS_DOMAIN_TRUST_NT }, + { "PASS_MACHINE_TRUST_ADS",PASS_MACHINE_TRUST_ADS }, + { "PASS_DOMAIN_TRUST_ADS", PASS_DOMAIN_TRUST_ADS }}; + int i; + + for (i = 0; i < flag_num; i++) { + if (flags[i].val == val) { + return strdup(flags[i].name); + } + } + + return strdup("unknown flag"); +} + + +/** + * Print trust password structure information + * + * @param mem_ctx memory context (for unicode name conversion) + * @param trust SAM_TRUST_PASSWD structure + * @param verbose verbose mode on/off + * @return 0 on success, otherwise failure + **/ + +static int print_trustpw_info(TALLOC_CTX *mem_ctx, SAM_TRUST_PASSWD *trust, BOOL verbose) +{ + char *dom_name; + if (!mem_ctx || !trust) return -1; + + /* convert unicode domain name to char* */ + if (!pull_ucs2_talloc(mem_ctx, &dom_name, trust->private.uni_name)) return -1; + dom_name[trust->private.uni_name_len] = 0; + + /* different output depending on level of verbosity */ + if (verbose) { + printf("Domain name: %s\n", dom_name); + printf("Domain SID: %s\n", sid_string_static(&trust->private.domain_sid)); + printf("Trust password %s\n", trust->private.pass); + printf("Trust type: %s\n", trustpw_flag_name(trust->private.flags)); + printf("Last modified %s\n", trust->private.mod_time ? http_timestring(trust->private.mod_time) : "0"); + + } else { + printf("%s:%s\n", dom_name, sid_string_static(&trust->private.domain_sid)); + } + + return 0; +} + + +/** + * Print trust password information by given name + * + * @param in initialised pdb_context + * @param name domain name of the trust password + * @param verbose verbose mode on/off + * @param smbpwdstyle smbpassword-style output (ignored here) + * @return 0 on success, otherwise failure + **/ + +static int print_trust_info(struct pdb_context *in, const char *name, BOOL verbose, BOOL smbpwdstyle) +{ + SAM_TRUST_PASSWD trust; + TALLOC_CTX *mem_ctx = NULL; + + mem_ctx = talloc_init("pdbedit: trust passwords listing"); + + if (NT_STATUS_IS_OK(in->pdb_gettrustpwnam(in, &trust, name))) { + return print_trustpw_info(mem_ctx, &trust, verbose); + } + + return -1; +} + +/********************************************************* + 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; +} + + +/** + * List trust passwords + * + * @param in initialised pdb context + * @param verbose turn on/off verbose mode + * @param smbpwdstyle ignored here (there was no trust passwords in smbpasswd file) + * @return 0 on success, otherwise failure + **/ + +static int print_trustpw_list(struct pdb_context *in, BOOL verbose, BOOL smbpwdstyle) +{ + SAM_TRUST_PASSWD trust; + TALLOC_CTX *mem_ctx = NULL; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + /* start enumeration and initialise memory context */ + status = in->pdb_settrustpwent(in); + if (NT_STATUS_IS_ERR(status)) return -1; + mem_ctx = talloc_init("pdbedit: trust passwords listing"); + + /* small separation to make it clear these are not regular accounts */ + if (!verbose) printf("---\n"); + + do { + /* fetch next trust password */ + status = in->pdb_gettrustpwent(in, &trust); + + if (trust.private.uni_name_len) { + /* print trust password info */ + if (verbose) printf ("---------------\n"); + print_trustpw_info(mem_ctx, &trust, verbose); + } + + } while (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) || NT_STATUS_EQUAL(status, NT_STATUS_OK)); + + talloc_destroy(mem_ctx); + return 0; +} + + +/********************************************************* + Fix a list of Users for uninitialised passwords +**********************************************************/ +static int fix_users_list (struct pdb_context *in) +{ + 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)))) { + printf("Updating record for user %s\n", pdb_get_username(sam_pwent)); + + if (!pdb_update_sam_account(sam_pwent)) { + printf("Update of user %s failed!\n", pdb_get_username(sam_pwent)); + } + pdb_free_sam(&sam_pwent); + check = NT_STATUS_IS_OK(pdb_init_sam(&sam_pwent)); + if (!check) { + fprintf(stderr, "Failed to initialise new SAM_ACCOUNT structure (out of memory?)\n"); + } + + } + 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, + const char *user_sid, const char *group_sid, + const BOOL badpw) +{ + 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 (user_sid) { + DOM_SID u_sid; + if (!string_to_sid(&u_sid, user_sid)) { + /* not a complete sid, may be a RID, try building a SID */ + int u_rid; + + if (sscanf(user_sid, "%d", &u_rid) != 1) { + fprintf(stderr, "Error passed string is not a complete user SID or RID!\n"); + return -1; + } + sid_copy(&u_sid, get_global_sam_sid()); + sid_append_rid(&u_sid, u_rid); + } + pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED); + } + if (group_sid) { + DOM_SID g_sid; + if (!string_to_sid(&g_sid, group_sid)) { + /* not a complete sid, may be a RID, try building a SID */ + int g_rid; + + if (sscanf(group_sid, "%d", &g_rid) != 1) { + fprintf(stderr, "Error passed string is not a complete group SID or RID!\n"); + return -1; + } + sid_copy(&g_sid, get_global_sam_sid()); + sid_append_rid(&g_sid, g_rid); + } + pdb_set_group_sid (sam_pwent, &g_sid, PDB_CHANGED); + } + + if (badpw) { + pdb_set_bad_password_count(sam_pwent, 0, PDB_CHANGED); + pdb_set_bad_password_time(sam_pwent, 0, PDB_CHANGED); + } + + if (NT_STATUS_IS_OK(in->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, char *user_sid, char *group_sid) +{ + SAM_ACCOUNT *sam_pwent=NULL; + NTSTATUS nt_status; + char *password1, *password2, *staticpass; + + get_global_sam_sid(); + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_new(&sam_pwent, username, 0))) { + DEBUG(0, ("could not create account to add new user %s\n", username)); + return -1; + } + + 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); + if (user_sid) { + DOM_SID u_sid; + if (!string_to_sid(&u_sid, user_sid)) { + /* not a complete sid, may be a RID, try building a SID */ + int u_rid; + + if (sscanf(user_sid, "%d", &u_rid) != 1) { + fprintf(stderr, "Error passed string is not a complete user SID or RID!\n"); + return -1; + } + sid_copy(&u_sid, get_global_sam_sid()); + sid_append_rid(&u_sid, u_rid); + } + pdb_set_user_sid (sam_pwent, &u_sid, PDB_CHANGED); + } + if (group_sid) { + DOM_SID g_sid; + if (!string_to_sid(&g_sid, group_sid)) { + /* not a complete sid, may be a RID, try building a SID */ + int g_rid; + + if (sscanf(group_sid, "%d", &g_rid) != 1) { + fprintf(stderr, "Error passed string is not a complete group SID or RID!\n"); + return -1; + } + sid_copy(&g_sid, get_global_sam_sid()); + sid_append_rid(&g_sid, g_rid); + } + pdb_set_group_sid (sam_pwent, &g_sid, 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 already 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; + fstring machineaccount; + struct passwd *pwd = NULL; + + get_global_sam_sid(); + + fstrcpy(machinename, machine_in); + machinename[15]= '\0'; + + if (machinename[strlen (machinename) -1] == '$') + machinename[strlen (machinename) -1] = '\0'; + + strlower_m(machinename); + + fstrcpy(machineaccount, machinename); + fstrcat(machineaccount, "$"); + + if ((pwd = getpwnam_alloc(machineaccount))) { + 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, machineaccount, 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, machineaccount, 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; +} + + +/** + * Add new trusting domain account + * + * @param in initialised pdb_context + * @param dom_name trusted domain name given in command line + * + * @return 0 on success, -1 otherwise + **/ + +static int new_trustdom(struct pdb_context *in, const char *dom_name) +{ + /* TODO */ + return -1; +} + + +/** + * Add new trust relationship password + * + * @param in initialised pdb_context + * @param dom_name trusting domain name given in command line + * @param dom_sid domain sid given in command line + * @param flag trust password type flag given in command line + * + * @return 0 on success, -1 otherwise + **/ + +static int new_trustpw(struct pdb_context *in, const char *dom_name, + const char *dom_sid, const char* flag) +{ + TALLOC_CTX *mem_ctx = NULL; + SAM_TRUST_PASSWD trust; + NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; + POLICY_HND connect_hnd; + DOM_SID *domain_sid = NULL; + smb_ucs2_t *uni_name = NULL; + char *givenpass, *domain_name = NULL; + struct in_addr srv_ip; + fstring srv_name, myname; + struct cli_state *cli; + time_t lct; + + if (!dom_name) return -1; + + mem_ctx = talloc_init("pdbedit: adding new trust password"); + + /* unicode name */ + trust.private.uni_name_len = strnlen(dom_name, 32); + push_ucs2_talloc(mem_ctx, &uni_name, dom_name); + strncpy_w(trust.private.uni_name, uni_name, 32); + + /* flags */ + trust.private.flags = trustpw_flag(flag); + + /* trusting SID */ + if (!dom_sid) { + /* if sid is not specified in command line, do our best + to establish it */ + + /* find domain PDC */ + if (!get_pdc_ip(dom_name, &srv_ip)) + return -1; + if (is_zero_ip(srv_ip)) + return -1; + if (!name_status_find(dom_name, 0x1b, 0x20, srv_ip, srv_name)) + return -1; + + get_myname(myname); + + /* Connect the domain pdc... */ + nt_status = cli_full_connection(&cli, myname, srv_name, &srv_ip, 139, + "IPC$", "IPC", "", "", "", 0, Undefined, NULL); + if (NT_STATUS_IS_ERR(nt_status)) + return -1; + if (!cli_nt_session_open(cli, PI_LSARPC)) + return -1; + + /* ...and query the domain sid */ + nt_status = cli_lsa_open_policy2(cli, mem_ctx, True, SEC_RIGHTS_QUERY_VALUE, + &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) return -1; + + nt_status = cli_lsa_query_info_policy(cli, mem_ctx, &connect_hnd, + 5, &domain_name, &domain_sid); + if (NT_STATUS_IS_ERR(nt_status)) return -1; + + nt_status = cli_lsa_close(cli, mem_ctx, &connect_hnd); + if (NT_STATUS_IS_ERR(nt_status)) return -1; + + cli_nt_session_close(cli); + cli_shutdown(cli); + + /* copying sid to trust password structure */ + sid_copy(&trust.private.domain_sid, domain_sid); + + } else { + if (!string_to_sid(&trust.private.domain_sid, dom_sid)) { + printf("Error: wrong SID specified !\n"); + return -1; + } + } + + /* password */ + givenpass = getpass("password:"); + memset(trust.private.pass, '\0', FSTRING_LEN); + strncpy(trust.private.pass, givenpass, FSTRING_LEN); + + /* last change time */ + lct = time(NULL); + trust.private.mod_time = lct; + + /* store trust password in passdb */ + nt_status = in->pdb_add_trust_passwd(in, &trust); + + talloc_destroy(mem_ctx); + if (NT_STATUS_IS_OK(nt_status)) + return 0; + + return -1; +} + + +/********************************************************* + 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_OK(in->pdb_getsampwnam(in, samaccount, username))) { + fprintf (stderr, "user %s does not exist in the passdb\n", username); + return -1; + } + + if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) { + fprintf (stderr, "Unable to delete user %s\n", username); + return -1; + } + return 0; +} + +/********************************************************* + Delete machine entry +**********************************************************/ + +static int delete_machine_entry (struct pdb_context *in, const char *machinename) +{ + fstring name; + SAM_ACCOUNT *samaccount = NULL; + + fstrcpy(name, machinename); + name[15] = '\0'; + if (name[strlen(name)-1] != '$') + fstrcat (name, "$"); + + if (!NT_STATUS_IS_OK(pdb_init_sam (&samaccount))) { + return -1; + } + + if (!NT_STATUS_IS_OK(in->pdb_getsampwnam(in, samaccount, name))) { + fprintf (stderr, "machine %s does not exist in the passdb\n", name); + return -1; + } + + if (!NT_STATUS_IS_OK(in->pdb_delete_sam_account (in, samaccount))) { + fprintf (stderr, "Unable to delete machine %s\n", name); + return -1; + } + + return 0; +} + +/********************************************************* + Start here. +**********************************************************/ + +int main (int argc, char **argv) +{ + static BOOL list_users = False; + static BOOL verbose = False; + static BOOL spstyle = False; + static BOOL machine = False; + static BOOL trustdom = 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 BOOL transfer_groups = False; + static BOOL force_initialised_password = False; + static char *logon_script = NULL; + static char *profile_path = NULL; + static char *account_control = NULL; + static char *account_policy = NULL; + static char *user_sid = NULL; + static char *group_sid = NULL; + static long int account_policy_value = 0; + BOOL account_policy_value_set = False; + static BOOL badpw_reset = False; + /* trust password parameters */ + static char *trustpw = NULL; + static char *trustsid = NULL; + static char *trustflags = NULL; + + 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}, + {"user SID", 'U', POPT_ARG_STRING, &user_sid, 0, "set user SID or RID", NULL}, + {"group SID", 'G', POPT_ARG_STRING, &group_sid, 0, "set group SID or RID", NULL}, + {"create", 'a', POPT_ARG_NONE, &add_user, 0, "create user", NULL}, + {"modify", 'r', POPT_ARG_NONE, &modify_user, 0, "modify user", NULL}, + {"delete", 'x', POPT_ARG_NONE, &delete_user, 0, "delete user", NULL}, + {"machine", 'm', POPT_ARG_NONE, &machine, 0, "account is a machine account", NULL}, + {"trustdom", 'I', POPT_ARG_NONE, &trustdom, 0, "account is a domain trust account", NULL}, + {"trustpw", 'N', POPT_ARG_STRING, &trustpw, 0, "trust password's domain name", NULL}, + {"trustsid", 'T', POPT_ARG_STRING, &trustsid, 0, "trust password's domain sid", NULL}, + {"trustflags", 'F', POPT_ARG_STRING, &trustflags, 0, "trust password flags", NULL}, + {"backend", 'b', POPT_ARG_STRING, &backend, 0, "use different passdb backend as default backend", NULL}, + {"import", 'i', POPT_ARG_STRING, &backend_in, 0, "import user accounts from this backend", NULL}, + {"export", 'e', POPT_ARG_STRING, &backend_out, 0, "export user accounts to this backend", NULL}, + {"group", 'g', POPT_ARG_NONE, &transfer_groups, 0, "use -i and -e for groups", NULL}, + {"account-policy", 'P', POPT_ARG_STRING, &account_policy, 0,"value of an account policy (like maximum password age)",NULL}, + {"value", 'C', POPT_ARG_LONG, &account_policy_value, 'C',"set the account policy to this value", NULL}, + {"account-control", 'c', POPT_ARG_STRING, &account_control, 0, "Values of account control", NULL}, + {"force-initialized-passwords", 0, POPT_ARG_NONE, &force_initialised_password, 0, "Force initialization of corrupt password strings in a passdb backend", NULL}, + {"bad-password-count-reset", 'z', POPT_ARG_NONE, &badpw_reset, 0, "reset bad password count", NULL}, + POPT_COMMON_SAMBA + POPT_TABLEEND + }; + + setup_logging("pdbedit", True); + + pc = poptGetContext(NULL, argc, (const char **) argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + while((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'C': + account_policy_value_set = True; + break; + } + } + + 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); + } + + if(!initialize_password_db(False)) + exit(1); + + if (!init_names()) + exit(1); + + setparms = (backend ? BIT_BACKEND : 0) + + (verbose ? BIT_VERBOSE : 0) + + (spstyle ? BIT_SPSTYLE : 0) + + (full_name ? BIT_FULLNAME : 0) + + (home_dir ? BIT_HOMEDIR : 0) + + (home_drive ? BIT_HDIRDRIVE : 0) + + (logon_script ? BIT_LOGSCRIPT : 0) + + (profile_path ? BIT_PROFILE : 0) + + (machine ? BIT_MACHINE : 0) + + (trustdom ? BIT_TRUSTDOM : 0) + + (trustpw ? BIT_TRUSTPW : 0) + + (trustsid ? BIT_TRUSTSID : 0) + + (trustflags ? BIT_TRUSTFLAGS : 0) + + (user_name ? BIT_USER : 0) + + (list_users ? BIT_LIST : 0) + + (force_initialised_password ? BIT_FIX_INIT : 0) + + (modify_user ? BIT_MODIFY : 0) + + (add_user ? BIT_CREATE : 0) + + (delete_user ? BIT_DELETE : 0) + + (account_control ? BIT_ACCTCTRL : 0) + + (account_policy ? BIT_ACCPOLICY : 0) + + (account_policy_value_set ? BIT_ACCPOLVAL : 0) + + (backend_in ? BIT_IMPORT : 0) + + (backend_out ? BIT_EXPORT : 0) + + (badpw_reset ? BIT_BADPWRESET : 0); + + 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; + + if (checkparms & BIT_FIX_INIT) { + return fix_users_list(bdef); + } + + /* account policy operations */ + if ((checkparms & BIT_ACCPOLICY) && !(checkparms & ~(BIT_ACCPOLICY + BIT_ACCPOLVAL))) { + uint32 value; + int field = account_policy_name_to_fieldnum(account_policy); + if (field == 0) { + 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; + } + if (transfer_groups) { + return export_groups(bin, bout); + } else { + 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)) { + print_users_list (bdef, verbose, spstyle); + return print_trustpw_list(bdef, verbose, spstyle); + } + if (!(checkparms & ~(BIT_USER + BIT_LIST))) { + return print_user_info (bdef, user_name, verbose, spstyle); + + } else if (!(checkparms & ~(BIT_TRUSTPW + BIT_LIST))) { + return print_trust_info(bdef, trustpw, verbose, spstyle); + } + } + + /* mask out users options */ + checkparms &= ~MASK_USER_GOOD; + + /* if bad password count is reset, we must be modifying */ + if (checkparms & BIT_BADPWRESET) { + checkparms |= BIT_MODIFY; + checkparms &= ~BIT_BADPWRESET; + } + + /* account operation */ + if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) { + /* check use of -u option */ + if (!(checkparms & (BIT_USER + BIT_TRUSTPW))) { + fprintf (stderr, "Username not specified! (use -u option)\n"); + return -1; + } + + /* account creation operations */ + if (!(checkparms & ~(BIT_CREATE + BIT_USER + BIT_MACHINE + BIT_TRUSTDOM))) { + /* machine trust account */ + if (checkparms & BIT_MACHINE) { + return new_machine (bdef, user_name); + /* interdomain trust account */ + } else if (checkparms & BIT_TRUSTDOM) { + return new_trustdom(bdef, user_name); + + /* ordinary user account */ + } else { + return new_user (bdef, user_name, full_name, home_dir, + home_drive, logon_script, + profile_path, user_sid, group_sid); + } + } + + /* 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, + user_sid, group_sid, + badpw_reset); + } + } + + /* trust password operation */ + if ((checkparms & BIT_CREATE) || (checkparms & BIT_MODIFY) || (checkparms & BIT_DELETE)) { + /* trust password creation */ + if (!(checkparms & ~(BIT_CREATE + BIT_TRUSTPW + BIT_TRUSTSID + BIT_TRUSTFLAGS))) { + return new_trustpw(bdef, trustpw, trustsid, trustflags); + } + } + + + if (setparms >= 0x20) { + fprintf (stderr, "Incompatible or insufficient options on command line!\n"); + } + poptPrintHelp(pc, stderr, 0); + + return 1; +} + diff --git a/source/utils/profiles.c b/source/utils/profiles.c new file mode 100644 index 00000000000..a31674dfb2e --- /dev/null +++ b/source/utils/profiles.c @@ -0,0 +1,742 @@ +/* + Samba Unix/Linux SMB client utility profiles.c + Copyright (C) 2002 Richard Sharpe, rsharpe@richardsharpe.com + Copyright (C) 2003 Jelmer Vernooij (conversion to popt) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 obviously 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> + +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, const unsigned char *sid_str) +{ + int i = 0, auth; + const unsigned 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 = (const unsigned char *)strchr(lstr + 1, '-'); + } + + return 1; +} + +#if 0 + +/* + * 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]; + } +} + +#endif + +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)); + } +} + +int main(int argc, char *argv[]) +{ + 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; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "verbose", 'v', POPT_ARG_NONE, NULL, 'v', "Sets verbose mode" }, + { "change-sid", 'c', POPT_ARG_STRING, NULL, 'c', "Provides SID to change" }, + { "new-sid", 'n', POPT_ARG_STRING, NULL, 'n', "Provides SID to change to" }, + { 0, 0, 0, 0 } + }; + + poptContext pc; + + pc = poptGetContext("profiles", argc, (const char **)argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + + poptSetOtherOptionHelp(pc, "<profilefile>"); + + /* + * Now, process the arguments + */ + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'c': + change = 1; + if (!get_sid(&old_sid, poptGetOptArg(pc))) { + fprintf(stderr, "Argument to -c should be a SID in form of S-1-5-...\n"); + poptPrintUsage(pc, stderr, 0); + exit(254); + } + break; + + case 'n': + new = 1; + if (!get_sid(&new_sid, poptGetOptArg(pc))) { + fprintf(stderr, "Argument to -n should be a SID in form of S-1-5-...\n"); + poptPrintUsage(pc, stderr, 0); + exit(253); + } + + break; + + case 'v': + verbose++; + break; + } + } + + if (!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(1); + } + + if ((!change & new) || (change & !new)) { + fprintf(stderr, "You must specify both -c and -n if one or the other is set!\n"); + poptPrintUsage(pc, stderr, 0); + exit(252); + } + + poptGetArg(pc); /* To get argv[0] */ + + fd = open(poptPeekArg(pc), O_RDWR, 0000); + + if (fd < 0) { + fprintf(stderr, "Could not open %s: %s\n", poptPeekArg(pc), + strerror(errno)); + exit(2); + } + + if (fstat(fd, &sbuf) < 0) { + fprintf(stderr, "Could not stat file %s, %s\n", poptPeekArg(pc), + 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; + +#ifdef HAVE_MMAP + base = mmap(&start, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); +#else + base = (char *)-1; + errno = ENOSYS; +#endif + + if ((int)base == -1) { + fprintf(stderr, "Could not mmap file: %s, %s\n", poptPeekArg(pc), + 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", poptPeekArg(pc)); + 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", poptPeekArg(pc)); + 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", poptPeekArg(pc)); + 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); + +#ifdef HAVE_MMAP + munmap(base, sbuf.st_size); +#endif + + poptFreeContext(pc); + + close(fd); + return 0; +} diff --git a/source/utils/rpccheck.c b/source/utils/rpccheck.c new file mode 100644 index 00000000000..ae109f69b65 --- /dev/null +++ b/source/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/source/utils/smbcacls.c b/source/utils/smbcacls.c new file mode 100644 index 00000000000..5a70d168842 --- /dev/null +++ b/source/utils/smbcacls.c @@ -0,0 +1,877 @@ +/* + Unix SMB/CIFS implementation. + ACL get/set utility + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Jelmer Vernooij 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 pstring owner_username; +static fstring server; +static int test_args = False; +static TALLOC_CTX *ctx; + +#define CREATE_ACCESS_READ READ_CONTROL_ACCESS + +/* numeric is set when the user wants numeric SIDs and ACEs rather + than going via LSA calls to resolve them */ +static BOOL numeric = False; + +enum acl_mode {SMB_ACL_SET, SMB_ACL_DELETE, SMB_ACL_MODIFY, SMB_ACL_ADD }; +enum chown_mode {REQUEST_NONE, REQUEST_CHOWN, REQUEST_CHGRP}; +enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; + +struct perm_value { + const char *perm; + uint32 mask; +}; + +/* These values discovered by inspection */ + +static const struct perm_value special_values[] = { + { "R", 0x00120089 }, + { "W", 0x00120116 }, + { "X", 0x001200a0 }, + { "D", 0x00010000 }, + { "P", 0x00040000 }, + { "O", 0x00080000 }, + { NULL, 0 }, +}; + +static const struct perm_value standard_values[] = { + { "READ", 0x001200a9 }, + { "CHANGE", 0x001301bf }, + { "FULL", 0x001f01ff }, + { NULL, 0 }, +}; + +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, SEC_DESC_SELF_RELATIVE, 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; + + fprintf(f, "REVISION:%d\n", sd->revision); + + /* Print owner and group sid */ + + if (sd->owner_sid) { + SidToString(sidstr, sd->owner_sid); + } else { + fstrcpy(sidstr, ""); + } + + fprintf(f, "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, old->type, + (change_mode == REQUEST_CHOWN) ? &sid : NULL, + (change_mode == REQUEST_CHGRP) ? &sid : NULL, + NULL, NULL, &sd_size); + + fnum = cli_nt_create(cli, filename, WRITE_OWNER_ACCESS); + + if (fnum == -1) { + printf("Failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + if (!cli_set_secdesc(cli, fnum, sd)) { + printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); + } + + cli_close(cli, fnum); + + return EXIT_OK; +} + + +/* The MSDN is contradictory over the ordering of ACE entries in an ACL. + However NT4 gives a "The information may have been modified by a + computer running Windows NT 5.0" if denied ACEs do not appear before + allowed ACEs. */ + +static int ace_compare(SEC_ACE *ace1, SEC_ACE *ace2) +{ + if (sec_ace_equal(ace1, ace2)) + return 0; + + if (ace1->type != ace2->type) + return ace2->type - ace1->type; + + if (sid_compare(&ace1->trustee, &ace2->trustee)) + return sid_compare(&ace1->trustee, &ace2->trustee); + + if (ace1->flags != ace2->flags) + return ace1->flags - ace2->flags; + + if (ace1->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->type, NULL, NULL, + NULL, old->dacl, &sd_size); + + fnum = cli_nt_create(cli, filename, WRITE_DAC_ACCESS); + + if (fnum == -1) { + printf("cacl_set failed to open %s: %s\n", filename, cli_errstr(cli)); + return EXIT_FAILED; + } + + if (!cli_set_secdesc(cli, fnum, sd)) { + printf("ERROR: secdesc set failed: %s\n", cli_errstr(cli)); + result = EXIT_FAILED; + } + + /* Clean up */ + + cli_close(cli, fnum); + + return result; +} + + +/***************************************************** +return a connection to a server +*******************************************************/ +static struct cli_state *connect_one(const char *share) +{ + struct cli_state *c; + struct in_addr ip; + NTSTATUS nt_status; + zero_ip(&ip); + + if (!cmdline_auth_info.got_pass) { + char *pass = getpass("Password: "); + if (pass) { + pstrcpy(cmdline_auth_info.password, pass); + cmdline_auth_info.got_pass = True; + } + } + + if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server, + &ip, 0, + share, "?????", + cmdline_auth_info.username, lp_workgroup(), + cmdline_auth_info.password, 0, + cmdline_auth_info.signing_state, NULL))) { + return c; + } else { + DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); + return NULL; + } +} + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc, const char *argv[]) +{ + char *share; + int opt; + enum acl_mode mode = SMB_ACL_SET; + static char *the_acl = NULL; + enum chown_mode change_mode = REQUEST_NONE; + int result; + fstring path; + pstring filename; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "delete", 'D', POPT_ARG_STRING, NULL, 'D', "Delete an acl", "ACL" }, + { "modify", 'M', POPT_ARG_STRING, NULL, 'M', "Modify an acl", "ACL" }, + { "add", 'a', POPT_ARG_STRING, NULL, 'a', "Add an acl", "ACL" }, + { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls", "ACLS" }, + { "chown", 'C', POPT_ARG_STRING, NULL, 'C', "Change ownership of a file", "USERNAME" }, + { "chgrp", 'G', POPT_ARG_STRING, NULL, 'G', "Change group ownership of a file", "GROUPNAME" }, + { "numeric", 0, POPT_ARG_NONE, &numeric, True, "Don't resolve sids or masks to names" }, + { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"}, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + { NULL } + }; + + struct cli_state *cli; + + ctx=talloc_init("main"); + + setlinebuf(stdout); + + dbf = x_stderr; + + setup_logging(argv[0],True); + + lp_load(dyn_CONFIGFILE,True,False,False); + load_interfaces(); + + pc = poptGetContext("smbcacls", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "//server1/share1 filename"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'S': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_SET; + break; + + case 'D': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_DELETE; + break; + + case 'M': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_MODIFY; + break; + + case 'a': + the_acl = smb_xstrdup(poptGetOptArg(pc)); + mode = SMB_ACL_ADD; + break; + + case 'C': + pstrcpy(owner_username,poptGetOptArg(pc)); + change_mode = REQUEST_CHOWN; + break; + + case 'G': + pstrcpy(owner_username,poptGetOptArg(pc)); + change_mode = REQUEST_CHGRP; + break; + } + } + + /* Make connection to server */ + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + fstrcpy(path, poptGetArg(pc)); + + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + return -1; + } + + pstrcpy(filename, poptGetArg(pc)); + + all_string_sub(path,"/","\\",0); + + fstrcpy(server,path+2); + share = strchr_m(server,'\\'); + if (!share) { + share = strchr_m(server,'/'); + if (!share) { + printf("Invalid argument: %s\n", 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)-2); + 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/source/utils/smbcontrol.c b/source/utils/smbcontrol.c new file mode 100644 index 00000000000..8a27684a4d3 --- /dev/null +++ b/source/utils/smbcontrol.c @@ -0,0 +1,747 @@ +/* + Unix SMB/CIFS implementation. + + Send messages to other Samba daemons + + Copyright (C) Tim Potter 2003 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Martin Pool 2001-2002 + Copyright (C) Simo Sorce 2002 + + This program is free software; you can 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" + +/* Default timeout value when waiting for replies (in seconds) */ + +#define DEFAULT_TIMEOUT 10 + +static int timeout = DEFAULT_TIMEOUT; +static int num_replies; /* Used by message callback fns */ + +/* Send a message to a destination pid. Zero means broadcast smbd. */ + +static BOOL send_message(pid_t pid, int msg_type, const void *buf, int len, + BOOL duplicates) +{ + TDB_CONTEXT *tdb; + BOOL ret; + int n_sent = 0; + + if (!message_init()) + return False; + + if (pid != 0) + return message_send_pid(pid, msg_type, buf, len, duplicates); + + tdb = tdb_open_log(lock_path("connections.tdb"), 0, + TDB_DEFAULT, O_RDWR, 0); + if (!tdb) { + fprintf(stderr,"Failed to open connections database" + ": %s\n", strerror(errno)); + 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; +} + +/* Wait for one or more reply messages */ + +static void wait_replies(BOOL multiple_replies) +{ + time_t start_time = time(NULL); + + /* Wait around a bit. This is pretty disgusting - we have to + busy-wait here as there is no nicer way to do it. */ + + do { + message_dispatch(); + if (num_replies > 0 && !multiple_replies) + break; + sleep(1); + } while (timeout - (time(NULL) - start_time) > 0); +} + +/* Message handler callback that displays a string on stdout */ + +static void print_string_cb(int msg_type, pid_t pid, void *buf, size_t len) +{ + printf("%.*s", (int)len, (const char *)buf); + num_replies++; +} + +/* Send no message. Useful for testing. */ + +static BOOL do_noop(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> noop\n"); + return False; + } + + /* Move along, nothing to see here */ + + return True; +} + +/* Send a debug string */ + +static BOOL do_debug(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> debug " + "<debug-string>\n"); + return False; + } + + return send_message( + pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False); +} + +/* Force a browser election */ + +static BOOL do_election(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> force-election\n"); + return False; + } + + return send_message( + pid, MSG_FORCE_ELECTION, NULL, 0, False); +} + +/* Ping a samba daemon process */ + +static void pong_cb(int msg_type, pid_t pid, void *buf, size_t len) +{ + printf("PONG from pid %u\n", (unsigned int)pid); + num_replies++; +} + +static BOOL do_ping(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> ping\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(pid, MSG_PING, NULL, 0, False)) + return False; + + message_register(MSG_PONG, pong_cb); + + wait_replies(pid == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + message_deregister(MSG_PONG); + + return num_replies; +} + +/* Set profiling options */ + +static BOOL do_profile(const pid_t pid, const int argc, const char **argv) +{ + int v; + + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> profile " + "<off|count|on|flush>\n"); + return False; + } + + if (strcmp(argv[1], "off") == 0) { + v = 0; + } else if (strcmp(argv[1], "count") == 0) { + v = 1; + } else if (strcmp(argv[1], "on") == 0) { + v = 2; + } else if (strcmp(argv[1], "flush") == 0) { + v = 3; + } else { + fprintf(stderr, "Unknown profile command '%s'\n", argv[1]); + return False; + } + + return send_message(pid, MSG_PROFILE, &v, sizeof(int), False); +} + +/* Return the profiling level */ + +static void profilelevel_cb(int msg_type, pid_t pid, void *buf, size_t len) +{ + int level; + const char *s; + + num_replies++; + + if (len != sizeof(int)) { + fprintf(stderr, "invalid message length %ld returned\n", + (unsigned long)len); + return; + } + + memcpy(&level, buf, sizeof(int)); + + switch (level) { + case 0: + s = "not enabled"; + break; + case 1: + s = "off"; + break; + case 3: + s = "count only"; + break; + case 7: + s = "count and time"; + break; + default: + s = "BOGUS"; + break; + } + + printf("Profiling %s on pid %u\n",s,(unsigned int)pid); +} + +static void profilelevel_rqst(int msg_type, pid_t pid, void *buf, size_t len) +{ + int v = 0; + + /* Send back a dummy reply */ + + send_message(pid, MSG_PROFILELEVEL, &v, sizeof(int), False); +} + +static BOOL do_profilelevel(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> profilelevel\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(pid, MSG_REQ_PROFILELEVEL, NULL, 0, False)) + return False; + + message_register(MSG_PROFILELEVEL, profilelevel_cb); + message_register(MSG_REQ_PROFILELEVEL, profilelevel_rqst); + + wait_replies(pid == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + message_deregister(MSG_PROFILE); + + return num_replies; +} + +/* Display debug level settings */ + +static BOOL do_debuglevel(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> debuglevel\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(pid, MSG_REQ_DEBUGLEVEL, NULL, 0, False)) + return False; + + message_register(MSG_DEBUGLEVEL, print_string_cb); + + wait_replies(pid == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + message_deregister(MSG_DEBUGLEVEL); + + return num_replies; +} + +/* Send a print notify message */ + +static BOOL do_printnotify(const pid_t pid, const int argc, const char **argv) +{ + const char *cmd; + + /* Check for subcommand */ + + if (argc == 1) { + fprintf(stderr, "Must specify subcommand:\n"); + fprintf(stderr, "\tqueuepause <printername>\n"); + fprintf(stderr, "\tqueueresume <printername>\n"); + fprintf(stderr, "\tjobpause <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobresume <printername> <unix jobid>\n"); + fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n"); + fprintf(stderr, "\tprinter <printername> <comment|port|" + "driver> <value>\n"); + + return False; + } + + cmd = argv[1]; + + if (strcmp(cmd, "queuepause") == 0) { + + if (argc != 3) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " queuepause <printername>\n"); + return False; + } + + notify_printer_status_byname(argv[2], PRINTER_STATUS_PAUSED); + + goto send; + + } else if (strcmp(cmd, "queueresume") == 0) { + + if (argc != 3) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " queuereume <printername>\n"); + return False; + } + + notify_printer_status_byname(argv[2], PRINTER_STATUS_OK); + + goto send; + + } else if (strcmp(cmd, "jobpause") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + argv[2], jobid, JOB_STATUS_PAUSED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "jobresume") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + argv[2], jobid, JOB_STATUS_QUEUED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "jobdelete") == 0) { + int jobid; + + if (argc != 4) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify" + " jobpause <printername> <unix-jobid>\n"); + return False; + } + + jobid = atoi(argv[3]); + + notify_job_status_byname( + argv[2], jobid, JOB_STATUS_DELETING, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + notify_job_status_byname( + argv[2], jobid, JOB_STATUS_DELETING| + JOB_STATUS_DELETED, + SPOOLSS_NOTIFY_MSG_UNIX_JOBID); + + goto send; + + } else if (strcmp(cmd, "printer") == 0) { + uint32 attribute; + + if (argc != 5) { + fprintf(stderr, "Usage: smbcontrol <dest> printnotify " + "printer <printername> <comment|port|driver> " + "<value>\n"); + return False; + } + + if (strcmp(argv[3], "comment") == 0) { + attribute = PRINTER_NOTIFY_COMMENT; + } else if (strcmp(argv[3], "port") == 0) { + attribute = PRINTER_NOTIFY_PORT_NAME; + } else if (strcmp(argv[3], "driver") == 0) { + attribute = PRINTER_NOTIFY_DRIVER_NAME; + } else { + fprintf(stderr, "Invalid printer command '%s'\n", + argv[3]); + return False; + } + + notify_printer_byname(argv[2], attribute, argv[4]); + + goto send; + } + + fprintf(stderr, "Invalid subcommand '%s'\n", cmd); + return False; + +send: + print_notify_send_messages(0); + return True; +} + +/* Close a share */ + +static BOOL do_closeshare(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> close-share " + "<sharename>\n"); + return False; + } + + return send_message( + pid, MSG_SMB_FORCE_TDIS, argv[1], strlen(argv[1]) + 1, False); +} + +/* Force a SAM synchronisation */ + +static BOOL do_samsync(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> samsync\n"); + return False; + } + + return send_message( + pid, MSG_SMB_SAM_SYNC, NULL, 0, False); +} + +/* Force a SAM replication */ + +static BOOL do_samrepl(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> samrepl\n"); + return False; + } + + return send_message( + pid, MSG_SMB_SAM_REPL, NULL, 0, False); +} + +/* Display talloc pool usage */ + +static BOOL do_poolusage(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> pool-usage\n"); + return False; + } + + /* Send a message and register our interest in a reply */ + + if (!send_message(pid, MSG_REQ_POOL_USAGE, NULL, 0, False)) + return False; + + message_register(MSG_POOL_USAGE, print_string_cb); + + wait_replies(pid == 0); + + /* No replies were received within the timeout period */ + + if (num_replies == 0) + printf("No replies received\n"); + + message_deregister(MSG_POOL_USAGE); + + return num_replies; +} + +/* Perform a dmalloc mark */ + +static BOOL do_dmalloc_mark(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> dmalloc-mark\n"); + return False; + } + + return send_message( + pid, MSG_REQ_DMALLOC_MARK, NULL, 0, False); +} + +/* Perform a dmalloc changed */ + +static BOOL do_dmalloc_changed(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> " + "dmalloc-log-changed\n"); + return False; + } + + return send_message( + pid, MSG_REQ_DMALLOC_LOG_CHANGED, NULL, 0, False); +} + +/* Shutdown a server process */ + +static BOOL do_shutdown(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> shutdown\n"); + return False; + } + + return send_message(pid, MSG_SHUTDOWN, NULL, 0, False); +} + +/* Notify a driver upgrade */ + +static BOOL do_drvupgrade(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: smbcontrol <dest> drvupgrade " + "<driver-name>\n"); + return False; + } + + return send_message( + pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False); +} + +static BOOL do_reload_config(const pid_t pid, const int argc, const char **argv) +{ + if (argc != 1) { + fprintf(stderr, "Usage: smbcontrol <dest> reload-config\n"); + return False; + } + + return send_message(pid, MSG_SMB_CONF_UPDATED, NULL, 0, False); +} + +/* A list of message type supported */ + +static const struct { + const char *name; /* Option name */ + BOOL (*fn)(const pid_t pid, const int argc, const char **argv); + const char *help; /* Short help text */ +} msg_types[] = { + { "debug", do_debug, "Set debuglevel" }, + { "force-election", do_election, + "Force a browse election" }, + { "ping", do_ping, "Elicit a response" }, + { "profile", do_profile, "" }, + { "profilelevel", do_profilelevel, "" }, + { "debuglevel", do_debuglevel, "Display current debuglevels" }, + { "printnotify", do_printnotify, "Send a print notify message" }, + { "close-share", do_closeshare, "Forcibly disconnect a share" }, + { "samsync", do_samsync, "Initiate SAM synchronisation" }, + { "samrepl", do_samrepl, "Initiate SAM replication" }, + { "pool-usage", do_poolusage, "Display talloc memory usage" }, + { "dmalloc-mark", do_dmalloc_mark, "" }, + { "dmalloc-log-changed", do_dmalloc_changed, "" }, + { "shutdown", do_shutdown, "Shut down daemon" }, + { "drvupgrade", do_drvupgrade, "Notify a printer driver has changed" }, + { "reload-config", do_reload_config, "Force smbd or winbindd to reload config file"}, + { "noop", do_noop, "Do nothing" }, + { NULL } +}; + +/* Display usage information */ + +static void usage(poptContext *pc) +{ + int i; + + poptPrintHelp(*pc, stderr, 0); + + fprintf(stderr, "\n"); + fprintf(stderr, "<destination> is one of \"nmbd\", \"smbd\" or a " + "process ID\n"); + + fprintf(stderr, "\n"); + fprintf(stderr, "<message-type> is one of:\n"); + + for (i = 0; msg_types[i].name; i++) + fprintf(stderr, "\t%-30s%s\n", msg_types[i].name, + msg_types[i].help); + + fprintf(stderr, "\n"); + + exit(1); +} + +/* Return the pid number for a string destination */ + +static pid_t parse_dest(const char *dest) +{ + pid_t pid; + + /* Zero is a special return value for broadcast smbd */ + + if (strequal(dest, "smbd")) + return 0; + + /* Try self - useful for testing */ + + if (strequal(dest, "self")) + return sys_getpid(); + + /* Check for numeric pid number */ + + if ((pid = atoi(dest)) != 0) + return pid; + + /* Look up other destinations in pidfile directory */ + + if ((pid = pidfile_pid(dest)) != 0) + return pid; + + fprintf(stderr,"Can't find pid for destination '%s'\n", dest); + + return -1; +} + +/* Execute smbcontrol command */ + +static BOOL do_command(int argc, const char **argv) +{ + const char *dest = argv[0], *command = argv[1]; + pid_t pid; + int i; + + /* Check destination */ + + if ((pid = parse_dest(dest)) == -1) + return False; + + /* Check command */ + + for (i = 0; msg_types[i].name; i++) { + if (strequal(command, msg_types[i].name)) + return msg_types[i].fn(pid, argc - 1, argv + 1); + } + + fprintf(stderr, "smbcontrol: unknown command '%s'\n", command); + + return False; +} + +/* Main program */ + +int main(int argc, const char **argv) +{ + poptContext pc; + int opt; + + static struct poptOption wbinfo_options[] = { + { "timeout", 't', POPT_ARG_INT, &timeout, 't', + "Set timeout value in seconds", "TIMEOUT" }, + + { "configfile", 's', POPT_ARG_STRING, NULL, 's', + "Use alternative configuration file", "CONFIGFILE" }, + + POPT_TABLEEND + }; + + struct poptOption options[] = { + { NULL, 0, POPT_ARG_INCLUDE_TABLE, wbinfo_options, 0, + "Options" }, + + POPT_AUTOHELP + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + setup_logging(argv[0],True); + + /* Parse command line arguments using popt */ + + pc = poptGetContext( + "smbcontrol", argc, (const char **)argv, options, 0); + + poptSetOtherOptionHelp(pc, "[OPTION...] <destination> <message-type> " + "<parameters>"); + + if (argc == 1) + usage(&pc); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case 't': /* --timeout */ + argc -= 2; + break; + case 's': /* --configfile */ + pstrcpy(dyn_CONFIGFILE, poptGetOptArg(pc)); + argc -= 2; + break; + default: + fprintf(stderr, "Invalid option\n"); + poptPrintHelp(pc, stderr, 0); + break; + } + } + + /* We should now have the remaining command line arguments in + argv. The argc parameter should have been decremented to the + correct value in the above switch statement. */ + + argv = (const char **)poptGetArgs(pc); + argc--; /* Don't forget about argv[0] */ + + if (argc == 1) + usage(&pc); + + lp_load(dyn_CONFIGFILE,False,False,False); + + /* Need to invert sense of return code -- samba + * routines mostly return True==1 for success, but + * shell needs 0. */ + + return !do_command(argc, argv); +} diff --git a/source/utils/smbcquotas.c b/source/utils/smbcquotas.c new file mode 100644 index 00000000000..0bd87554209 --- /dev/null +++ b/source/utils/smbcquotas.c @@ -0,0 +1,546 @@ +/* + Unix SMB/CIFS implementation. + QUOTA get/set utility + + Copyright (C) Andrew Tridgell 2000 + Copyright (C) Tim Potter 2000 + Copyright (C) Jeremy Allison 2000 + Copyright (C) Stefan (metze) Metzmacher 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 pstring server; + +/* numeric is set when the user wants numeric SIDs and ACEs rather + than going via LSA calls to resolve them */ +static BOOL numeric; +static BOOL verbose; + +enum todo_values {NOOP_QUOTA=0,FS_QUOTA,USER_QUOTA,LIST_QUOTA,SET_QUOTA}; +enum exit_values {EXIT_OK, EXIT_FAILED, EXIT_PARSE_ERROR}; + +static struct cli_state *cli_ipc = NULL; +static POLICY_HND pol; +static BOOL got_policy_hnd; + +static struct cli_state *connect_one(const char *share); + +/* Open cli connection and policy handle */ + +static BOOL cli_open_policy_hnd(void) +{ + /* Initialise cli LSA connection */ + + if (!cli_ipc) { + cli_ipc = connect_one("IPC$"); + if (!cli_nt_session_open (cli_ipc, 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(cli_ipc, cli_ipc->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, BOOL _numeric) +{ + 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 (!cli_open_policy_hnd() || + !NT_STATUS_IS_OK(cli_lsa_lookup_sids(cli_ipc, cli_ipc->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 (!cli_open_policy_hnd() || + !NT_STATUS_IS_OK(cli_lsa_lookup_names(cli_ipc, cli_ipc->mem_ctx, + &pol, 1, &str, &sids, + &types))) { + result = False; + goto done; + } + + sid_copy(sid, &sids[0]); + done: + + return result; +} + +#define QUOTA_GET 1 +#define QUOTA_SETLIM 2 +#define QUOTA_SETFLAGS 3 +#define QUOTA_LIST 4 + +enum {PARSE_FLAGS,PARSE_LIM}; + +static int parse_quota_set(pstring set_str, pstring username_str, enum SMB_QUOTA_TYPE *qtype, int *cmd, SMB_NTQUOTA_STRUCT *pqt) +{ + char *p = set_str,*p2; + int todo; + BOOL stop = False; + BOOL enable = False; + BOOL deny = False; + + if (strnequal(set_str,"UQLIM:",6)) { + p += 6; + *qtype = SMB_USER_QUOTA_TYPE; + *cmd = QUOTA_SETLIM; + todo = PARSE_LIM; + if ((p2=strstr(p,":"))==NULL) { + return -1; + } + + *p2 = '\0'; + p2++; + + fstrcpy(username_str,p); + p = p2; + } else if (strnequal(set_str,"FSQLIM:",7)) { + p +=7; + *qtype = SMB_USER_FS_QUOTA_TYPE; + *cmd = QUOTA_SETLIM; + todo = PARSE_LIM; + } else if (strnequal(set_str,"FSQFLAGS:",9)) { + p +=9; + todo = PARSE_FLAGS; + *qtype = SMB_USER_FS_QUOTA_TYPE; + *cmd = QUOTA_SETFLAGS; + } else { + return -1; + } + + switch (todo) { + case PARSE_LIM: +#if defined(HAVE_LONGLONG) + if (sscanf(p,"%llu/%llu",&pqt->softlim,&pqt->hardlim)!=2) { +#else + if (sscanf(p,"%lu/%lu",&pqt->softlim,&pqt->hardlim)!=2) { +#endif + return -1; + } + + break; + case PARSE_FLAGS: + while (!stop) { + + if ((p2=strstr(p,"/"))==NULL) { + stop = True; + } else { + *p2 = '\0'; + p2++; + } + + if (strnequal(p,"QUOTA_ENABLED",13)) { + enable = True; + } else if (strnequal(p,"DENY_DISK",9)) { + deny = True; + } else if (strnequal(p,"LOG_SOFTLIMIT",13)) { + pqt->qflags |= QUOTAS_LOG_THRESHOLD; + } else if (strnequal(p,"LOG_HARDLIMIT",13)) { + pqt->qflags |= QUOTAS_LOG_LIMIT; + } else { + return -1; + } + + p=p2; + } + + if (deny) { + pqt->qflags |= QUOTAS_DENY_DISK; + } else if (enable) { + pqt->qflags |= QUOTAS_ENABLED; + } + + break; + } + + return 0; +} + +static int do_quota(struct cli_state *cli, enum SMB_QUOTA_TYPE qtype, uint16 cmd, pstring username_str, SMB_NTQUOTA_STRUCT *pqt) +{ + uint32 fs_attrs = 0; + int quota_fnum = 0; + SMB_NTQUOTA_LIST *qtl = NULL; + SMB_NTQUOTA_STRUCT qt; + ZERO_STRUCT(qt); + + if (!cli_get_fs_attr_info(cli, &fs_attrs)) { + d_printf("Failed to get the filesystem attributes %s.\n", + cli_errstr(cli)); + return -1; + } + + if (!(fs_attrs & FILE_VOLUME_QUOTAS)) { + d_printf("Quotas are not supported by the server.\n"); + return 0; + } + + if (!cli_get_quota_handle(cli, "a_fnum)) { + d_printf("Failed to open \\%s %s.\n", + FAKE_FILE_NAME_QUOTA,cli_errstr(cli)); + return -1; + } + + switch(qtype) { + case SMB_USER_QUOTA_TYPE: + if (!StringToSid(&qt.sid, username_str)) { + d_printf("StringToSid() failed for [%s]\n",username_str); + return -1; + } + + switch(cmd) { + case QUOTA_GET: + if (!cli_get_user_quota(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_user_quota %s\n", + cli_errstr(cli),username_str); + return -1; + } + dump_ntquota(&qt,verbose,numeric,SidToString); + break; + case QUOTA_SETLIM: + pqt->sid = qt.sid; + if (!cli_set_user_quota(cli, quota_fnum, pqt)) { + d_printf("%s cli_set_user_quota %s\n", + cli_errstr(cli),username_str); + return -1; + } + if (!cli_get_user_quota(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_user_quota %s\n", + cli_errstr(cli),username_str); + return -1; + } + dump_ntquota(&qt,verbose,numeric,SidToString); + break; + case QUOTA_LIST: + if (!cli_list_user_quota(cli, quota_fnum, &qtl)) { + d_printf("%s cli_set_user_quota %s\n", + cli_errstr(cli),username_str); + return -1; + } + dump_ntquota_list(&qtl,verbose,numeric,SidToString); + free_ntquota_list(&qtl); + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + break; + case SMB_USER_FS_QUOTA_TYPE: + switch(cmd) { + case QUOTA_GET: + if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + case QUOTA_SETLIM: + if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + qt.softlim = pqt->softlim; + qt.hardlim = pqt->hardlim; + if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_set_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + case QUOTA_SETFLAGS: + if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + qt.qflags = pqt->qflags; + if (!cli_set_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_set_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + if (!cli_get_fs_quota_info(cli, quota_fnum, &qt)) { + d_printf("%s cli_get_fs_quota_info\n", + cli_errstr(cli)); + return -1; + } + dump_ntquota(&qt,True,numeric,NULL); + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + break; + default: + d_printf("Unknown Error\n"); + return -1; + } + + cli_close(cli, quota_fnum); + + return 0; +} + +/***************************************************** +return a connection to a server +*******************************************************/ +static struct cli_state *connect_one(const char *share) +{ + struct cli_state *c; + struct in_addr ip; + NTSTATUS nt_status; + zero_ip(&ip); + + if (!cmdline_auth_info.got_pass) { + char *pass = getpass("Password: "); + if (pass) { + pstrcpy(cmdline_auth_info.password, pass); + cmdline_auth_info.got_pass = True; + } + } + + if (NT_STATUS_IS_OK(nt_status = cli_full_connection(&c, global_myname(), server, + &ip, 0, + share, "?????", + cmdline_auth_info.username, lp_workgroup(), + cmdline_auth_info.password, 0, + cmdline_auth_info.signing_state, NULL))) { + return c; + } else { + DEBUG(0,("cli_full_connection failed! (%s)\n", nt_errstr(nt_status))); + return NULL; + } +} + +/**************************************************************************** + main program +****************************************************************************/ + int main(int argc, const char *argv[]) +{ + char *share; + int opt; + int result; + int todo = 0; + pstring username_str = {0}; + pstring path = {0}; + pstring set_str = {0}; + enum SMB_QUOTA_TYPE qtype; + int cmd = 0; + static BOOL test_args = False; + struct cli_state *cli; + BOOL fix_user = False; + SMB_NTQUOTA_STRUCT qt; + poptContext pc; + struct poptOption long_options[] = { + POPT_AUTOHELP + { "user", 'u', POPT_ARG_STRING, NULL, 'u', "Show quotas for user", "user" }, + { "list", 'L', POPT_ARG_NONE, NULL, 'L', "List user quotas" }, + { "fs", 'F', POPT_ARG_NONE, NULL, 'F', "Show filesystem quotas" }, + { "set", 'S', POPT_ARG_STRING, NULL, 'S', "Set acls\n\ +SETSTRING:\n\ +UQLIM:<username>/<softlimit>/<hardlimit> for user quotas\n\ +FSQLIM:<softlimit>/<hardlimit> for filesystem defaults\n\ +FSQFLAGS:QUOTA_ENABLED/DENY_DISK/LOG_SOFTLIMIT/LOG_HARD_LIMIT", "SETSTRING" }, + { "numeric", 'n', POPT_ARG_NONE, &numeric, True, "Don't resolve sids or limits to names" }, + { "verbose", 'v', POPT_ARG_NONE, &verbose, True, "be verbose" }, + { "test-args", 't', POPT_ARG_NONE, &test_args, True, "Test arguments"}, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + { NULL } + }; + + ZERO_STRUCT(qt); + + setlinebuf(stdout); + + dbf = x_stderr; + + fault_setup(NULL); + + setup_logging(argv[0],True); + + + lp_load(dyn_CONFIGFILE,True,False,False); + load_interfaces(); + + pc = poptGetContext("smbcquotas", argc, argv, long_options, 0); + + poptSetOtherOptionHelp(pc, "//server1/share1"); + + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'L': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + todo = LIST_QUOTA; + break; + + case 'F': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + todo = FS_QUOTA; + break; + + case 'u': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + pstrcpy(username_str,poptGetOptArg(pc)); + todo = USER_QUOTA; + fix_user = True; + break; + + case 'S': + if (todo != 0) { + d_printf("Please specify only one option of <-L|-F|-S|-u>\n"); + exit(EXIT_PARSE_ERROR); + } + pstrcpy(set_str,poptGetOptArg(pc)); + todo = SET_QUOTA; + break; + } + } + + if (todo == 0) + todo = USER_QUOTA; + + if (!fix_user) + pstrcpy(username_str,cmdline_auth_info.username); + + /* Make connection to server */ + if(!poptPeekArg(pc)) { + poptPrintUsage(pc, stderr, 0); + exit(EXIT_PARSE_ERROR); + } + + pstrcpy(path, poptGetArg(pc)); + + all_string_sub(path,"/","\\",0); + + pstrcpy(server,path+2); + share = strchr_m(server,'\\'); + if (!share) { + share = strchr_m(server,'/'); + if (!share) { + printf("Invalid argument: %s\n", share); + exit(EXIT_PARSE_ERROR); + } + } + + *share = 0; + share++; + + if (todo == SET_QUOTA) { + if (parse_quota_set(set_str, username_str, &qtype, &cmd, &qt)) { + printf("Invalid argument: -S %s\n", set_str); + exit(EXIT_PARSE_ERROR); + } + } + + if (!test_args) { + cli = connect_one(share); + if (!cli) { + exit(EXIT_FAILED); + } + } else { + exit(EXIT_OK); + } + + + /* Perform requested action */ + + switch (todo) { + case FS_QUOTA: + result = do_quota(cli,SMB_USER_FS_QUOTA_TYPE, QUOTA_GET, username_str, NULL); + break; + case LIST_QUOTA: + result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_LIST, username_str, NULL); + break; + case USER_QUOTA: + result = do_quota(cli,SMB_USER_QUOTA_TYPE, QUOTA_GET, username_str, NULL); + break; + case SET_QUOTA: + result = do_quota(cli, qtype, cmd, username_str, &qt); + break; + default: + + result = EXIT_FAILED; + break; + } + + return result; +} + diff --git a/source/utils/smbfilter.c b/source/utils/smbfilter.c new file mode 100644 index 00000000000..5d67c8fc7cf --- /dev/null +++ b/source/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 %ld bytes to %s\n", (unsigned long)length, fname); +} + +static void filter_reply(char *buf) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + unsigned x; + + if (msg_type) return; + + switch (type) { + + case SMBnegprot: + /* force the security bits */ + x = CVAL(buf, smb_vwv1); + x = (x | SECURITY_SET) & ~SECURITY_MASK; + SCVAL(buf, smb_vwv1, x); + + /* force the capabilities */ + x = IVAL(buf,smb_vwv9+1); + x = (x | CAPABILITY_SET) & ~CAPABILITY_MASK; + SIVAL(buf, smb_vwv9+1, x); + break; + + } +} + +static void filter_request(char *buf) +{ + int msg_type = CVAL(buf,0); + int type = CVAL(buf,smb_com); + 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/source/utils/smbget.c b/source/utils/smbget.c new file mode 100644 index 00000000000..64630bba8fd --- /dev/null +++ b/source/utils/smbget.c @@ -0,0 +1,589 @@ +/* + smbget: a wget-like utility with support for recursive downloading and + smb:// urls + Copyright (C) 2003-2004 Jelmer Vernooij <jelmer@samba.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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 "libsmbclient.h" + +#if _FILE_OFFSET_BITS==64 +#define OFF_T_FORMAT "%lld" +#else +#define OFF_T_FORMAT "%ld" +#endif + +int columns = 0; + +time_t total_start_time = 0; +off_t total_bytes = 0; + +#define SMB_MAXPATHLEN MAXPATHLEN + +/* Number of bytes to read when checking whether local and remote file are really the same file */ +#define RESUME_CHECK_SIZE 512 +#define RESUME_DOWNLOAD_OFFSET 1024 +#define RESUME_CHECK_OFFSET RESUME_DOWNLOAD_OFFSET+RESUME_CHECK_SIZE +/* Number of bytes to read at once */ +#define SMB_DEFAULT_BLOCKSIZE 64000 + +const char *username = NULL, *password = NULL, *workgroup = NULL; +int nonprompt = 0, quiet = 0, dots = 0, keep_permissions = 0, verbose = 0, send_stdout = 0; +int blocksize = SMB_DEFAULT_BLOCKSIZE; + +int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile); + +int get_num_cols(void) +{ +#ifdef TIOCGWINSZ + struct winsize ws; + if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0) { + return 0; + } + return ws.ws_col; +#else +#warning No support for TIOCGWINSZ + char *cols = getenv("COLUMNS"); + if(!cols) return 0; + return atoi(cols); +#endif +} + +void change_columns(int sig) +{ + columns = get_num_cols(); +} + +void human_readable(off_t s, char *buffer, int l) +{ + if(s > 1024 * 1024 * 1024) snprintf(buffer, l, "%.2fGb", 1.0 * s / (1024 * 1024 * 1024)); + else if(s > 1024 * 1024) snprintf(buffer, l, "%.2fMb", 1.0 * s / (1024 * 1024)); + else if(s > 1024) snprintf(buffer, l, "%.2fkb", 1.0 * s / 1024); + else snprintf(buffer, l, OFF_T_FORMAT"b", s); +} + +void get_auth_data(const char *srv, const char *shr, char *wg, int wglen, char *un, int unlen, char *pw, int pwlen) +{ + static char hasasked = 0; + char *wgtmp, *usertmp; + char tmp[128]; + + if(hasasked) return; + hasasked = 1; + + if(!nonprompt && !username) { + printf("Username for %s at %s [guest] ", shr, srv); + fgets(tmp, sizeof(tmp), stdin); + if(tmp[strlen(tmp)-1] == '\n')tmp[strlen(tmp)-1] = '\0'; + strncpy(un, tmp, unlen-1); + } else if(username) strncpy(un, username, unlen-1); + + if(!nonprompt && !password) { + char *prompt, *pass; + asprintf(&prompt, "Password for %s at %s: ", shr, srv); + pass = getpass(prompt); + free(prompt); + strncpy(pw, pass, pwlen-1); + } else if(password) strncpy(pw, password, pwlen-1); + + if(workgroup)strncpy(wg, workgroup, wglen-1); + + wgtmp = strndup(wg, wglen); + usertmp = strndup(un, unlen); + if(!quiet)printf("Using workgroup %s, %s%s\n", wgtmp, *usertmp?"user ":"guest user", usertmp); + free(wgtmp); free(usertmp); +} + +int smb_download_dir(const char *base, const char *name, int resume) +{ + char path[SMB_MAXPATHLEN]; + int dirhandle; + struct smbc_dirent *dirent; + const char *relname = name; + char *tmpname; + struct stat remotestat; + snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (base[0] && name[0] && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name); + + /* List files in directory and call smb_download_file on them */ + dirhandle = smbc_opendir(path); + if(dirhandle < 1) { + if(errno == ENOTDIR) return smb_download_file(base, name, 1, resume, NULL); + fprintf(stderr, "Can't open directory %s: %s\n", path, strerror(errno)); + return 0; + } + + while(*relname == '/')relname++; + mkdir(relname, 0755); + + tmpname = strdup(name); + + while((dirent = smbc_readdir(dirhandle))) { + char *newname; + if(!strcmp(dirent->name, ".") || !strcmp(dirent->name, ".."))continue; + asprintf(&newname, "%s/%s", tmpname, dirent->name); + switch(dirent->smbc_type) { + case SMBC_DIR: + smb_download_dir(base, newname, resume); + break; + + case SMBC_WORKGROUP: + smb_download_dir("smb://", dirent->name, resume); + break; + + case SMBC_SERVER: + smb_download_dir("smb://", dirent->name, resume); + break; + + case SMBC_FILE: + smb_download_file(base, newname, 1, resume, NULL); + break; + + case SMBC_FILE_SHARE: + smb_download_dir(base, newname, resume); + break; + + case SMBC_PRINTER_SHARE: + if(!quiet)printf("Ignoring printer share %s\n", dirent->name); + break; + + case SMBC_COMMS_SHARE: + if(!quiet)printf("Ignoring comms share %s\n", dirent->name); + break; + + case SMBC_IPC_SHARE: + if(!quiet)printf("Ignoring ipc$ share %s\n", dirent->name); + break; + + default: + fprintf(stderr, "Ignoring file '%s' of type '%d'\n", newname, dirent->smbc_type); + break; + } + free(newname); + } + free(tmpname); + + if(keep_permissions) { + if(smbc_fstat(dirhandle, &remotestat) < 0) { + fprintf(stderr, "Unable to get stats on %s on remote server\n", path); + smbc_closedir(dirhandle); + return 0; + } + + if(chmod(relname, remotestat.st_mode) < 0) { + fprintf(stderr, "Unable to change mode of local dir %s to %o\n", relname, remotestat.st_mode); + smbc_closedir(dirhandle); + return 0; + } + } + + smbc_closedir(dirhandle); + return 1; +} + +char *print_time(long t) +{ + static char buffer[100]; + int secs, mins, hours; + if(t < -1) { + strncpy(buffer, "Unknown", sizeof(buffer)); + return buffer; + } + + secs = (int)t % 60; + mins = (int)t / 60 % 60; + hours = (int)t / (60 * 60); + snprintf(buffer, sizeof(buffer)-1, "%02d:%02d:%02d", hours, mins, secs); + return buffer; +} + +void print_progress(const char *name, time_t start, time_t now, off_t start_pos, off_t pos, off_t total) +{ + double avg = 0.0; + long eta = -1; + double prcnt = 0.0; + char hpos[20], htotal[20], havg[20]; + char *status, *filename; + int len; + if(now - start)avg = 1.0 * (pos - start_pos) / (now - start); + eta = (total - pos) / avg; + if(total)prcnt = 100.0 * pos / total; + + human_readable(pos, hpos, sizeof(hpos)); + human_readable(total, htotal, sizeof(htotal)); + human_readable(avg, havg, sizeof(havg)); + + len = asprintf(&status, "%s of %s (%.2f%%) at %s/s ETA: %s", hpos, htotal, prcnt, havg, print_time(eta)); + + if(columns) { + int required = strlen(name), available = columns - len - strlen("[] "); + if(required > available) asprintf(&filename, "...%s", name + required - available + 3); + else filename = strndup(name, available); + } else filename = strdup(name); + + fprintf(stderr, "\r[%s] %s", filename, status); + + free(filename); free(status); +} + +int smb_download_file(const char *base, const char *name, int recursive, int resume, char *outfile) { + int remotehandle, localhandle; + time_t start_time = time(NULL); + const char *newpath; + char path[SMB_MAXPATHLEN]; + char checkbuf[2][RESUME_CHECK_SIZE]; + char *readbuf = NULL; + off_t offset_download = 0, offset_check = 0, curpos = 0, start_offset = 0; + struct stat localstat, remotestat; + + snprintf(path, SMB_MAXPATHLEN-1, "%s%s%s", base, (*base && *name && name[0] != '/' && base[strlen(base)-1] != '/')?"/":"", name); + + remotehandle = smbc_open(path, O_RDONLY, 0755); + + if(remotehandle < 0) { + switch(errno) { + case EISDIR: + if(!recursive) { + fprintf(stderr, "%s is a directory. Specify -R to download recursively\n", path); + return 0; + } + smb_download_dir(base, name, resume); + return 0; + + case ENOENT: + fprintf(stderr, "%s can't be found on the remote server\n", path); + return 0; + + case ENOMEM: + fprintf(stderr, "Not enough memory\n"); + exit(1); + return 0; + + case ENODEV: + fprintf(stderr, "The share name used in %s does not exist\n", path); + return 0; + + case EACCES: + fprintf(stderr, "You don't have enough permissions to access %s\n", path); + return 0; + + default: + perror("smbc_open"); + return 0; + } + } + + if(smbc_fstat(remotehandle, &remotestat) < 0) { + fprintf(stderr, "Can't stat %s: %s\n", path, strerror(errno)); + return 0; + } + + if(outfile) newpath = outfile; + else if(!name[0]) { + newpath = strrchr(base, '/'); + if(newpath)newpath++; else newpath = base; + } else newpath = name; + + if(newpath[0] == '/')newpath++; + + /* Open local file and, if necessary, resume */ + if(!send_stdout) { + localhandle = open(newpath, O_CREAT | O_NONBLOCK | O_RDWR | (!resume?O_EXCL:0), 0755); + if(localhandle < 0) { + fprintf(stderr, "Can't open %s: %s\n", newpath, strerror(errno)); + smbc_close(remotehandle); + return 0; + } + + fstat(localhandle, &localstat); + + start_offset = localstat.st_size; + + if(localstat.st_size && localstat.st_size == remotestat.st_size) { + if(verbose)fprintf(stderr, "%s is already downloaded completely.\n", path); + else if(!quiet)fprintf(stderr, "%s\n", path); + smbc_close(remotehandle); + close(localhandle); + return 1; + } + + if(localstat.st_size > RESUME_CHECK_OFFSET && remotestat.st_size > RESUME_CHECK_OFFSET) { + offset_download = localstat.st_size - RESUME_DOWNLOAD_OFFSET; + offset_check = localstat.st_size - RESUME_CHECK_OFFSET; + if(verbose)printf("Trying to start resume of %s at "OFF_T_FORMAT"\n" + "At the moment "OFF_T_FORMAT" of "OFF_T_FORMAT" bytes have been retrieved\n", newpath, offset_check, + localstat.st_size, remotestat.st_size); + } + + if(offset_check) { + off_t off1, off2; + /* First, check all bytes from offset_check to offset_download */ + off1 = lseek(localhandle, offset_check, SEEK_SET); + if(off1 < 0) { + fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in local file %s\n", offset_check, newpath); + smbc_close(remotehandle); close(localhandle); + return 0; + } + + off2 = smbc_lseek(remotehandle, offset_check, SEEK_SET); + if(off2 < 0) { + fprintf(stderr, "Can't seek to "OFF_T_FORMAT" in remote file %s\n", offset_check, newpath); + smbc_close(remotehandle); close(localhandle); + return 0; + } + + if(off1 != off2) { + fprintf(stderr, "Offset in local and remote files is different (local: "OFF_T_FORMAT", remote: "OFF_T_FORMAT")\n", off1, off2); + return 0; + } + + if(smbc_read(remotehandle, checkbuf[0], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) { + fprintf(stderr, "Can't read %d bytes from remote file %s\n", RESUME_CHECK_SIZE, path); + smbc_close(remotehandle); close(localhandle); + return 0; + } + + if(read(localhandle, checkbuf[1], RESUME_CHECK_SIZE) != RESUME_CHECK_SIZE) { + fprintf(stderr, "Can't read %d bytes from local file %s\n", RESUME_CHECK_SIZE, name); + smbc_close(remotehandle); close(localhandle); + return 0; + } + + if(memcmp(checkbuf[0], checkbuf[1], RESUME_CHECK_SIZE) == 0) { + if(verbose)printf("Current local and remote file appear to be the same. Starting download from offset "OFF_T_FORMAT"\n", offset_download); + } else { + fprintf(stderr, "Local and remote file appear to be different, not doing resume for %s\n", path); + smbc_close(remotehandle); close(localhandle); + return 0; + } + } + } else { + localhandle = STDOUT_FILENO; + start_offset = 0; + offset_download = 0; + offset_check = 0; + } + + readbuf = malloc(blocksize); + + /* Now, download all bytes from offset_download to the end */ + for(curpos = offset_download; curpos < remotestat.st_size; curpos+=blocksize) { + ssize_t bytesread = smbc_read(remotehandle, readbuf, blocksize); + if(bytesread < 0) { + fprintf(stderr, "Can't read %d bytes at offset "OFF_T_FORMAT", file %s\n", blocksize, curpos, path); + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) close(localhandle); + free(readbuf); + return 0; + } + + total_bytes += bytesread; + + if(write(localhandle, readbuf, bytesread) < 0) { + fprintf(stderr, "Can't write %d bytes to local file %s at offset "OFF_T_FORMAT"\n", bytesread, path, curpos); + free(readbuf); + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) close(localhandle); + return 0; + } + + if(dots)fputc('.', stderr); + else if(!quiet) { + print_progress(newpath, start_time, time(NULL), start_offset, curpos, remotestat.st_size); + } + } + + free(readbuf); + + if(dots){ + fputc('\n', stderr); + printf("%s downloaded\n", path); + } else if(!quiet) { + int i; + fprintf(stderr, "\r%s", path); + if(columns) { + for(i = strlen(path); i < columns; i++) { + fputc(' ', stderr); + } + } + fputc('\n', stderr); + } + + if(keep_permissions && !send_stdout) { + if(fchmod(localhandle, remotestat.st_mode) < 0) { + fprintf(stderr, "Unable to change mode of local file %s to %o\n", path, remotestat.st_mode); + smbc_close(remotehandle); + close(localhandle); + return 0; + } + } + + smbc_close(remotehandle); + if (localhandle != STDOUT_FILENO) close(localhandle); + return 1; +} + +void clean_exit(void) +{ + char bs[100]; + human_readable(total_bytes, bs, sizeof(bs)); + if(!quiet)fprintf(stderr, "Downloaded %s in %lu seconds\n", bs, time(NULL) - total_start_time); + exit(0); +} + +void signal_quit(int v) +{ + clean_exit(); +} + +int readrcfile(const char *name, const struct poptOption long_options[]) +{ + FILE *fd = fopen(name, "r"); + int lineno = 0, i; + char var[101], val[101]; + char found; + int *intdata; char **stringdata; + if(!fd) { + fprintf(stderr, "Can't open RC file %s\n", name); + return 1; + } + + while(!feof(fd)) { + lineno++; + if(fscanf(fd, "%100s %100s\n", var, val) < 2) { + fprintf(stderr, "Can't parse line %d of %s, ignoring.\n", lineno, name); + continue; + } + + found = 0; + + for(i = 0; long_options[i].shortName; i++) { + if(!long_options[i].longName)continue; + if(strcmp(long_options[i].longName, var)) continue; + if(!long_options[i].arg)continue; + + switch(long_options[i].argInfo) { + case POPT_ARG_NONE: + intdata = (int *)long_options[i].arg; + if(!strcmp(val, "on")) *intdata = 1; + else if(!strcmp(val, "off")) *intdata = 0; + else fprintf(stderr, "Illegal value %s for %s at line %d in %s\n", val, var, lineno, name); + break; + case POPT_ARG_INT: + intdata = (int *)long_options[i].arg; + *intdata = atoi(val); + break; + case POPT_ARG_STRING: + stringdata = (char **)long_options[i].arg; + *stringdata = strdup(val); + break; + default: + fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name); + break; + } + + found = 1; + } + if(!found) { + fprintf(stderr, "Invalid variable %s at line %d in %s\n", var, lineno, name); + } + } + + fclose(fd); + return 0; +} + +int main(int argc, const char **argv) +{ + int resume = 0, recursive = 0; + int c = 0; + int debuglevel = 0; + const char *file = NULL; + char *rcfile = NULL; + char *outputfile = NULL; + struct poptOption long_options[] = { + {"guest", 'a', POPT_ARG_NONE, NULL, 'a', "Work as user guest" }, + {"resume", 'r', POPT_ARG_NONE, &resume, 0, "Automatically resume aborted files" }, + {"recursive", 'R', POPT_ARG_NONE, &recursive, 0, "Recursively download files" }, + {"username", 'u', POPT_ARG_STRING, &username, 'u', "Username to use" }, + {"password", 'p', POPT_ARG_STRING, &password, 'p', "Password to use" }, + {"workgroup", 'w', POPT_ARG_STRING, &workgroup, 'w', "Workgroup to use (optional)" }, + {"nonprompt", 'n', POPT_ARG_NONE, &nonprompt, 'n', "Don't ask anything (non-interactive)" }, + {"debuglevel", 'd', POPT_ARG_INT, &debuglevel, 'd', "Debuglevel to use" }, + {"outputfile", 'o', POPT_ARG_STRING, &outputfile, 'o', "Write downloaded data to specified file" }, + {"stdout", 'O', POPT_ARG_NONE, &send_stdout, 'O', "Write data to stdout" }, + {"dots", 'D', POPT_ARG_NONE, &dots, 'D', "Show dots as progress indication" }, + {"quiet", 'q', POPT_ARG_NONE, &quiet, 'q', "Be quiet" }, + {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v', "Be verbose" }, + {"keep-permissions", 'P', POPT_ARG_NONE, &keep_permissions, 'P', "Keep permissions" }, + {"blocksize", 'b', POPT_ARG_INT, &blocksize, 'b', "Change number of bytes in a block"}, + {"rcfile", 'f', POPT_ARG_STRING, NULL, 0, "Use specified rc file"}, + POPT_AUTOHELP + POPT_TABLEEND + }; + poptContext pc; + + /* only read rcfile if it exists */ + asprintf(&rcfile, "%s/.smbgetrc", getenv("HOME")); + if(access(rcfile, F_OK) == 0) readrcfile(rcfile, long_options); + free(rcfile); + +#ifdef SIGWINCH + signal(SIGWINCH, change_columns); +#endif + signal(SIGINT, signal_quit); + signal(SIGTERM, signal_quit); + + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + + while((c = poptGetNextOpt(pc)) >= 0) { + switch(c) { + case 'f': + readrcfile(poptGetOptArg(pc), long_options); + break; + case 'a': + username = ""; password = ""; + break; + } + } + + if((send_stdout || outputfile) && recursive) { + fprintf(stderr, "The -o or -O and -R options can not be used together.\n"); + return 1; + } + + if(outputfile && send_stdout) { + fprintf(stderr, "The -o and -O options cannot be used together.\n"); + return 1; + } + + if(smbc_init(get_auth_data, debuglevel) < 0) { + fprintf(stderr, "Unable to initialize libsmbclient\n"); + return 1; + } + + columns = get_num_cols(); + + total_start_time = time(NULL); + + while((file = poptGetArg(pc))) { + if(!recursive) return smb_download_file(file, "", recursive, resume, outputfile); + else return smb_download_dir(file, "", resume); + } + + clean_exit(); + + return 0; +} diff --git a/source/utils/smbpasswd.c b/source/utils/smbpasswd.c new file mode 100644 index 00000000000..0476a2e39c0 --- /dev/null +++ b/source/utils/smbpasswd.c @@ -0,0 +1,614 @@ +/* + * 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 PASSWORD ldap admin password\n"); + printf(" -x delete user\n"); + printf(" -R ORDER name resolve order\n"); + + exit(1); +} + +static void set_line_buffering(FILE *f) +{ + setvbuf(f, NULL, _IOLBF, 0); +} + +/******************************************************************* + Process command line options + ******************************************************************/ +static int process_options(int argc, char **argv, int local_flags) +{ + int ch; + 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 passdb startup(). */ + if(!initialize_password_db(False)) { + DEBUG(0, ("Failed to open passdb!\n")); + exit(1); + } + + /* Ensure we have a SAM sid. */ + get_global_sam_sid(); + + /* + * Ensure both add/delete user are not set + * Ensure add/delete user and either remote machine or join domain are + * not both set. + */ + if(((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) == (LOCAL_ADD_USER|LOCAL_DELETE_USER)) || + ((local_flags & (LOCAL_ADD_USER|LOCAL_DELETE_USER)) && + (remote_machine != NULL))) { + usage(); + } + + /* Only load interfaces if we are doing network operations. */ + + if (remote_machine) { + load_interfaces(); + } + + if (!user_name[0] && (pwd = getpwuid_alloc(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_m(new_passwd); + } + + /* + * Now ensure the username ends in '$' for + * the machine add. + */ + + slprintf(buf, sizeof(buf)-1, "%s$", user_name); + fstrcpy(user_name, buf); + } else if (local_flags & LOCAL_INTERDOM_ACCOUNT) { + static fstring buf; + + if ((local_flags & LOCAL_ADD_USER) && (new_passwd == NULL)) { + /* + * Prompt for trusting domain's account password + */ + new_passwd = prompt_for_new_password(stdin_passwd_get); + if(!new_passwd) { + fprintf(stderr, "Unable to get newpassword.\n"); + exit(1); + } + } + + /* prepare uppercased and '$' terminated username */ + slprintf(buf, sizeof(buf) - 1, "%s$", user_name); + fstrcpy(user_name, buf); + + } else { + + if (remote_machine != NULL) { + old_passwd = get_pass("Old SMB password:",stdin_passwd_get); + } + + if (!(local_flags & LOCAL_SET_PASSWORD)) { + + /* + * If we are trying to enable a user, first we need to find out + * if they are using a modern version of the smbpasswd file that + * disables a user by just writing a flag into the file. If so + * then we can re-enable a user without prompting for a new + * password. If not (ie. they have a no stored password in the + * smbpasswd file) then we need to prompt for a new password. + */ + + if(local_flags & LOCAL_ENABLE_USER) { + 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 == NULL)) { + 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/source/utils/smbtree.c b/source/utils/smbtree.c new file mode 100644 index 00000000000..cbe1bd448f8 --- /dev/null +++ b/source/utils/smbtree.c @@ -0,0 +1,237 @@ +/* + Unix SMB/CIFS implementation. + Network neighbourhood browser. + + Copyright (C) Tim Potter 2000 + Copyright (C) Jelmer Vernooij 2003 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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; + +/* How low can we go? */ + +enum tree_level {LEV_WORKGROUP, LEV_SERVER, LEV_SHARE}; +static enum tree_level level = LEV_SHARE; + +/* Holds a list of workgroups or servers */ + +struct name_list { + struct name_list *prev, *next; + 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); +} + +/**************************************************************************** + 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, falling back to broadcast\n", + master_workgroup)); + use_bcast = True; + } else if(!use_bcast) { + if (!(cli = get_ipc_connect(inet_ntoa(server_ip), &server_ip, user_info))) + return False; + } + + if (!(cli = get_ipc_connect_master_ip_bcast(master_workgroup, user_info))) { + DEBUG(4, ("Unable to find master browser by " + "broadcast\n")); + return False; + } + + if (!cli_NetServerEnum(cli, master_workgroup, + SV_TYPE_DOMAIN_ENUM, add_name, &workgroups)) + return False; + + return True; +} + +/* Retrieve the list of servers for a given workgroup */ + +static BOOL get_servers(char *workgroup, struct user_auth_info *user_info) +{ + struct cli_state *cli; + struct 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[]) +{ + struct poptOption long_options[] = { + POPT_AUTOHELP + { "broadcast", 'b', POPT_ARG_VAL, &use_bcast, True, "Use broadcast instead of using the master browser" }, + { "domains", 'D', POPT_ARG_VAL, &level, LEV_WORKGROUP, "List only domains (workgroups) of tree" }, + { "servers", 'S', POPT_ARG_VAL, &level, LEV_SERVER, "List domains(workgroups) and servers of tree" }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_TABLEEND + }; + poptContext pc; + + /* Initialise samba stuff */ + + setlinebuf(stdout); + + dbf = x_stderr; + + setup_logging(argv[0],True); + + pc = poptGetContext("smbtree", argc, (const char **)argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + while(poptGetNextOpt(pc) != -1); + poptFreeContext(pc); + + lp_load(dyn_CONFIGFILE,True,False,False); + load_interfaces(); + + /* Parse command line args */ + + if (!cmdline_auth_info.got_pass) { + char *pass = getpass("Password: "); + if (pass) { + pstrcpy(cmdline_auth_info.password, pass); + } + cmdline_auth_info.got_pass = True; + } + + /* Now do our stuff */ + + if (!print_tree(&cmdline_auth_info)) + return 1; + + return 0; +} diff --git a/source/utils/smbw_sample.c b/source/utils/smbw_sample.c new file mode 100644 index 00000000000..5cd792df7a2 --- /dev/null +++ b/source/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/source/utils/status.c b/source/utils/status.c new file mode 100644 index 00000000000..4585b101b28 --- /dev/null +++ b/source/utils/status.c @@ -0,0 +1,693 @@ +/* + 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 + */ + +#define NO_SYSLOG + +#include "includes.h" + +#define SMB_MAXPIDS 2048 +static pstring Ucrit_username = ""; /* added by OH */ +static pid_t Ucrit_pid[SMB_MAXPIDS]; /* Ugly !!! */ /* added by OH */ +static int Ucrit_MaxPid=0; /* added by OH */ +static unsigned int Ucrit_IsActive = 0; /* added by OH */ + +static 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; + +/* 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 BOOL Ucrit_addPid( pid_t pid ) +{ + if ( !Ucrit_IsActive ) + return True; + + if ( Ucrit_MaxPid >= SMB_MAXPIDS ) { + d_printf("ERROR: More than %d pids for user %s!\n", + SMB_MAXPIDS, Ucrit_username); + + return False; + } + + Ucrit_pid[Ucrit_MaxPid++] = pid; + + return True; +} + +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); + d_printf("pread_count: %u\n", profile_p->syscall_pread_count); + d_printf("pread_time: %u\n", profile_p->syscall_pread_time); + d_printf("pread_bytes: %u\n", profile_p->syscall_pread_bytes); + d_printf("pwrite_count: %u\n", profile_p->syscall_pwrite_count); + d_printf("pwrite_time: %u\n", profile_p->syscall_pwrite_time); + d_printf("pwrite_bytes: %u\n", profile_p->syscall_pwrite_bytes); +#ifdef WITH_SENDFILE + d_printf("sendfile_count: %u\n", profile_p->syscall_sendfile_count); + d_printf("sendfile_time: %u\n", profile_p->syscall_sendfile_time); + d_printf("sendfile_bytes: %u\n", profile_p->syscall_sendfile_bytes); +#endif + d_printf("lseek_count: %u\n", profile_p->syscall_lseek_count); + d_printf("lseek_time: %u\n", profile_p->syscall_lseek_time); + d_printf("rename_count: %u\n", profile_p->syscall_rename_count); + d_printf("rename_time: %u\n", profile_p->syscall_rename_time); + d_printf("fsync_count: %u\n", profile_p->syscall_fsync_count); + d_printf("fsync_time: %u\n", profile_p->syscall_fsync_time); + d_printf("stat_count: %u\n", profile_p->syscall_stat_count); + d_printf("stat_time: %u\n", profile_p->syscall_stat_time); + d_printf("fstat_count: %u\n", profile_p->syscall_fstat_count); + d_printf("fstat_time: %u\n", profile_p->syscall_fstat_time); + d_printf("lstat_count: %u\n", profile_p->syscall_lstat_count); + d_printf("lstat_time: %u\n", profile_p->syscall_lstat_time); + d_printf("unlink_count: %u\n", profile_p->syscall_unlink_count); + d_printf("unlink_time: %u\n", profile_p->syscall_unlink_time); + d_printf("chmod_count: %u\n", profile_p->syscall_chmod_count); + d_printf("chmod_time: %u\n", profile_p->syscall_chmod_time); + d_printf("fchmod_count: %u\n", profile_p->syscall_fchmod_count); + d_printf("fchmod_time: %u\n", profile_p->syscall_fchmod_time); + d_printf("chown_count: %u\n", profile_p->syscall_chown_count); + d_printf("chown_time: %u\n", profile_p->syscall_chown_time); + d_printf("fchown_count: %u\n", profile_p->syscall_fchown_count); + d_printf("fchown_time: %u\n", profile_p->syscall_fchown_time); + d_printf("chdir_count: %u\n", profile_p->syscall_chdir_count); + d_printf("chdir_time: %u\n", profile_p->syscall_chdir_time); + d_printf("getwd_count: %u\n", profile_p->syscall_getwd_count); + d_printf("getwd_time: %u\n", profile_p->syscall_getwd_time); + d_printf("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; + + 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; + } + + Ucrit_addPid( sessionid.pid ); + + d_printf("%5d %-12s %-12s %-12s (%s)\n", + (int)sessionid.pid, uidtoname(sessionid.uid), gidtoname(sessionid.gid), + sessionid.remote_machine, sessionid.hostname); + + 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_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)) != -1) { + 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_STRING); + 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/source/utils/testparm.c b/source/utils/testparm.c new file mode 100644 index 00000000000..9db6d538d24 --- /dev/null +++ b/source/utils/testparm.c @@ -0,0 +1,367 @@ +/* + 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()) { + fprintf(stderr, "ERROR: in 'security=domain' mode the 'encrypt passwords' parameter must always be set to 'true'.\n"); + ret = 1; + } + + if (lp_wins_support() && lp_wins_server_list()) { + fprintf(stderr, "ERROR: both 'wins support = true' and 'wins server = <server list>' \ +cannot be set in the smb.conf file. nmbd will abort with this setting.\n"); + ret = 1; + } + + if (!directory_exist(lp_lockdir(), &st)) { + fprintf(stderr, "ERROR: lock directory %s does not exist\n", + lp_lockdir()); + ret = 1; + } else if ((st.st_mode & 0777) != 0755) { + fprintf(stderr, "WARNING: lock directory %s should have permissions 0755 for browsing to work\n", + lp_lockdir()); + ret = 1; + } + + if (!directory_exist(lp_piddir(), &st)) { + fprintf(stderr, "ERROR: pid directory %s does not exist\n", + lp_piddir()); + ret = 1; + } + + /* + * 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"); + + fprintf(stderr, "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()) { + fprintf(stderr, "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) { + fprintf( stderr, "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) { + fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and the 'passwd program' (%s) \ +cannot be executed (error was %s).\n", truncated_prog, strerror(errno) ); + ret = 1; + } + } + +#ifdef WITH_PAM + } +#endif + + if(lp_passwd_chat() == NULL) { + fprintf(stderr, "ERROR: the 'unix password sync' parameter is set and there is no valid 'passwd chat' \ +parameter.\n"); + ret = 1; + } + + /* + * Check that we have a valid script and that it hasn't + * been written to expect the old password. + */ + + if(lp_encrypted_passwords()) { + if(strstr_m( lp_passwd_chat(), "%o")!=NULL) { + fprintf(stderr, "ERROR: the 'passwd chat' script [%s] expects to use the old plaintext password \ +via the %%o substitution. With encrypted passwords this is not possible.\n", lp_passwd_chat() ); + ret = 1; + } + } + } + + if (strlen(lp_winbind_separator()) != 1) { + fprintf(stderr,"ERROR: the 'winbind separator' parameter must be a single character.\n"); + ret = 1; + } + + if (*lp_winbind_separator() == '+') { + fprintf(stderr,"'winbind separator = +' might cause problems with group membership.\n"); + } + + if (lp_algorithmic_rid_base() < BASE_RID) { + /* Try to prevent admin foot-shooting, we can't put algorithmic + rids below 1000, that's the 'well known RIDs' on NT */ + fprintf(stderr,"'algorithmic rid base' must be equal to or above %lu\n", BASE_RID); + } + + if (lp_algorithmic_rid_base() & 1) { + fprintf(stderr,"'algorithmic rid base' must be even.\n"); + } + +#ifndef HAVE_DLOPEN + if (lp_preload_modules()) { + fprintf(stderr,"WARNING: 'preload modules = ' set while loading plugins not supported.\n"); + } +#endif + + if (!lp_passdb_backend()) { + fprintf(stderr,"ERROR: passdb backend must have a value or be left out\n"); + } + + return ret; +} + + int main(int argc, const char *argv[]) +{ + const char *config_file = dyn_CONFIGFILE; + int s; + static BOOL silent_mode = False; + int ret = 0; + 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"}, + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + pc = poptGetContext(NULL, argc, argv, long_options, + POPT_CONTEXT_KEEP_FIRST); + poptSetOtherOptionHelp(pc, "[OPTION...] <config-file> [host-name] [host-ip]"); + + while(poptGetNextOpt(pc) != -1); + + setup_logging(poptGetArg(pc), True); + + if (poptPeekArg(pc)) + config_file = poptGetArg(pc); + + cname = poptGetArg(pc); + caddr = poptGetArg(pc); + + if ( cname && ! caddr ) { + printf ( "ERROR: You must specify both a machine name and an IP address.\n" ); + return(1); + } + + if (new_local_machine) { + set_local_machine_name(new_local_machine, True); + } + + dbf = x_stderr; + DEBUGLEVEL = 2; + AllowDebugChange = False; + + fprintf(stderr,"Load smb config files from %s\n",config_file); + + if (!lp_load(config_file,False,True,False)) { + fprintf(stderr,"Error loading services.\n"); + return(1); + } + + fprintf(stderr,"Loaded services file OK.\n"); + + ret = do_global_checks(); + + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) + if (strlen(lp_servicename(s)) > 12) { + fprintf(stderr, "WARNING: You have some share names that are longer than 12 characters.\n" ); + fprintf(stderr, "These may not be accessible to some older clients.\n" ); + fprintf(stderr, "(Eg. Windows9x, WindowsMe, and smbclient prior to Samba 3.0.)\n" ); + break; + } + } + + for (s=0;s<1000;s++) { + if (VALID_SNUM(s)) { + const char **deny_list = lp_hostsdeny(s); + const char **allow_list = lp_hostsallow(s); + int i; + if(deny_list) { + for (i=0; deny_list[i]; i++) { + char *hasstar = strchr_m(deny_list[i], '*'); + char *hasquery = strchr_m(deny_list[i], '?'); + if(hasstar || hasquery) { + fprintf(stderr,"Invalid character %c in hosts deny list (%s) for service %s.\n", + hasstar ? *hasstar : *hasquery, deny_list[i], lp_servicename(s) ); + } + } + } + + if(allow_list) { + for (i=0; allow_list[i]; i++) { + char *hasstar = strchr_m(allow_list[i], '*'); + char *hasquery = strchr_m(allow_list[i], '?'); + if(hasstar || hasquery) { + fprintf(stderr,"Invalid character %c in hosts allow list (%s) for service %s.\n", + hasstar ? *hasstar : *hasquery, allow_list[i], lp_servicename(s) ); + } + } + } + + if(lp_level2_oplocks(s) && !lp_oplocks(s)) { + fprintf(stderr,"Invalid combination of parameters for service %s. \ + Level II oplocks can only be set if oplocks are also set.\n", + lp_servicename(s) ); + } + + if (lp_map_hidden(s) && !(lp_create_mask(s) & S_IXOTH)) { + fprintf(stderr,"Invalid combination of parameters for service %s. \ + Map hidden can only work if create mask includes octal 01 (S_IXOTH).\n", + lp_servicename(s) ); + } + if (lp_map_hidden(s) && (lp_force_create_mode(s) & S_IXOTH)) { + fprintf(stderr,"Invalid combination of parameters for service %s. \ + Map hidden can only work if force create mode excludes octal 01 (S_IXOTH).\n", + lp_servicename(s) ); + } + if (lp_map_system(s) && !(lp_create_mask(s) & S_IXGRP)) { + fprintf(stderr,"Invalid combination of parameters for service %s. \ + Map system can only work if create mask includes octal 010 (S_IXGRP).\n", + lp_servicename(s) ); + } + if (lp_map_system(s) && (lp_force_create_mode(s) & S_IXGRP)) { + fprintf(stderr,"Invalid combination of parameters for service %s. \ + Map system can only work if force create mode excludes octal 010 (S_IXGRP).\n", + lp_servicename(s) ); + } + } + } + + + if (!silent_mode) { + fprintf(stderr,"Server role: "); + switch(lp_server_role()) { + case ROLE_STANDALONE: + fprintf(stderr,"ROLE_STANDALONE\n"); + break; + case ROLE_DOMAIN_MEMBER: + fprintf(stderr,"ROLE_DOMAIN_MEMBER\n"); + break; + case ROLE_DOMAIN_BDC: + fprintf(stderr,"ROLE_DOMAIN_BDC\n"); + break; + case ROLE_DOMAIN_PDC: + fprintf(stderr,"ROLE_DOMAIN_PDC\n"); + break; + default: + fprintf(stderr,"Unknown -- internal error?\n"); + break; + } + } + + if (!cname) { + if (!silent_mode) { + fprintf(stderr,"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(-1), lp_hostsallow(-1), cname, caddr) + && allow_access(lp_hostsdeny(s), lp_hostsallow(s), cname, caddr)) { + fprintf(stderr,"Allow connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(s)); + } else { + fprintf(stderr,"Deny connection from %s (%s) to %s\n", + cname,caddr,lp_servicename(s)); + } + } + } + } + return(ret); +} diff --git a/source/utils/testprns.c b/source/utils/testprns.c new file mode 100644 index 00000000000..7e52b86afb6 --- /dev/null +++ b/source/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); +} |