From bcfe04afcde6ab57125502ba8597f80a8302db34 Mon Sep 17 00:00:00 2001 From: Gerald Carter Date: Sat, 10 Jul 2004 00:27:02 +0000 Subject: r1427: getting nmbd to build; still missing NetServerEnum() client call; i know this break the smbclient build; reverted to the 3.0 debug system and will look at merging debug.c from 4.0 later --- SAMBA_3_2_MERGE/source/Makefile.in | 34 +- SAMBA_3_2_MERGE/source/include/debug.h | 207 ++- SAMBA_3_2_MERGE/source/include/safe_string.h | 18 + SAMBA_3_2_MERGE/source/lib/debug.c | 1025 ++++++++++-- SAMBA_3_2_MERGE/source/lib/util.c | 10 + SAMBA_3_2_MERGE/source/lib/util_str.c | 4 + SAMBA_3_2_MERGE/source/nmbd/nmbd_lmhosts.c | 102 ++ SAMBA_3_2_MERGE/source/nmbd/nmbd_sendannounce.c | 599 +++++++ SAMBA_3_2_MERGE/source/nmbd/nmbd_synclists.c | 310 ++++ SAMBA_3_2_MERGE/source/nmbd/nmbd_winsserver.c | 2034 +++++++++++++++++++++++ SAMBA_3_2_MERGE/source/param/loadparm.c | 11 + SAMBA_3_2_MERGE/source/smbd/chgpasswd.c | 1007 +++++++++++ SAMBA_3_2_MERGE/source/smbd/session.c | 2 +- SAMBA_3_2_MERGE/source/utils/testparm.c | 367 ++++ 14 files changed, 5593 insertions(+), 137 deletions(-) create mode 100644 SAMBA_3_2_MERGE/source/nmbd/nmbd_lmhosts.c create mode 100644 SAMBA_3_2_MERGE/source/nmbd/nmbd_sendannounce.c create mode 100644 SAMBA_3_2_MERGE/source/nmbd/nmbd_synclists.c create mode 100644 SAMBA_3_2_MERGE/source/nmbd/nmbd_winsserver.c create mode 100644 SAMBA_3_2_MERGE/source/smbd/chgpasswd.c create mode 100644 SAMBA_3_2_MERGE/source/utils/testparm.c diff --git a/SAMBA_3_2_MERGE/source/Makefile.in b/SAMBA_3_2_MERGE/source/Makefile.in index cd09f69a3d0..e7e643b3a92 100644 --- a/SAMBA_3_2_MERGE/source/Makefile.in +++ b/SAMBA_3_2_MERGE/source/Makefile.in @@ -209,7 +209,8 @@ LIB_OBJ = $(VERSION_OBJ) lib/charcnv.o lib/debug.o lib/fault.o \ lib/pam_errors.o intl/lang_tdb.o lib/account_pol.o \ lib/adt_tree.o lib/gencache.o $(TDB_OBJ) \ lib/module.o lib/ldap_escape.o @CHARSET_STATIC@ \ - lib/privileges.o lib/secdesc.o lib/secace.o lib/secacl.o + lib/privileges.o lib/secdesc.o lib/secace.o lib/secacl.o \ + lib/mutex.o # lib/genparser.o lib/genparser_samba.o LIB_SMBD_OBJ = lib/system_smbd.o lib/util_smbd.o @@ -348,8 +349,10 @@ LIBCLI_OBJ1 = libcli/cliconnect.o libcli/clideltree.o \ libcli/clifile.o libcli/clilist.o libcli/climessage.o \ libcli/clireadwrite.o libcli/clitrans2.o -LIBCLI_OBJ = $(LIBCLI_OBJ1) $(LIBCLI_UTILS_OBJ) $(LIBCLI_AUTH_OBJ) \ - $(LIBCLI_RAW_OBJ) +LIBCLI_OBJ2 = lib/select.o lib/genrand.o + +LIBCLI_OBJ = $(LIBCLI_OBJ1) $(LIBCLI_OBJ2) $(LIBCLI_UTILS_OBJ) $(LIBCLI_AUTH_OBJ) \ + $(LIBCLI_RAW_OBJ) LIBMSRPC_OBJ = rpc_client/cli_lsarpc.o rpc_client/cli_samr.o \ rpc_client/cli_netlogon.o rpc_client/cli_srvsvc.o \ @@ -524,9 +527,24 @@ NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \ nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \ nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o -NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \ +TALLOC_OBJ = lib/talloc.o lib/mutex.o lib/tallocmsg.o + +NMBD_OBJ2 = lib/fault.o lib/cmdline/popt_common.o lib/debug.o \ + lib/util_str.o lib/util.o lib/wins_srv.o lib/util_sock.o \ + lib/charcnv.o lib/util_file.o lib/interface.o lib/time.o \ + lib/system.o lib/signal.o lib/data_blob.o lib/gencache.o \ + lib/md5.o lib/md4.o lib/hmacmd5.o lib/sock_exec.o lib/util_unistr.o \ + lib/substitute.o lib/crc32.o lib/xfile.o lib/interfaces.o \ + lib/ms_fnmatch.o lib/iconv.o \ + $(TALLOC_OBJ) + +NMBD_OBJ = $(NMBD_OBJ1) $(NMBD_OBJ2) $(PARAM_OBJ) $(LIBCLI_OBJ) $(KRBCLIENT_OBJ) $(UBIQX_OBJ) \ $(PROFILE_OBJ) $(LIB_NONSMBD_OBJ) $(SECRETS_OBJ) $(POPT_LIB_OBJ) +NMBD_OBJ = $(NMBD_OBJ1) $(BASIC_OBJ) $(PARAM_OBJ) $(LIBCLI_OBJ) $(PROFILE_OBJ) \ + $(UBIQX_OBJ) $(LIBRPC_OBJ) $(LIBNMB_OBJ) $(TDB_OBJ) nsswitch/wb_client.o \ + nsswitch/wb_common.o lib/cmdline/popt_common.o lib/fault.o + WREPL_OBJ1 = wrepld/server.o wrepld/process.o wrepld/parser.o wrepld/socket.o \ wrepld/partners.o @@ -632,13 +650,13 @@ BASIC_OBJ = lib/util_str.o lib/debug.o lib/xfile.o lib/util.o \ lib/charcnv.o lib/data_blob.o lib/util_unistr.o lib/iconv.o \ lib/util_sock.o lib/module.o lib/substitute.o lib/gencache.o \ lib/wins_srv.o lib/version.o lib/hmacmd5.o lib/username.o \ - lib/md5.o lib/util_pw.o lib/select.o lib/util_getent.o lib/sock_exec.o \ + lib/md5.o lib/util_pw.o lib/util_getent.o lib/sock_exec.o \ lib/util_file.o lib/interface.o lib/interfaces.o lib/ms_fnmatch.o \ - lib/util_sid.o lib/signal.o lib/genrand.o lib/md4.o lib/crc32.o \ + lib/util_sid.o lib/signal.o lib/md4.o lib/crc32.o \ intl/lang_tdb.o lib/dummysmbd.o lib/clobber.o lib/smbrun.o \ lib/util_sec.o lib/getsmbpass.o lib/pidfile.o \ - lib/replace.o lib/bitmap.o lib/util_uuid.o lib/fsusage.o \ - lib/messages.o lib/tallocmsg.o lib/dmallocmsg.o lib/pam_errors.o \ + lib/replace.o lib/bitmap.o lib/util_uuid.o lib/fsusage.o \ + lib/messages.o lib/tallocmsg.o lib/dmallocmsg.o lib/pam_errors.o diff --git a/SAMBA_3_2_MERGE/source/include/debug.h b/SAMBA_3_2_MERGE/source/include/debug.h index c872aa8e777..05a9a3f0c52 100644 --- a/SAMBA_3_2_MERGE/source/include/debug.h +++ b/SAMBA_3_2_MERGE/source/include/debug.h @@ -1,8 +1,11 @@ /* Unix SMB/CIFS implementation. - Samba debug defines - Copyright (C) Andrew Tridgell 2003 - + SMB debug stuff + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) John H Terpstra 1996-1998 + Copyright (C) Luke Kenneth Casson Leighton 1996-1998 + Copyright (C) Paul Ashton 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 @@ -21,37 +24,179 @@ #ifndef _DEBUG_H #define _DEBUG_H -/* the debug operations structure - contains function pointers to - various debug implementations of each operation */ -struct debug_ops { - /* function to log (using DEBUG) suspicious usage of data structure */ - void (*log_suspicious_usage)(const char* from, const char* info); - - /* function to log (using printf) suspicious usage of data structure. - * To be used in circumstances when using DEBUG would cause loop. */ - void (*print_suspicious_usage)(const char* from, const char* info); - - /* function to return process/thread id */ - uint32_t (*get_task_id)(void); - - /* function to log process/thread id */ - void (*log_task_id)(int fd); -}; - -void do_debug(const char *, ...) PRINTF_ATTRIBUTE(1,2); +/* -------------------------------------------------------------------------- ** + * Debugging code. See also debug.c + */ + +/* mkproto.awk has trouble with ifdef'd function definitions (it ignores + * the #ifdef directive and will read both definitions, thus creating two + * diffferent prototype declarations), so we must do these by hand. + */ +/* I know the __attribute__ stuff is ugly, but it does ensure we get the + arguments to DEBUG() right. We have got them wrong too often in the + past. + The PRINTFLIKE comment does the equivalent for SGI MIPSPro. + */ +/* PRINTFLIKE1 */ +int Debug1( const char *, ... ) PRINTF_ATTRIBUTE(1,2); +/* PRINTFLIKE1 */ +BOOL dbgtext( const char *, ... ) PRINTF_ATTRIBUTE(1,2); +BOOL dbghdr( int level, const char *file, const char *func, int line ); + +extern XFILE *dbf; +extern pstring debugf; + +/* If we have these macros, we can add additional info to the header. */ +#ifdef HAVE_FUNCTION_MACRO +#define FUNCTION_MACRO (__FUNCTION__) +#else +#define FUNCTION_MACRO ("") +#endif + +/* + * Redefine DEBUGLEVEL because so we don't have to change every source file + * that *unnecessarily* references it. Source files neeed not extern reference + * DEBUGLEVEL, as it's extern in includes.h (which all source files include). + * Eventually, all these references should be removed, and all references to + * DEBUGLEVEL should be references to DEBUGLEVEL_CLASS[DBGC_ALL]. This could + * still be through a macro still called DEBUGLEVEL. This cannot be done now + * because some references would expand incorrectly. + */ +#define DEBUGLEVEL *debug_level extern int DEBUGLEVEL; -#define DEBUGLVL(level) ((level) <= DEBUGLEVEL) -#define DEBUG(level, body) do { if (DEBUGLVL(level)) do_debug body; } while (0) -#define DEBUGADD(level, body) DEBUG(level, body) -#define DEBUGC(class, level, body) DEBUG(level, body) -#define DEBUGADDC(class, level, body) DEBUG(level, body) -#define DEBUGTAB(n) do_debug_tab(n) +/* + * Define all new debug classes here. A class is represented by an entry in + * the DEBUGLEVEL_CLASS array. Index zero of this arrray is equivalent to the + * old DEBUGLEVEL. Any source file that does NOT add the following lines: + * + * #undef DBGC_CLASS + * #define DBGC_CLASS DBGC_ + * + * at the start of the file (after #include "includes.h") will default to + * using index zero, so it will behaive just like it always has. + */ +#define DBGC_ALL 0 /* index equivalent to DEBUGLEVEL */ + +#define DBGC_TDB 1 +#define DBGC_PRINTDRIVERS 2 +#define DBGC_LANMAN 3 +#define DBGC_SMB 4 +#define DBGC_RPC_PARSE 5 +#define DBGC_RPC_SRV 6 +#define DBGC_RPC_CLI 7 +#define DBGC_PASSDB 8 +#define DBGC_SAM 9 +#define DBGC_AUTH 10 +#define DBGC_WINBIND 11 +#define DBGC_VFS 12 +#define DBGC_IDMAP 13 +#define DBGC_QUOTA 14 +#define DBGC_ACLS 15 + +/* So you can define DBGC_CLASS before including debug.h */ +#ifndef DBGC_CLASS +#define DBGC_CLASS 0 /* override as shown above */ +#endif + +extern int *DEBUGLEVEL_CLASS; +extern BOOL *DEBUGLEVEL_CLASS_ISSET; + +/* Debugging macros + * + * DEBUGLVL() + * If the 'file specific' debug class level >= level OR the system-wide + * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then + * generate a header using the default macros for file, line, and + * function name. Returns True if the debug level was <= DEBUGLEVEL. + * + * Example: if( DEBUGLVL( 2 ) ) dbgtext( "Some text.\n" ); + * + * DEBUGLVLC() + * If the 'macro specified' debug class level >= level OR the system-wide + * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then + * generate a header using the default macros for file, line, and + * function name. Returns True if the debug level was <= DEBUGLEVEL. + * + * Example: if( DEBUGLVLC( DBGC_TDB, 2 ) ) dbgtext( "Some text.\n" ); + * + * DEBUG() + * If the 'file specific' debug class level >= level OR the system-wide + * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then + * generate a header using the default macros for file, line, and + * function name. Each call to DEBUG() generates a new header *unless* the + * previous debug output was unterminated (i.e. no '\n'). + * See debug.c:dbghdr() for more info. + * + * Example: DEBUG( 2, ("Some text and a value %d.\n", value) ); + * + * DEBUGC() + * If the 'macro specified' debug class level >= level OR the system-wide + * DEBUGLEVEL (synomym for DEBUGLEVEL_CLASS[ DBGC_ALL ]) >= level then + * generate a header using the default macros for file, line, and + * function name. Each call to DEBUG() generates a new header *unless* the + * previous debug output was unterminated (i.e. no '\n'). + * See debug.c:dbghdr() for more info. + * + * Example: DEBUGC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) ); + * + * DEBUGADD(), DEBUGADDC() + * Same as DEBUG() and DEBUGC() except the text is appended to the previous + * DEBUG(), DEBUGC(), DEBUGADD(), DEBUGADDC() with out another interviening + * header. + * + * Example: DEBUGADD( 2, ("Some text and a value %d.\n", value) ); + * DEBUGADDC( DBGC_TDB, 2, ("Some text and a value %d.\n", value) ); + * + * Note: If the debug class has not be redeined (see above) then the optimizer + * will remove the extra conditional test. + */ + +#define DEBUGLVL( level ) \ + ( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) ) + + +#define DEBUGLVLC( dbgc_class, level ) \ + ( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) ) ) + + +#define DEBUG( level, body ) \ + (void)( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \ + && (dbgtext body) ) + +#define DEBUGC( dbgc_class, level, body ) \ + (void)( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && (dbghdr( level, __FILE__, FUNCTION_MACRO, (__LINE__) )) \ + && (dbgtext body) ) -enum debug_logtype {DEBUG_FILE, DEBUG_STDOUT, DEBUG_STDERR}; +#define DEBUGADD( level, body ) \ + (void)( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ DBGC_CLASS ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ DBGC_CLASS ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && (dbgtext body) ) -/* keep some debug class defines for now to avoid changing old code too much */ -#define DBGC_AUTH 0 +#define DEBUGADDC( dbgc_class, level, body ) \ + (void)( ((level) <= MAX_DEBUG_LEVEL) && \ + ((DEBUGLEVEL_CLASS[ dbgc_class ] >= (level))|| \ + (!DEBUGLEVEL_CLASS_ISSET[ dbgc_class ] && \ + DEBUGLEVEL_CLASS[ DBGC_ALL ] >= (level)) ) \ + && (dbgtext body) ) -#endif /* _DEBUG_H */ +#endif diff --git a/SAMBA_3_2_MERGE/source/include/safe_string.h b/SAMBA_3_2_MERGE/source/include/safe_string.h index 431dc400aa1..745370f09e0 100644 --- a/SAMBA_3_2_MERGE/source/include/safe_string.h +++ b/SAMBA_3_2_MERGE/source/include/safe_string.h @@ -67,6 +67,10 @@ char * __unsafe_string_function_usage_here__(void); #else +/* String copy functions - macro hell below adds 'type checking' (limited, + but the best we can do in C) and may tag with function name/number to + record the last 'clobber region' on that string */ + #define pstrcpy(d,s) safe_strcpy((d), (s),sizeof(pstring)-1) #define pstrcat(d,s) safe_strcat((d), (s),sizeof(pstring)-1) #define fstrcpy(d,s) safe_strcpy((d),(s),sizeof(fstring)-1) @@ -82,6 +86,20 @@ char * __unsafe_string_function_usage_here__(void); #endif + #define safe_strcpy_base(dest, src, base, size) \ + safe_strcpy(dest, src, size-PTR_DIFF(dest,base)-1) + +#define nstrcpy(d,s) safe_strcpy((d), (s),sizeof(nstring)-1) +#define unstrcpy(d,s) safe_strcpy((d), (s),sizeof(unstring)-1) + +/* overmalloc_safe_strcpy: DEPRECATED! Used when you know the + * destination buffer is longer than maxlength, but you don't know how + * long. This is not a good situation, because we can't do the normal + * sanity checks. Don't use in new code! */ + +#define overmalloc_safe_strcpy(dest,src,maxlength) safe_strcpy(dest,src,maxlength) + + /* replace some string functions with multi-byte versions */ #define strlower(s) strlower_m(s) diff --git a/SAMBA_3_2_MERGE/source/lib/debug.c b/SAMBA_3_2_MERGE/source/lib/debug.c index c9247f380f4..dce1a2c956f 100644 --- a/SAMBA_3_2_MERGE/source/lib/debug.c +++ b/SAMBA_3_2_MERGE/source/lib/debug.c @@ -1,8 +1,9 @@ /* Unix SMB/CIFS implementation. - Samba debug functions - Copyright (C) Andrew Tridgell 2003 - Copyright (C) James J Myers 2003 + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Elrond 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 @@ -21,145 +22,975 @@ #include "includes.h" -/* this global variable determines what messages are printed */ -int DEBUGLEVEL; +/* -------------------------------------------------------------------------- ** + * Defines... + * + * FORMAT_BUFR_MAX - Index of the last byte of the format buffer; + * format_bufr[FORMAT_BUFR_MAX] should always be reserved + * for a terminating null byte. + */ +#define FORMAT_BUFR_MAX ( sizeof( format_bufr ) - 1 ) -/* the registered mutex handlers */ -static struct { - const char *name; - struct debug_ops ops; -} debug_handlers; +/* -------------------------------------------------------------------------- ** + * This module implements Samba's debugging utility. + * + * The syntax of a debugging log file is represented as: + * + * :== { } + * + * :== '\n' + * + * :== '[' TIME ',' LEVEL ']' [ [FILENAME ':'] [FUNCTION '()'] ] + * + * :== { } + * + * :== TEXT '\n' + * + * TEXT is a string of characters excluding the newline character. + * LEVEL is the DEBUG level of the message (an integer in the range 0..10). + * TIME is a timestamp. + * FILENAME is the name of the file from which the debug message was generated. + * FUNCTION is the function from which the debug message was generated. + * + * Basically, what that all means is: + * + * - A debugging log file is made up of debug messages. + * + * - Each debug message is made up of a header and text. The header is + * separated from the text by a newline. + * + * - The header begins with the timestamp and debug level of the message + * enclosed in brackets. The filename and function from which the + * message was generated may follow. The filename is terminated by a + * colon, and the function name is terminated by parenthesis. + * + * - The message text is made up of zero or more lines, each terminated by + * a newline. + */ -/* state variables for the debug system */ -static struct { - int fd; - enum debug_logtype logtype; - const char *prog_name; -} state; +/* -------------------------------------------------------------------------- ** + * External variables. + * + * dbf - Global debug file handle. + * debugf - Debug file name. + * DEBUGLEVEL - System-wide debug message limit. Messages with message- + * levels higher than DEBUGLEVEL will not be processed. + */ -/* - the backend for debug messages. Note that the DEBUG() macro has already - ensured that the log level has been met before this is called +XFILE *dbf = NULL; +pstring debugf = ""; +BOOL debug_warn_unknown_class = True; +BOOL debug_auto_add_unknown_class = True; +BOOL AllowDebugChange = True; + +/* + used to check if the user specified a + logfile on the command line */ -void do_debug(const char *format, ...) +BOOL override_logfile; + + +/* + * This is to allow assignment to DEBUGLEVEL before the debug + * system has been initialised. + */ +static int debug_all_class_hack = 1; +static BOOL debug_all_class_isset_hack = True; + +static int debug_num_classes = 0; +int *DEBUGLEVEL_CLASS = &debug_all_class_hack; +BOOL *DEBUGLEVEL_CLASS_ISSET = &debug_all_class_isset_hack; + +/* DEBUGLEVEL is #defined to *debug_level */ +int DEBUGLEVEL = &debug_all_class_hack; + + +/* -------------------------------------------------------------------------- ** + * Internal variables. + * + * stdout_logging - Default False, if set to True then dbf will be set to + * stdout and debug output will go to dbf only, and not + * to syslog. Set in setup_logging() and read in Debug1(). + * + * debug_count - Number of debug messages that have been output. + * Used to check log size. + * + * syslog_level - Internal copy of the message debug level. Written by + * dbghdr() and read by Debug1(). + * + * format_bufr - Used to format debug messages. The dbgtext() function + * prints debug messages to a string, and then passes the + * string to format_debug_text(), which uses format_bufr + * to build the formatted output. + * + * format_pos - Marks the first free byte of the format_bufr. + * + * + * log_overflow - When this variable is True, never attempt to check the + * size of the log. This is a hack, so that we can write + * a message using DEBUG, from open_logs() when we + * are unable to open a new log file for some reason. + */ + +static BOOL stdout_logging = False; +static int debug_count = 0; +#ifdef WITH_SYSLOG +static int syslog_level = 0; +#endif +static pstring format_bufr = { '\0' }; +static size_t format_pos = 0; +static BOOL log_overflow = False; + +/* + * Define all the debug class selection names here. Names *MUST NOT* contain + * white space. There must be one name for each DBGC_, and they + * must be in the table in the order of DBGC_.. + */ +static const char *default_classname_table[] = { + "all", /* DBGC_ALL; index refs traditional DEBUGLEVEL */ + "tdb", /* DBGC_TDB */ + "printdrivers", /* DBGC_PRINTDRIVERS */ + "lanman", /* DBGC_LANMAN */ + "smb", /* DBGC_SMB */ + "rpc_parse", /* DBGC_RPC_PARSE */ + "rpc_srv", /* DBGC_RPC_SRV */ + "rpc_cli", /* DBGC_RPC_CLI */ + "passdb", /* DBGC_PASSDB */ + "sam", /* DBGC_SAM */ + "auth", /* DBGC_AUTH */ + "winbind", /* DBGC_WINBIND */ + "vfs", /* DBGC_VFS */ + "idmap", /* DBGC_IDMAP */ + "quota", /* DBGC_QUOTA */ + "acls", /* DBGC_ACLS */ + NULL +}; + +static char **classname_table = NULL; + + +/* -------------------------------------------------------------------------- ** + * Functions... + */ + + +/**************************************************************************** +utility lists registered debug class names's +****************************************************************************/ + +#define MAX_CLASS_NAME_SIZE 1024 + +static char *debug_list_class_names_and_levels(void) { - va_list ap; - char *s = NULL; + int i, dim; + char **list; + char *buf = NULL; + char *b; + BOOL err = False; + + if (DEBUGLEVEL_CLASS == &debug_all_class_hack) + return NULL; - if (state.fd == 0) { - reopen_logs(); + list = calloc(debug_num_classes + 1, sizeof(char *)); + if (!list) + return NULL; + + /* prepare strings */ + for (i = 0, dim = 0; i < debug_num_classes; i++) { + int l = asprintf(&list[i], + "%s:%d ", + classname_table[i], + DEBUGLEVEL_CLASS_ISSET[i]?DEBUGLEVEL_CLASS[i]:DEBUGLEVEL); + if (l < 0 || l > MAX_CLASS_NAME_SIZE) { + err = True; + goto done; + } + dim += l; } - if (state.fd <= 0) return; + /* create single string list - add space for newline */ + b = buf = malloc(dim+1); + if (!buf) { + err = True; + goto done; + } + for (i = 0; i < debug_num_classes; i++) { + int l = strlen(list[i]); + strncpy(b, list[i], l); + b = b + l; + } + b[-1] = '\n'; /* replace last space with newline */ + b[0] = '\0'; /* null terminate string */ + +done: + /* free strings list */ + for (i = 0; i < debug_num_classes; i++) + if (list[i]) free(list[i]); + free(list); + + if (err) { + if (buf) + free(buf); + return NULL; + } else { + return buf; + } +} + +/**************************************************************************** +utility access to debug class names's +****************************************************************************/ +const char *debug_classname_from_index(int ndx) +{ + if (ndx < 0 || ndx >= debug_num_classes) + return NULL; + else + return classname_table[ndx]; +} + +/**************************************************************************** +utility to translate names to debug class index's (internal version) +****************************************************************************/ +static int debug_lookup_classname_int(const char* classname) +{ + int i; - va_start(ap, format); - vasprintf(&s, format, ap); - va_end(ap); + if (!classname) return -1; + + for (i=0; i < debug_num_classes; i++) { + if (strcmp(classname, classname_table[i])==0) + return i; + } + return -1; +} + +/**************************************************************************** +Add a new debug class to the system +****************************************************************************/ +int debug_add_class(const char *classname) +{ + int ndx; + void *new_ptr; + + if (!classname) + return -1; + + /* check the init has yet been called */ + debug_init(); + + ndx = debug_lookup_classname_int(classname); + if (ndx >= 0) + return ndx; + ndx = debug_num_classes; + + new_ptr = DEBUGLEVEL_CLASS; + if (DEBUGLEVEL_CLASS == &debug_all_class_hack) + { + /* Initial loading... */ + new_ptr = NULL; + } + new_ptr = Realloc(new_ptr, + sizeof(int) * (debug_num_classes + 1)); + if (!new_ptr) + return -1; + DEBUGLEVEL_CLASS = new_ptr; + DEBUGLEVEL_CLASS[ndx] = 0; - log_task_id(); + /* debug_level is the pointer used for the DEBUGLEVEL-thingy */ + if (ndx==0) + { + /* Transfer the initial level from debug_all_class_hack */ + DEBUGLEVEL_CLASS[ndx] = DEBUGLEVEL; + } + debug_level = DEBUGLEVEL_CLASS; + + new_ptr = DEBUGLEVEL_CLASS_ISSET; + if (new_ptr == &debug_all_class_isset_hack) + { + new_ptr = NULL; + } + new_ptr = Realloc(new_ptr, + sizeof(BOOL) * (debug_num_classes + 1)); + if (!new_ptr) + return -1; + DEBUGLEVEL_CLASS_ISSET = new_ptr; + DEBUGLEVEL_CLASS_ISSET[ndx] = False; + + new_ptr = Realloc(classname_table, + sizeof(char *) * (debug_num_classes + 1)); + if (!new_ptr) + return -1; + classname_table = new_ptr; + + classname_table[ndx] = strdup(classname); + if (! classname_table[ndx]) + return -1; - write(state.fd, s, strlen(s)); - free(s); + debug_num_classes++; + + return ndx; } -/* - reopen the log file (usually called because the log file name might have changed) -*/ -void reopen_logs(void) +/**************************************************************************** +utility to translate names to debug class index's (public version) +****************************************************************************/ +int debug_lookup_classname(const char *classname) { - char *logfile = lp_logfile(); - char *fname = NULL; - int old_fd = state.fd; + int ndx; + + if (!classname || !*classname) return -1; + + ndx = debug_lookup_classname_int(classname); - state.fd = 0; + if (ndx != -1) + return ndx; + + if (debug_warn_unknown_class) + { + DEBUG(0, ("debug_lookup_classname(%s): Unknown class\n", + classname)); + } + if (debug_auto_add_unknown_class) + { + return debug_add_class(classname); + } + return -1; +} + + +/**************************************************************************** +dump the current registered debug levels +****************************************************************************/ +static void debug_dump_status(int level) +{ + int q; + + DEBUG(level, ("INFO: Current debug levels:\n")); + for (q = 0; q < debug_num_classes; q++) + { + DEBUGADD(level, (" %s: %s/%d\n", + classname_table[q], + (DEBUGLEVEL_CLASS_ISSET[q] + ? "True" : "False"), + DEBUGLEVEL_CLASS[q])); + } +} + +/**************************************************************************** +parse the debug levels from smbcontrol. Example debug level parameter: + printdrivers:7 +****************************************************************************/ +static BOOL debug_parse_params(char **params) +{ + int i, ndx; + char *class_name; + char *class_level; - switch (state.logtype) { - case DEBUG_STDOUT: - state.fd = 1; - break; + if (!params) + return False; - case DEBUG_STDERR: - state.fd = 2; - break; + /* Allow DBGC_ALL to be specified w/o requiring its class name e.g."10" + * v.s. "all:10", this is the traditional way to set DEBUGLEVEL + */ + if (isdigit((int)params[0][0])) { + DEBUGLEVEL_CLASS[DBGC_ALL] = atoi(params[0]); + DEBUGLEVEL_CLASS_ISSET[DBGC_ALL] = True; + i = 1; /* start processing at the next params */ + } + else + i = 0; /* DBGC_ALL not specified OR class name was included */ - case DEBUG_FILE: - if ((*logfile) == '/') { - fname = strdup(logfile); + /* Fill in new debug class levels */ + for (; i < debug_num_classes && params[i]; i++) { + if ((class_name=strtok(params[i],":")) && + (class_level=strtok(NULL, "\0")) && + ((ndx = debug_lookup_classname(class_name)) != -1)) { + DEBUGLEVEL_CLASS[ndx] = atoi(class_level); + DEBUGLEVEL_CLASS_ISSET[ndx] = True; } else { - asprintf(&fname, "%s/%s.log", dyn_LOGFILEBASE, logfile); - } - if (fname) { - state.fd = open(fname, O_CREAT|O_APPEND|O_WRONLY, 0644); - free(fname); + DEBUG(0,("debug_parse_params: unrecognized debug class name or format [%s]\n", params[i])); + return False; } - break; } - if (old_fd > 2) { - close(old_fd); + return True; +} + +/**************************************************************************** +parse the debug levels from smb.conf. Example debug level string: + 3 tdb:5 printdrivers:7 +Note: the 1st param has no "name:" preceeding it. +****************************************************************************/ +BOOL debug_parse_levels(const char *params_str) +{ + char **params; + + /* Just in case */ + debug_init(); + + if (AllowDebugChange == False) + return True; + + params = str_list_make(params_str, NULL); + + if (debug_parse_params(params)) + { + debug_dump_status(5); + str_list_free(¶ms); + return True; + } else { + str_list_free(¶ms); + return False; } } -/* - control the name of the logfile and whether logging will be to stdout, stderr - or a file -*/ -void setup_logging(const char *prog_name, enum debug_logtype new_logtype) +/**************************************************************************** +receive a "set debug level" message +****************************************************************************/ +static void debug_message(int msg_type, pid_t src, void *buf, size_t len) { - state.logtype = new_logtype; - state.prog_name = prog_name; - reopen_logs(); + const char *params_str = buf; + + /* Check, it's a proper string! */ + if (params_str[len-1] != '\0') + { + DEBUG(1, ("Invalid debug message from pid %u to pid %u\n", + (unsigned int)src, (unsigned int)getpid())); + return; + } + + DEBUG(3, ("INFO: Remote set of debug to `%s' (pid %u from pid %u)\n", + params_str, (unsigned int)getpid(), (unsigned int)src)); + + debug_parse_levels(params_str); } -/* - return a string constant containing n tabs - no more than 10 tabs are returned -*/ -const char *do_debug_tab(uint_t n) + +/**************************************************************************** +send a "set debug level" message +****************************************************************************/ +void debug_message_send(pid_t pid, const char *params_str) { - const char *tabs[] = {"", "\t", "\t\t", "\t\t\t", "\t\t\t\t", "\t\t\t\t\t", - "\t\t\t\t\t\t", "\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t", - "\t\t\t\t\t\t\t\t\t", "\t\t\t\t\t\t\t\t\t\t"}; - return tabs[MIN(n, 10)]; + if (!params_str) + return; + message_send_pid(pid, MSG_DEBUG, params_str, strlen(params_str) + 1, + False); } +/**************************************************************************** + Return current debug level. +****************************************************************************/ -/* - log/print suspicious usage - print comments and backtrace -*/ -void log_suspicious_usage(const char *from, const char *info) +static void debuglevel_message(int msg_type, pid_t src, void *buf, size_t len) { - if (debug_handlers.ops.log_suspicious_usage) { - debug_handlers.ops.log_suspicious_usage(from, info); - } + char *message = debug_list_class_names_and_levels(); + + DEBUG(1,("INFO: Received REQ_DEBUGLEVEL message from PID %u\n",(unsigned int)src)); + message_send_pid(src, MSG_DEBUGLEVEL, message, strlen(message) + 1, True); + + SAFE_FREE(message); } -void print_suspicious_usage(const char* from, const char* info) + +/**************************************************************************** +Init debugging (one time stuff) +****************************************************************************/ +void debug_init(void) { - if (debug_handlers.ops.print_suspicious_usage) { - debug_handlers.ops.print_suspicious_usage(from, info); + static BOOL initialised = False; + const char **p; + + if (initialised) + return; + + initialised = True; + + message_register(MSG_DEBUG, debug_message); + message_register(MSG_REQ_DEBUGLEVEL, debuglevel_message); + + for(p = default_classname_table; *p; p++) + { + debug_add_class(*p); } } -uint32_t get_task_id(void) + +/* ************************************************************************** ** + * get ready for syslog stuff + * ************************************************************************** ** + */ +void setup_logging(const char *pname, BOOL interactive) +{ + debug_init(); + + /* reset to allow multiple setup calls, going from interactive to + non-interactive */ + stdout_logging = False; + dbf = NULL; + + if (interactive) { + stdout_logging = True; + dbf = x_stdout; + x_setbuf( x_stdout, NULL ); + } +#ifdef WITH_SYSLOG + else { + const char *p = strrchr_m( pname,'/' ); + if (p) + pname = p + 1; +#ifdef LOG_DAEMON + openlog( pname, LOG_PID, SYSLOG_FACILITY ); +#else + /* for old systems that have no facility codes. */ + openlog( pname, LOG_PID ); +#endif + } +#endif +} /* setup_logging */ + +/* ************************************************************************** ** + * reopen the log files + * note that we now do this unconditionally + * We attempt to open the new debug fp before closing the old. This means + * if we run out of fd's we just keep using the old fd rather than aborting. + * Fix from dgibson@linuxcare.com. + * ************************************************************************** ** + */ + +BOOL reopen_logs( void ) { - if (debug_handlers.ops.get_task_id) { - return debug_handlers.ops.get_task_id(); + pstring fname; + mode_t oldumask; + XFILE *new_dbf = NULL; + XFILE *old_dbf = NULL; + BOOL ret = True; + + if (stdout_logging) + return True; + + oldumask = umask( 022 ); + + pstrcpy(fname, debugf ); + + if (lp_loaded()) { + char *logfname; + + logfname = lp_logfile(); + if (*logfname) + pstrcpy(fname, logfname); } - return getpid(); + + pstrcpy( debugf, fname ); + new_dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644); + + if (!new_dbf) { + log_overflow = True; + DEBUG(0, ("Unable to open new log file %s: %s\n", debugf, strerror(errno))); + log_overflow = False; + if (dbf) + x_fflush(dbf); + ret = False; + } else { + x_setbuf(new_dbf, NULL); + old_dbf = dbf; + dbf = new_dbf; + if (old_dbf) + (void) x_fclose(old_dbf); + } + + /* Fix from klausr@ITAP.Physik.Uni-Stuttgart.De + * to fix problem where smbd's that generate less + * than 100 messages keep growing the log. + */ + force_check_log_size(); + (void)umask(oldumask); + + /* Take over stderr to catch ouput into logs */ + if (dbf && sys_dup2(x_fileno(dbf), 2) == -1) { + close_low_fds(True); /* Close stderr too, if dup2 can't point it + at the logfile */ + } + + return ret; } -void log_task_id(void) +/* ************************************************************************** ** + * Force a check of the log size. + * ************************************************************************** ** + */ +void force_check_log_size( void ) { - if (debug_handlers.ops.log_task_id) { - debug_handlers.ops.log_task_id(state.fd); + debug_count = 100; +} + +/*************************************************************************** + Check to see if there is any need to check if the logfile has grown too big. +**************************************************************************/ + +BOOL need_to_check_log_size( void ) +{ + int maxlog; + + if( debug_count < 100 ) + return( False ); + + maxlog = lp_max_log_size() * 1024; + if( !dbf || maxlog <= 0 ) { + debug_count = 0; + return(False); } + return( True ); } -/* - register a set of debug handlers. -*/ -void register_debug_handlers(const char *name, struct debug_ops *ops) + +/* ************************************************************************** ** + * Check to see if the log has grown to be too big. + * ************************************************************************** ** + */ + +void check_log_size( void ) { - debug_handlers.name = name; - debug_handlers.ops = *ops; + int maxlog; + SMB_STRUCT_STAT st; + + /* + * We need to be root to check/change log-file, skip this and let the main + * loop check do a new check as root. + */ + + if( geteuid() != 0 ) + return; + + if(log_overflow || !need_to_check_log_size() ) + return; + + maxlog = lp_max_log_size() * 1024; + + if( sys_fstat( x_fileno( dbf ), &st ) == 0 && st.st_size > maxlog ) { + (void)reopen_logs(); + if( dbf && get_file_size( debugf ) > maxlog ) { + pstring name; + + slprintf( name, sizeof(name)-1, "%s.old", debugf ); + (void)rename( debugf, name ); + + if (!reopen_logs()) { + /* We failed to reopen a log - continue using the old name. */ + (void)rename(name, debugf); + } + } + } + + /* + * Here's where we need to panic if dbf == NULL.. + */ + + if(dbf == NULL) { + /* This code should only be reached in very strange + * circumstances. If we merely fail to open the new log we + * should stick with the old one. ergo this should only be + * reached when opening the logs for the first time: at + * startup or when the log level is increased from zero. + * -dwg 6 June 2000 + */ + dbf = x_fopen( "/dev/console", O_WRONLY, 0); + if(dbf) { + DEBUG(0,("check_log_size: open of debug file %s failed - using console.\n", + debugf )); + } else { + /* + * We cannot continue without a debug file handle. + */ + abort(); + } + } + debug_count = 0; +} /* check_log_size */ + +/* ************************************************************************** ** + * Write an debug message on the debugfile. + * This is called by dbghdr() and format_debug_text(). + * ************************************************************************** ** + */ + int Debug1( const char *format_str, ... ) +{ + va_list ap; + int old_errno = errno; + + debug_count++; + + if( stdout_logging ) + { + va_start( ap, format_str ); + if(dbf) + (void)x_vfprintf( dbf, format_str, ap ); + va_end( ap ); + errno = old_errno; + return( 0 ); + } + +#ifdef WITH_SYSLOG + if( !lp_syslog_only() ) +#endif + { + if( !dbf ) + { + mode_t oldumask = umask( 022 ); + + dbf = x_fopen( debugf, O_WRONLY|O_APPEND|O_CREAT, 0644 ); + (void)umask( oldumask ); + if( dbf ) + { + x_setbuf( dbf, NULL ); + } + else + { + errno = old_errno; + return(0); + } + } + } + +#ifdef WITH_SYSLOG + if( syslog_level < lp_syslog() ) + { + /* map debug levels to syslog() priorities + * note that not all DEBUG(0, ...) calls are + * necessarily errors + */ + static int priority_map[] = { + LOG_ERR, /* 0 */ + LOG_WARNING, /* 1 */ + LOG_NOTICE, /* 2 */ + LOG_INFO, /* 3 */ + }; + int priority; + pstring msgbuf; + + if( syslog_level >= ( sizeof(priority_map) / sizeof(priority_map[0]) ) + || syslog_level < 0) + priority = LOG_DEBUG; + else + priority = priority_map[syslog_level]; + + va_start( ap, format_str ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + msgbuf[255] = '\0'; + syslog( priority, "%s", msgbuf ); + } +#endif + + check_log_size(); + +#ifdef WITH_SYSLOG + if( !lp_syslog_only() ) +#endif + { + va_start( ap, format_str ); + if(dbf) + (void)x_vfprintf( dbf, format_str, ap ); + va_end( ap ); + if(dbf) + (void)x_fflush( dbf ); + } + + errno = old_errno; + + return( 0 ); + } /* Debug1 */ + + +/* ************************************************************************** ** + * Print the buffer content via Debug1(), then reset the buffer. + * + * Input: none + * Output: none + * + * ************************************************************************** ** + */ +static void bufr_print( void ) + { + format_bufr[format_pos] = '\0'; + (void)Debug1( "%s", format_bufr ); + format_pos = 0; + } /* bufr_print */ + +/* ************************************************************************** ** + * Format the debug message text. + * + * Input: msg - Text to be added to the "current" debug message text. + * + * Output: none. + * + * Notes: The purpose of this is two-fold. First, each call to syslog() + * (used by Debug1(), see above) generates a new line of syslog + * output. This is fixed by storing the partial lines until the + * newline character is encountered. Second, printing the debug + * message lines when a newline is encountered allows us to add + * spaces, thus indenting the body of the message and making it + * more readable. + * + * ************************************************************************** ** + */ +static void format_debug_text( char *msg ) + { + size_t i; + BOOL timestamp = (!stdout_logging && (lp_timestamp_logs() || + !(lp_loaded()))); + + for( i = 0; msg[i]; i++ ) + { + /* Indent two spaces at each new line. */ + if(timestamp && 0 == format_pos) + { + format_bufr[0] = format_bufr[1] = ' '; + format_pos = 2; + } + + /* If there's room, copy the character to the format buffer. */ + if( format_pos < FORMAT_BUFR_MAX ) + format_bufr[format_pos++] = msg[i]; + + /* If a newline is encountered, print & restart. */ + if( '\n' == msg[i] ) + bufr_print(); + + /* If the buffer is full dump it out, reset it, and put out a line + * continuation indicator. + */ + if( format_pos >= FORMAT_BUFR_MAX ) + { + bufr_print(); + (void)Debug1( " +>\n" ); + } + } + + /* Just to be safe... */ + format_bufr[format_pos] = '\0'; + } /* format_debug_text */ + +/* ************************************************************************** ** + * Flush debug output, including the format buffer content. + * + * Input: none + * Output: none + * + * ************************************************************************** ** + */ +void dbgflush( void ) + { + bufr_print(); + if(dbf) + (void)x_fflush( dbf ); + } /* dbgflush */ + +/* ************************************************************************** ** + * Print a Debug Header. + * + * Input: level - Debug level of the message (not the system-wide debug + * level. ) + * file - Pointer to a string containing the name of the file + * from which this function was called, or an empty string + * if the __FILE__ macro is not implemented. + * func - Pointer to a string containing the name of the function + * from which this function was called, or an empty string + * if the __FUNCTION__ macro is not implemented. + * line - line number of the call to dbghdr, assuming __LINE__ + * works. + * + * Output: Always True. This makes it easy to fudge a call to dbghdr() + * in a macro, since the function can be called as part of a test. + * Eg: ( (level <= DEBUGLEVEL) && (dbghdr(level,"",line)) ) + * + * Notes: This function takes care of setting syslog_level. + * + * ************************************************************************** ** + */ + +BOOL dbghdr( int level, const char *file, const char *func, int line ) +{ + /* Ensure we don't lose any real errno value. */ + int old_errno = errno; + + if( format_pos ) { + /* This is a fudge. If there is stuff sitting in the format_bufr, then + * the *right* thing to do is to call + * format_debug_text( "\n" ); + * to write the remainder, and then proceed with the new header. + * Unfortunately, there are several places in the code at which + * the DEBUG() macro is used to build partial lines. That in mind, + * we'll work under the assumption that an incomplete line indicates + * that a new header is *not* desired. + */ + return( True ); + } + +#ifdef WITH_SYSLOG + /* Set syslog_level. */ + syslog_level = level; +#endif + + /* Don't print a header if we're logging to stdout. */ + if( stdout_logging ) + return( True ); + + /* Print the header if timestamps are turned on. If parameters are + * not yet loaded, then default to timestamps on. + */ + if( lp_timestamp_logs() || !(lp_loaded()) ) { + char header_str[200]; + + header_str[0] = '\0'; + + if( lp_debug_pid()) + slprintf(header_str,sizeof(header_str)-1,", pid=%u",(unsigned int)sys_getpid()); + + if( lp_debug_uid()) { + size_t hs_len = strlen(header_str); + slprintf(header_str + hs_len, + sizeof(header_str) - 1 - hs_len, + ", effective(%u, %u), real(%u, %u)", + (unsigned int)geteuid(), (unsigned int)getegid(), + (unsigned int)getuid(), (unsigned int)getgid()); + } + + /* Print it all out at once to prevent split syslog output. */ + (void)Debug1( "[%s, %d%s] %s:%s(%d)\n", + timestring_x(lp_debug_hires_timestamp()), level, + header_str, file, func, line ); + } + + errno = old_errno; + return( True ); } + +/* ************************************************************************** ** + * Add text to the body of the "current" debug message via the format buffer. + * + * Input: format_str - Format string, as used in printf(), et. al. + * ... - Variable argument list. + * + * ..or.. va_alist - Old style variable parameter list starting point. + * + * Output: Always True. See dbghdr() for more info, though this is not + * likely to be used in the same way. + * + * ************************************************************************** ** + */ + BOOL dbgtext( const char *format_str, ... ) + { + va_list ap; + pstring msgbuf; + + va_start( ap, format_str ); + vslprintf( msgbuf, sizeof(msgbuf)-1, format_str, ap ); + va_end( ap ); + + format_debug_text( msgbuf ); + + return( True ); + } /* dbgtext */ + + +/* ************************************************************************** */ diff --git a/SAMBA_3_2_MERGE/source/lib/util.c b/SAMBA_3_2_MERGE/source/lib/util.c index 0a8f7c9762c..95edf5482b7 100644 --- a/SAMBA_3_2_MERGE/source/lib/util.c +++ b/SAMBA_3_2_MERGE/source/lib/util.c @@ -1076,6 +1076,16 @@ void zero_ip(struct in_addr *ip) return; } +/******************************************************************* + Set an IP to 255.255.255.255 +******************************************************************/ + +void one_ip(struct in_addr *ip) +{ + *ip = inet_makeaddr(0xffffffff,0xffffffff); + return; +} + #if (defined(HAVE_NETGROUP) && defined(WITH_AUTOMOUNT)) /****************************************************************** Remove any mount options such as -rsize=2048,wsize=2048 etc. diff --git a/SAMBA_3_2_MERGE/source/lib/util_str.c b/SAMBA_3_2_MERGE/source/lib/util_str.c index 60eddf903f5..2e10b780c02 100644 --- a/SAMBA_3_2_MERGE/source/lib/util_str.c +++ b/SAMBA_3_2_MERGE/source/lib/util_str.c @@ -603,9 +603,11 @@ char *safe_strcpy(char *dest,const char *src, size_t maxlength) * or fstring) then this should cause an error under a memory * checker. */ dest[maxlength] = '\0'; +#if 0 /* JERRY -- temporary */ if (PTR_DIFF(&len, dest) > 0) { /* check if destination is on the stack, ok if so */ log_suspicious_usage("safe_strcpy", src); } +#endif #endif if (!src) { @@ -644,9 +646,11 @@ char *safe_strcat(char *dest, const char *src, size_t maxlength) return dest; #ifdef DEVELOPER +#if 0 /* JERRY -- temporary */ if (PTR_DIFF(&src_len, dest) > 0) { /* check if destination is on the stack, ok if so */ log_suspicious_usage("safe_strcat", src); } +#endif #endif src_len = strlen(src); dest_len = strlen(dest); diff --git a/SAMBA_3_2_MERGE/source/nmbd/nmbd_lmhosts.c b/SAMBA_3_2_MERGE/source/nmbd/nmbd_lmhosts.c new file mode 100644 index 00000000000..215c3d0d90e --- /dev/null +++ b/SAMBA_3_2_MERGE/source/nmbd/nmbd_lmhosts.c @@ -0,0 +1,102 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios routines and daemon - version 2 + Copyright (C) Jeremy Allison 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: + + Handle lmhosts file reading. + +*/ + +#include "includes.h" + +/**************************************************************************** +Load a lmhosts file. +****************************************************************************/ + +void load_lmhosts_file(char *fname) +{ + pstring name; + int name_type; + struct in_addr ipaddr; + XFILE *fp = startlmhosts( fname ); + TALLOC_CTX *mem_ctx; + + + if (!fp) { + DEBUG(2,("load_lmhosts_file: Can't open lmhosts file %s. Error was %s\n", + fname, strerror(errno))); + return; + } + + if ( !(mem_ctx = talloc_init( "load_lmhosts_file" )) ) { + DEBUG(0,("load_lmhosts_file: talloc_init() failed!\n")); + return; + } + + while (getlmhostsent(mem_ctx, fp, name, &name_type, &ipaddr) ) { + struct subnet_record *subrec = NULL; + enum name_source source = LMHOSTS_NAME; + + /* We find a relevent subnet to put this entry on, then add it. */ + /* Go through all the broadcast subnets and see if the mask matches. */ + for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + if(same_net(ipaddr, subrec->bcast_ip, subrec->mask_ip)) + break; + } + + /* If none match add the name to the remote_broadcast_subnet. */ + if(subrec == NULL) + subrec = remote_broadcast_subnet; + + if(name_type == -1) { + /* Add the (0) and (0x20) names directly into the namelist for this subnet. */ + (void)add_name_to_subnet(subrec,name,0x00,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + (void)add_name_to_subnet(subrec,name,0x20,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } else { + /* Add the given name type to the subnet namelist. */ + (void)add_name_to_subnet(subrec,name,name_type,(uint16)NB_ACTIVE,PERMANENT_TTL,source,1,&ipaddr); + } + } + + endlmhosts(fp); + + talloc_destroy( mem_ctx ); +} + +/**************************************************************************** + Find a name read from the lmhosts file. We secretly check the names on + the remote_broadcast_subnet as if the name was added to a regular broadcast + subnet it will be found by normal name query processing. +****************************************************************************/ + +BOOL find_name_in_lmhosts(struct nmb_name *nmbname, struct name_record **namerecp) +{ + struct name_record *namerec; + + *namerecp = NULL; + + if((namerec = find_name_on_subnet(remote_broadcast_subnet, nmbname, FIND_ANY_NAME))==NULL) + return False; + + if(!NAME_IS_ACTIVE(namerec) || (namerec->data.source != LMHOSTS_NAME)) + return False; + + *namerecp = namerec; + return True; +} diff --git a/SAMBA_3_2_MERGE/source/nmbd/nmbd_sendannounce.c b/SAMBA_3_2_MERGE/source/nmbd/nmbd_sendannounce.c new file mode 100644 index 00000000000..7992383c7a3 --- /dev/null +++ b/SAMBA_3_2_MERGE/source/nmbd/nmbd_sendannounce.c @@ -0,0 +1,599 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Luke Kenneth Casson Leighton 1994-1998 + Copyright (C) Jeremy Allison 1994-1998 + + SMB Version handling + Copyright (C) John H Terpstra 1995-1998 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "includes.h" + +extern int updatecount; +extern BOOL found_lm_clients; + +/**************************************************************************** + Send a browser reset packet. +**************************************************************************/ + +void send_browser_reset(int reset_type, const char *to_name, int to_type, struct in_addr to_ip) +{ + pstring outbuf; + char *p; + + DEBUG(3,("send_browser_reset: sending reset request type %d to %s<%02x> IP %s.\n", + reset_type, to_name, to_type, inet_ntoa(to_ip) )); + + memset(outbuf,'\0',sizeof(outbuf)); + p = outbuf; + SCVAL(p,0,ANN_ResetBrowserState); + p++; + SCVAL(p,0,reset_type); + p++; + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + global_myname(), 0x0, to_name, to_type, to_ip, + FIRST_SUBNET->myip, DGRAM_PORT); +} + +/**************************************************************************** + Broadcast a packet to the local net requesting that all servers in this + workgroup announce themselves to us. + **************************************************************************/ + +void broadcast_announce_request(struct subnet_record *subrec, struct work_record *work) +{ + pstring outbuf; + char *p; + + work->needannounce = True; + + DEBUG(3,("broadcast_announce_request: sending announce request for workgroup %s \ +to subnet %s\n", work->work_group, subrec->subnet_name)); + + memset(outbuf,'\0',sizeof(outbuf)); + p = outbuf; + SCVAL(p,0,ANN_AnnouncementRequest); + p++; + + SCVAL(p,0,work->token); /* (local) Unique workgroup token id. */ + p++; + p += push_string(NULL, p+1, global_myname(), 15, STR_ASCII|STR_UPPER|STR_TERMINATE); + + send_mailslot(False, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + global_myname(), 0x0, work->work_group,0x1e, subrec->bcast_ip, + subrec->myip, DGRAM_PORT); +} + +/**************************************************************************** + Broadcast an announcement. + **************************************************************************/ + +static void send_announcement(struct subnet_record *subrec, int announce_type, + const char *from_name, const char *to_name, int to_type, struct in_addr to_ip, + time_t announce_interval, + const char *server_name, int server_type, const char *server_comment) +{ + pstring outbuf; + unstring upper_server_name; + char *p; + + memset(outbuf,'\0',sizeof(outbuf)); + p = outbuf+1; + + SCVAL(outbuf,0,announce_type); + + /* Announcement parameters. */ + SCVAL(p,0,updatecount); + SIVAL(p,1,announce_interval*1000); /* Milliseconds - despite the spec. */ + + safe_strcpy(upper_server_name, server_name, sizeof(upper_server_name)-1); + strupper_m(upper_server_name); + push_string(NULL, p+5, upper_server_name, 16, STR_ASCII|STR_TERMINATE); + + SCVAL(p,21,lp_major_announce_version()); /* Major version. */ + SCVAL(p,22,lp_minor_announce_version()); /* Minor version. */ + + SIVAL(p,23,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); + /* Browse version: got from NT/AS 4.00 - Value defined in smb.h (JHT). */ + SSVAL(p,27,BROWSER_ELECTION_VERSION); + SSVAL(p,29,BROWSER_CONSTANT); /* Browse signature. */ + + p += 31 + push_string(NULL, p+31, server_comment, -1, STR_ASCII|STR_TERMINATE); + + send_mailslot(False,BROWSE_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + from_name, 0x0, to_name, to_type, to_ip, subrec->myip, + DGRAM_PORT); +} + +/**************************************************************************** + Broadcast a LanMan announcement. +**************************************************************************/ + +static void send_lm_announcement(struct subnet_record *subrec, int announce_type, + char *from_name, char *to_name, int to_type, struct in_addr to_ip, + time_t announce_interval, + char *server_name, int server_type, char *server_comment) +{ + pstring outbuf; + char *p=outbuf; + + memset(outbuf,'\0',sizeof(outbuf)); + + SSVAL(p,0,announce_type); + SIVAL(p,2,server_type & ~SV_TYPE_LOCAL_LIST_ONLY); + SCVAL(p,6,lp_major_announce_version()); /* Major version. */ + SCVAL(p,7,lp_minor_announce_version()); /* Minor version. */ + SSVAL(p,8,announce_interval); /* In seconds - according to spec. */ + + p += 10; + p += push_string(NULL, p, server_name, 15, STR_ASCII|STR_UPPER|STR_TERMINATE); + p += push_string(NULL, p, server_comment, sizeof(pstring)-15, STR_ASCII|STR_UPPER|STR_TERMINATE); + + send_mailslot(False,LANMAN_MAILSLOT, outbuf, PTR_DIFF(p,outbuf), + from_name, 0x0, to_name, to_type, to_ip, subrec->myip, + DGRAM_PORT); +} + +/**************************************************************************** + We are a local master browser. Announce this to WORKGROUP<1e>. +****************************************************************************/ + +static void send_local_master_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bit set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_local_master_announcement: type %x for name %s on subnet %s for workgroup %s\n", + type, global_myname(), subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_LocalMasterAnnouncement, + global_myname(), /* From nbt name. */ + work->work_group, 0x1e, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + global_myname(), /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce the workgroup WORKGROUP to MSBROWSE<01>. +****************************************************************************/ + +static void send_workgroup_announcement(struct subnet_record *subrec, struct work_record *work) +{ + DEBUG(3,("send_workgroup_announcement: on subnet %s for workgroup %s\n", + subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_DomainAnnouncement, + global_myname(), /* From nbt name. */ + MSBROWSE, 0x1, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + work->work_group, /* Name to announce. */ + SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT, /* workgroup announce flags. */ + global_myname()); /* From name as comment. */ +} + +/**************************************************************************** + Announce the given host to WORKGROUP<1d>. +****************************************************************************/ + +static void send_host_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Ensure we don't have the prohibited bits set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_host_announcement: type %x for host %s on subnet %s for workgroup %s\n", + type, servrec->serv.name, subrec->subnet_name, work->work_group)); + + send_announcement(subrec, ANN_HostAnnouncement, + servrec->serv.name, /* From nbt name. */ + work->work_group, 0x1d, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + work->announce_interval, /* Time until next announce. */ + servrec->serv.name, /* Name to announce. */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce the given LanMan host +****************************************************************************/ + +static void send_lm_host_announcement(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec, int lm_interval) +{ + /* Ensure we don't have the prohibited bits set. */ + uint32 type = servrec->serv.type & ~SV_TYPE_LOCAL_LIST_ONLY; + + DEBUG(3,("send_lm_host_announcement: type %x for host %s on subnet %s for workgroup %s, ttl: %d\n", + type, servrec->serv.name, subrec->subnet_name, work->work_group, lm_interval)); + + send_lm_announcement(subrec, ANN_HostAnnouncement, + servrec->serv.name, /* From nbt name. */ + work->work_group, 0x00, /* To nbt name. */ + subrec->bcast_ip, /* To ip. */ + lm_interval, /* Time until next announce. */ + servrec->serv.name, /* Name to announce (fstring not netbios name struct). */ + type, /* Type field. */ + servrec->serv.comment); +} + +/**************************************************************************** + Announce a server record. + ****************************************************************************/ + +static void announce_server(struct subnet_record *subrec, struct work_record *work, + struct server_record *servrec) +{ + /* Only do domain announcements if we are a master and it's + our primary name we're being asked to announce. */ + + if (AM_LOCAL_MASTER_BROWSER(work) && strequal(global_myname(),servrec->serv.name)) { + send_local_master_announcement(subrec, work, servrec); + send_workgroup_announcement(subrec, work); + } else { + send_host_announcement(subrec, work, servrec); + } +} + +/**************************************************************************** + Go through all my registered names on all broadcast subnets and announce + them if the timeout requires it. + **************************************************************************/ + +void announce_my_server_names(time_t t) +{ + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup()); + + if(work) { + struct server_record *servrec; + + if (work->needannounce) { + /* Drop back to a max 3 minute announce. This is to prevent a + single lost packet from breaking things for too long. */ + + work->announce_interval = MIN(work->announce_interval, + CHECK_TIME_MIN_HOST_ANNCE*60); + work->lastannounce_time = t - (work->announce_interval+1); + work->needannounce = False; + } + + /* Announce every minute at first then progress to every 12 mins */ + if ((t - work->lastannounce_time) < work->announce_interval) + continue; + + if (work->announce_interval < (CHECK_TIME_MAX_HOST_ANNCE * 60)) + work->announce_interval += 60; + + work->lastannounce_time = t; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) { + if (is_myname(servrec->serv.name)) + announce_server(subrec, work, servrec); + } + } /* if work */ + } /* for subrec */ +} + +/**************************************************************************** + Go through all my registered names on all broadcast subnets and announce + them as a LanMan server if the timeout requires it. +**************************************************************************/ + +void announce_my_lm_server_names(time_t t) +{ + struct subnet_record *subrec; + static time_t last_lm_announce_time=0; + int announce_interval = lp_lm_interval(); + int lm_announce = lp_lm_announce(); + + if ((announce_interval <= 0) || (lm_announce <= 0)) { + /* user absolutely does not want LM announcements to be sent. */ + return; + } + + if ((lm_announce >= 2) && (!found_lm_clients)) { + /* has been set to 2 (Auto) but no LM clients detected (yet). */ + return; + } + + /* Otherwise: must have been set to 1 (Yes), or LM clients *have* + been detected. */ + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + struct work_record *work = find_workgroup_on_subnet(subrec, lp_workgroup()); + + if(work) { + struct server_record *servrec; + + if (last_lm_announce_time && ((t - last_lm_announce_time) < announce_interval )) + continue; + + last_lm_announce_time = t; + + for (servrec = work->serverlist; servrec; servrec = servrec->next) { + if (is_myname(servrec->serv.name)) + /* skipping equivalent of announce_server() */ + send_lm_host_announcement(subrec, work, servrec, announce_interval); + } + } /* if work */ + } /* for subrec */ +} + +/* Announce timer. Moved into global static so it can be reset + when a machine becomes a local master browser. */ +static time_t announce_timer_last=0; + +/**************************************************************************** + Reset the announce_timer so that a local master browser announce will be done + immediately. + ****************************************************************************/ + +void reset_announce_timer(void) +{ + announce_timer_last = time(NULL) - (CHECK_TIME_MST_ANNOUNCE * 60); +} + +/**************************************************************************** + Announce myself as a local master browser to a domain master browser. + **************************************************************************/ + +void announce_myself_to_domain_master_browser(time_t t) +{ + struct subnet_record *subrec; + struct work_record *work; + + if(!we_are_a_wins_client()) { + DEBUG(10,("announce_myself_to_domain_master_browser: no unicast subnet, ignoring.\n")); + return; + } + + if (!announce_timer_last) + announce_timer_last = t; + + if ((t-announce_timer_last) < (CHECK_TIME_MST_ANNOUNCE * 60)) { + DEBUG(10,("announce_myself_to_domain_master_browser: t (%d) - last(%d) < %d\n", + (int)t, (int)announce_timer_last, + CHECK_TIME_MST_ANNOUNCE * 60 )); + return; + } + + announce_timer_last = t; + + /* Look over all our broadcast subnets to see if any of them + has the state set as local master browser. */ + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + for (work = subrec->workgrouplist; work; work = work->next) { + if (AM_LOCAL_MASTER_BROWSER(work)) { + DEBUG(4,( "announce_myself_to_domain_master_browser: I am a local master browser for \ +workgroup %s on subnet %s\n", work->work_group, subrec->subnet_name)); + + /* Look in nmbd_browsersync.c for the rest of this code. */ + announce_and_sync_with_domain_master_browser(subrec, work); + } + } + } +} + +/**************************************************************************** +Announce all samba's server entries as 'gone'. +This must *only* be called on shutdown. +****************************************************************************/ + +void announce_my_servers_removed(void) +{ + int announce_interval = lp_lm_interval(); + int lm_announce = lp_lm_announce(); + struct subnet_record *subrec; + + for (subrec = FIRST_SUBNET; subrec; subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) { + struct work_record *work; + for (work = subrec->workgrouplist; work; work = work->next) { + struct server_record *servrec; + + work->announce_interval = 0; + for (servrec = work->serverlist; servrec; servrec = servrec->next) { + if (!is_myname(servrec->serv.name)) + continue; + servrec->serv.type = 0; + if(AM_LOCAL_MASTER_BROWSER(work)) + send_local_master_announcement(subrec, work, servrec); + send_host_announcement(subrec, work, servrec); + + if ((announce_interval <= 0) || (lm_announce <= 0)) { + /* user absolutely does not want LM announcements to be sent. */ + continue; + } + + if ((lm_announce >= 2) && (!found_lm_clients)) { + /* has been set to 2 (Auto) but no LM clients detected (yet). */ + continue; + } + + /* + * lm announce was set or we have seen lm announcements, so do + * a lm announcement of host removed. + */ + + send_lm_host_announcement(subrec, work, servrec, 0); + } + } + } +} + +/**************************************************************************** + Do all the "remote" announcements. These are used to put ourselves + on a remote browse list. They are done blind, no checking is done to + see if there is actually a local master browser at the other end. + **************************************************************************/ + +void announce_remote(time_t t) +{ + char *s; + const char *ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + char *comment; + int stype = lp_default_server_announce(); + TALLOC_CTX *mem_ctx; + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_announce(); + if (!*s) + return; + + comment = string_truncate(lp_serverstring(), MAX_SERVER_STRING_LENGTH); + + if ( !(mem_ctx = talloc_init( "announce_remote" )) ) { + DEBUG(0,("announce_remote: talloc_init() failed!\n")); + return; + } + + + for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) { + /* The entries are of the form a.b.c.d/WORKGROUP with + WORKGROUP being optional */ + const char *wgroup; + char *pwgroup; + int i; + + pwgroup = strchr_m(s2,'/'); + if (pwgroup) + *pwgroup++ = 0; + if (!pwgroup || !*pwgroup) + wgroup = lp_workgroup(); + else + wgroup = pwgroup; + + addr = *interpret_addr2(mem_ctx, s2); + + /* Announce all our names including aliases */ + /* Give the ip address as the address of our first + broadcast subnet. */ + + for(i=0; my_netbios_names(i); i++) { + const char *name = my_netbios_names(i); + + DEBUG(5,("announce_remote: Doing remote announce for server %s to IP %s.\n", + name, inet_ntoa(addr) )); + + send_announcement(FIRST_SUBNET, ANN_HostAnnouncement, + name, /* From nbt name. */ + wgroup, 0x1d, /* To nbt name. */ + addr, /* To ip. */ + REMOTE_ANNOUNCE_INTERVAL, /* Time until next announce. */ + name, /* Name to announce. */ + stype, /* Type field. */ + comment); + } + } + + talloc_destroy( mem_ctx ); +} + +/**************************************************************************** + Implement the 'remote browse sync' feature Andrew added. + These are used to put our browse lists into remote browse lists. +**************************************************************************/ + +void browse_sync_remote(time_t t) +{ + char *s; + const char *ptr; + static time_t last_time = 0; + pstring s2; + struct in_addr addr; + struct work_record *work; + pstring outbuf; + char *p; + unstring myname; + TALLOC_CTX *mem_ctx; + + if (last_time && (t < (last_time + REMOTE_ANNOUNCE_INTERVAL))) + return; + + last_time = t; + + s = lp_remote_browse_sync(); + if (!*s) + return; + + /* + * We only do this if we are the local master browser + * for our workgroup on the firsst subnet. + */ + + if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) { + DEBUG(0,("browse_sync_remote: Cannot find workgroup %s on subnet %s\n", + lp_workgroup(), FIRST_SUBNET->subnet_name )); + return; + } + + if(!AM_LOCAL_MASTER_BROWSER(work)) { + DEBUG(5,("browse_sync_remote: We can only do this if we are a local master browser \ +for workgroup %s on subnet %s.\n", lp_workgroup(), FIRST_SUBNET->subnet_name )); + return; + } + + memset(outbuf,'\0',sizeof(outbuf)); + p = outbuf; + SCVAL(p,0,ANN_MasterAnnouncement); + p++; + + unstrcpy(myname, global_myname()); + strupper_m(myname); + myname[15]='\0'; + push_pstring_base(p, myname, outbuf); + + p = skip_string(p,1); + + if ( !(mem_ctx = talloc_init( "browse_sync_remote" )) ) { + DEBUG(0,("announce_remote: talloc_init() failed!\n")); + return; + } + + + for (ptr=s; next_token(&ptr,s2,NULL,sizeof(s2)); ) { + /* The entries are of the form a.b.c.d */ + addr = *interpret_addr2(mem_ctx, s2); + + DEBUG(5,("announce_remote: Doing remote browse sync announce for server %s to IP %s.\n", + global_myname(), inet_ntoa(addr) )); + + send_mailslot(True, BROWSE_MAILSLOT, outbuf,PTR_DIFF(p,outbuf), + global_myname(), 0x0, "*", 0x0, addr, FIRST_SUBNET->myip, DGRAM_PORT); + } + + talloc_destroy( mem_ctx ); +} diff --git a/SAMBA_3_2_MERGE/source/nmbd/nmbd_synclists.c b/SAMBA_3_2_MERGE/source/nmbd/nmbd_synclists.c new file mode 100644 index 00000000000..c33486395df --- /dev/null +++ b/SAMBA_3_2_MERGE/source/nmbd/nmbd_synclists.c @@ -0,0 +1,310 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios routines and daemon - version 2 + Copyright (C) Andrew Tridgell 1994-1998 + Copyright (C) Luke Kenneth Casson Leighton 1994-1998 + Copyright (C) Jeremy Allison 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. + +*/ + +/* this file handles asynchronous browse synchronisation requests. The + requests are done by forking and putting the result in a file in the + locks directory. We do it this way because we don't want nmbd to be + blocked waiting for some server to respond on a TCP connection. This + also allows us to have more than 1 sync going at once (tridge) */ + +#include "includes.h" + +struct sync_record { + struct sync_record *next, *prev; + unstring workgroup; + unstring server; + pstring fname; + struct in_addr ip; + pid_t pid; +}; + +/* a linked list of current sync connections */ +static struct sync_record *syncs; + +static XFILE *fp; + +/******************************************************************* + This is the NetServerEnum callback. + Note sname and comment are in UNIX codepage format. + ******************************************************************/ + +static void callback(const char *sname, uint32 stype, + const char *comment, void *state) +{ + x_fprintf(fp,"\"%s\" %08X \"%s\"\n", sname, stype, comment); +} + +/******************************************************************* + Synchronise browse lists with another browse server. + Log in on the remote server's SMB port to their IPC$ service, + do a NetServerEnum and record the results in fname +******************************************************************/ + +static void sync_child(char *name, int nm_type, + char *workgroup, + struct in_addr ip, BOOL local, BOOL servers, + char *fname) +{ + extern fstring local_machine; + fstring unix_workgroup; + static struct cli_state *cli; + uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0; + struct nmb_name called, calling; + + /* W2K DMB's return empty browse lists on port 445. Use 139. + * Patch from Andy Levine andyl@epicrealm.com. + */ + + make_nmb_name(&calling, local_machine, 0x0); + make_nmb_name(&called , name, nm_type); + + /* have to open a new connection */ + if (!(cli=cli_state_init()) || !cli_socket_connect(cli, name, &ip)) { + DEBUG(1,("sync_child: Connection to %s failed\n", name)); + return; + } + + if (!cli_transport_establish(cli, &calling, &called)) { + DEBUG(2,("sync_child: cli_transport_establish() to %s failed\n", name )); + return; + } + + + if ( !NT_STATUS_IS_OK(cli_negprot(cli)) ) { + cli_shutdown(cli); + return; + } + + if ( !NT_STATUS_IS_OK(cli_session_setup(cli, "", "", workgroup)) ) { + cli_shutdown(cli); + return; + } + + if ( !NT_STATUS_IS_OK(cli_send_tconX(cli, "IPC$", "IPC", "")) ) { + cli_shutdown(cli); + return; + } + + /* All the cli_XX functions take UNIX character set. */ + fstrcpy( unix_workgroup, cli->transport->negotiate.server_domain ? + cli->transport->negotiate.server_domain : workgroup ); + +#if 0 /* JERRY -- temporarily disabled until we implement this call */ + /* Fetch a workgroup list. */ + cli_NetServerEnum(&cli, unix_workgroup, + local_type|SV_TYPE_DOMAIN_ENUM, + callback, NULL); + + /* Now fetch a server list. */ + if (servers) { + fstrcpy(unix_workgroup, workgroup); + cli_NetServerEnum(&cli, unix_workgroup, + local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL, + callback, NULL); + } +#endif + + cli_shutdown(cli); +} + +/******************************************************************* + initialise a browse sync with another browse server. Log in on the + remote server's SMB port to their IPC$ service, do a NetServerEnum + and record the results +******************************************************************/ + +void sync_browse_lists(struct work_record *work, + char *name, int nm_type, + struct in_addr ip, BOOL local, BOOL servers) +{ + struct sync_record *s; + static int counter; + + START_PROFILE(sync_browse_lists); + /* Check we're not trying to sync with ourselves. This can + happen if we are a domain *and* a local master browser. */ + if (ismyip(ip)) { +done: + END_PROFILE(sync_browse_lists); + return; + } + + s = (struct sync_record *)malloc(sizeof(*s)); + if (!s) goto done; + + ZERO_STRUCTP(s); + + unstrcpy(s->workgroup, work->work_group); + unstrcpy(s->server, name); + s->ip = ip; + + slprintf(s->fname, sizeof(pstring)-1, + "%s/sync.%d", lp_lockdir(), counter++); + all_string_sub(s->fname,"//", "/", 0); + + DLIST_ADD(syncs, s); + + /* the parent forks and returns, leaving the child to do the + actual sync and call END_PROFILE*/ + CatchChild(); + if ((s->pid = sys_fork())) return; + + BlockSignals( False, SIGTERM ); + + DEBUG(2,("Initiating browse sync for %s to %s(%s)\n", + work->work_group, name, inet_ntoa(ip))); + + fp = x_fopen(s->fname,O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (!fp) { + END_PROFILE(sync_browse_lists); + _exit(1); + } + + sync_child(name, nm_type, work->work_group, ip, local, servers, + s->fname); + + x_fclose(fp); + END_PROFILE(sync_browse_lists); + _exit(0); +} + +/********************************************************************** + Handle one line from a completed sync file. + **********************************************************************/ + +static void complete_one(struct sync_record *s, + char *sname, uint32 stype, char *comment) +{ + struct work_record *work; + struct server_record *servrec; + + stype &= ~SV_TYPE_LOCAL_LIST_ONLY; + + if (stype & SV_TYPE_DOMAIN_ENUM) { + /* See if we can find the workgroup on this subnet. */ + if((work=find_workgroup_on_subnet(unicast_subnet, sname))) { + /* We already know about this workgroup - + update the ttl. */ + update_workgroup_ttl(work,lp_max_ttl()); + } else { + /* Create the workgroup on the subnet. */ + work = create_workgroup_on_subnet(unicast_subnet, + sname, lp_max_ttl()); + if (work) { + /* remember who the master is */ + unstrcpy(work->local_master_browser_name, comment); + } + } + return; + } + + work = find_workgroup_on_subnet(unicast_subnet, s->workgroup); + if (!work) { + DEBUG(3,("workgroup %s doesn't exist on unicast subnet?\n", + s->workgroup)); + return; + } + + if ((servrec = find_server_in_workgroup( work, sname))) { + /* Check that this is not a locally known + server - if so ignore the entry. */ + if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) { + /* We already know about this server - update + the ttl. */ + update_server_ttl(servrec, lp_max_ttl()); + /* Update the type. */ + servrec->serv.type = stype; + } + return; + } + + /* Create the server in the workgroup. */ + create_server_on_workgroup(work, sname,stype, lp_max_ttl(), comment); +} + +/********************************************************************** + Read the completed sync info. +**********************************************************************/ + +static void complete_sync(struct sync_record *s) +{ + XFILE *f; + unstring server, type_str; + unsigned type; + pstring comment; + pstring line; + const char *ptr; + int count=0; + + f = x_fopen(s->fname,O_RDONLY, 0); + + if (!f) + return; + + while (!x_feof(f)) { + + if (!fgets_slash(line,sizeof(pstring),f)) + continue; + + ptr = line; + + if (!next_token(&ptr,server,NULL,sizeof(server)) || + !next_token(&ptr,type_str,NULL, sizeof(type_str)) || + !next_token(&ptr,comment,NULL, sizeof(comment))) { + continue; + } + + sscanf(type_str, "%X", &type); + + complete_one(s, server, type, comment); + + count++; + } + + x_fclose(f); + + unlink(s->fname); + + DEBUG(2,("sync with %s(%s) for workgroup %s completed (%d records)\n", + s->server, inet_ntoa(s->ip), s->workgroup, count)); +} + +/********************************************************************** + Check for completion of any of the child processes. +**********************************************************************/ + +void sync_check_completion(void) +{ + struct sync_record *s, *next; + + for (s=syncs;s;s=next) { + next = s->next; + if (!process_exists(s->pid)) { + /* it has completed - grab the info */ + complete_sync(s); + DLIST_REMOVE(syncs, s); + ZERO_STRUCTP(s); + SAFE_FREE(s); + } + } +} diff --git a/SAMBA_3_2_MERGE/source/nmbd/nmbd_winsserver.c b/SAMBA_3_2_MERGE/source/nmbd/nmbd_winsserver.c new file mode 100644 index 00000000000..e7fd458317f --- /dev/null +++ b/SAMBA_3_2_MERGE/source/nmbd/nmbd_winsserver.c @@ -0,0 +1,2034 @@ +/* + Unix SMB/CIFS implementation. + NBT netbios routines and daemon - version 2 + + Copyright (C) Jeremy Allison 1994-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" + +#define WINS_LIST "wins.dat" +#define WINS_VERSION 1 + +/**************************************************************************** + Change the wins owner address in the record. +*****************************************************************************/ + +static void update_wins_owner(struct name_record *namerec, struct in_addr wins_ip) +{ + if (namerec==NULL) + return; + namerec->data.wins_ip=wins_ip; +} + +/**************************************************************************** + Create the wins flags based on the nb flags and the input value. +*****************************************************************************/ + +static void update_wins_flag(struct name_record *namerec, int flags) +{ + if (namerec==NULL) + return; + + namerec->data.wins_flags=0x0; + + /* if it's a group, it can be a normal or a special one */ + if (namerec->data.nb_flags & NB_GROUP) { + if (namerec->name.name_type==0x1C) + namerec->data.wins_flags|=WINS_SGROUP; + else + if (namerec->data.num_ips>1) + namerec->data.wins_flags|=WINS_SGROUP; + else + namerec->data.wins_flags|=WINS_NGROUP; + } else { + /* can be unique or multi-homed */ + if (namerec->data.num_ips>1) + namerec->data.wins_flags|=WINS_MHOMED; + else + namerec->data.wins_flags|=WINS_UNIQUE; + } + + /* the node type are the same bits */ + namerec->data.wins_flags|=namerec->data.nb_flags&NB_NODETYPEMASK; + + /* the static bit is elsewhere */ + if (namerec->data.death_time == PERMANENT_TTL) + namerec->data.wins_flags|=WINS_STATIC; + + /* and add the given bits */ + namerec->data.wins_flags|=flags; + + DEBUG(8,("update_wins_flag: nbflags: 0x%x, ttl: 0x%d, flags: 0x%x, winsflags: 0x%x\n", + namerec->data.nb_flags, (int)namerec->data.death_time, flags, namerec->data.wins_flags)); +} + +/**************************************************************************** + Return the general ID value and increase it if requested. +*****************************************************************************/ + +static void get_global_id_and_update(SMB_BIG_UINT *current_id, BOOL update) +{ + /* + * it's kept as a static here, to prevent people from messing + * with the value directly + */ + + static SMB_BIG_UINT general_id = 1; + + DEBUG(5,("get_global_id_and_update: updating version ID: %d\n", (int)general_id)); + + *current_id = general_id; + + if (update) + general_id++; +} + +/**************************************************************************** + Possibly call the WINS hook external program when a WINS change is made. +*****************************************************************************/ + +static void wins_hook(const char *operation, struct name_record *namerec, int ttl) +{ + pstring command; + char *cmd = lp_wins_hook(); + char *p, *namestr; + int i; + + if (!cmd || !*cmd) return; + + for (p=namerec->name.name; *p; p++) { + if (!(isalnum((int)*p) || strchr_m("._-",*p))) { + DEBUG(3,("not calling wins hook for invalid name %s\n", nmb_namestr(&namerec->name))); + return; + } + } + + /* Use the name without the nametype (and scope) appended */ + + namestr = nmb_namestr(&namerec->name); + if ((p = strchr(namestr, '<'))) + *p = 0; + + p = command; + p += slprintf(p, sizeof(command)-1, "%s %s %s %02x %d", + cmd, + operation, + namestr, + namerec->name.name_type, + ttl); + + for (i=0;idata.num_ips;i++) { + p += slprintf(p, sizeof(command) - (p-command) -1, " %s", inet_ntoa(namerec->data.ip[i])); + } + + DEBUG(3,("calling wins hook for %s\n", nmb_namestr(&namerec->name))); + smbrun(command, NULL); +} + + +/**************************************************************************** +Determine if this packet should be allocated to the WINS server. +*****************************************************************************/ + +BOOL packet_is_for_wins_server(struct packet_struct *packet) +{ + struct nmb_packet *nmb = &packet->packet.nmb; + + /* Only unicast packets go to a WINS server. */ + if((wins_server_subnet == NULL) || (nmb->header.nm_flags.bcast == True)) { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #1.\n")); + return False; + } + + /* Check for node status requests. */ + if (nmb->question.question_type != QUESTION_TYPE_NB_QUERY) + return False; + + switch(nmb->header.opcode) { + /* + * A WINS server issues WACKS, not receives them. + */ + case NMB_WACK_OPCODE: + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #2 (WACK).\n")); + return False; + /* + * A WINS server only processes registration and + * release requests, not responses. + */ + case NMB_NAME_REG_OPCODE: + case NMB_NAME_MULTIHOMED_REG_OPCODE: + case NMB_NAME_REFRESH_OPCODE_8: /* ambiguity in rfc1002 about which is correct. */ + case NMB_NAME_REFRESH_OPCODE_9: /* WinNT uses 8 by default. */ + if(nmb->header.response) { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #3 (response = 1).\n")); + return False; + } + break; + + case NMB_NAME_RELEASE_OPCODE: + if(nmb->header.response) { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #4 (response = 1).\n")); + return False; + } + break; + + /* + * Only process unicast name queries with rd = 1. + */ + case NMB_NAME_QUERY_OPCODE: + if(!nmb->header.response && !nmb->header.nm_flags.recursion_desired) { + DEBUG(10, ("packet_is_for_wins_server: failing WINS test #5 (response = 1).\n")); + return False; + } + break; + } + + return True; +} + +/**************************************************************************** +Utility function to decide what ttl to give a register/refresh request. +*****************************************************************************/ + +static int get_ttl_from_packet(struct nmb_packet *nmb) +{ + int ttl = nmb->additional->ttl; + + if(ttl < lp_min_wins_ttl() ) + ttl = lp_min_wins_ttl(); + + if(ttl > lp_max_wins_ttl() ) + ttl = lp_max_wins_ttl(); + + return ttl; +} + +/**************************************************************************** +Load or create the WINS database. +*****************************************************************************/ + +BOOL initialise_wins(void) +{ + time_t time_now = time(NULL); + XFILE *fp; + pstring line; + TALLOC_CTX *mem_ctx; + + if(!lp_we_are_a_wins_server()) + return True; + + add_samba_names_to_subnet(wins_server_subnet); + + if((fp = x_fopen(lock_path(WINS_LIST),O_RDONLY,0)) == NULL) { + DEBUG(2,("initialise_wins: Can't open wins database file %s. Error was %s\n", + WINS_LIST, strerror(errno) )); + return True; + } + + if ( !(mem_ctx = talloc_init( "initialise_wins" )) ) { + DEBUG(0,("announce_remote: talloc_init() failed!\n")); + return False; + } + + + while (!x_feof(fp)) { + pstring name_str, ip_str, ttl_str, nb_flags_str; + unsigned int num_ips; + pstring name; + struct in_addr *ip_list; + int type = 0; + int nb_flags; + int ttl; + const char *ptr; + char *p; + BOOL got_token; + BOOL was_ip; + int i; + unsigned int hash; + int version; + + /* Read a line from the wins.dat file. Strips whitespace + from the beginning and end of the line. */ + if (!fgets_slash(line,sizeof(pstring),fp)) + continue; + + if (*line == '#') + continue; + + if (strncmp(line,"VERSION ", 8) == 0) { + if (sscanf(line,"VERSION %d %u", &version, &hash) != 2 || + version != WINS_VERSION) { + DEBUG(0,("Discarding invalid wins.dat file [%s]\n",line)); + x_fclose(fp); + return True; + } + continue; + } + + ptr = line; + + /* + * Now we handle multiple IP addresses per name we need + * to iterate over the line twice. The first time to + * determine how many IP addresses there are, the second + * time to actually parse them into the ip_list array. + */ + + if (!next_token(&ptr,name_str,NULL,sizeof(name_str))) { + DEBUG(0,("initialise_wins: Failed to parse name when parsing line %s\n", line )); + continue; + } + + if (!next_token(&ptr,ttl_str,NULL,sizeof(ttl_str))) { + DEBUG(0,("initialise_wins: Failed to parse time to live when parsing line %s\n", line )); + continue; + } + + /* + * Determine the number of IP addresses per line. + */ + num_ips = 0; + do { + got_token = next_token(&ptr,ip_str,NULL,sizeof(ip_str)); + was_ip = False; + + if(got_token && strchr(ip_str, '.')) { + num_ips++; + was_ip = True; + } + } while( got_token && was_ip); + + if(num_ips == 0) { + DEBUG(0,("initialise_wins: Missing IP address when parsing line %s\n", line )); + continue; + } + + if(!got_token) { + DEBUG(0,("initialise_wins: Missing nb_flags when parsing line %s\n", line )); + continue; + } + + /* Allocate the space for the ip_list. */ + if((ip_list = (struct in_addr *)malloc( num_ips * sizeof(struct in_addr))) == NULL) { + DEBUG(0,("initialise_wins: Malloc fail !\n")); + return False; + } + + /* Reset and re-parse the line. */ + ptr = line; + next_token(&ptr,name_str,NULL,sizeof(name_str)); + next_token(&ptr,ttl_str,NULL,sizeof(ttl_str)); + for(i = 0; i < num_ips; i++) { + next_token(&ptr, ip_str, NULL, sizeof(ip_str)); + ip_list[i] = *interpret_addr2(mem_ctx, ip_str); + } + next_token(&ptr,nb_flags_str,NULL, sizeof(nb_flags_str)); + + /* + * Deal with SELF or REGISTER name encoding. Default is REGISTER + * for compatibility with old nmbds. + */ + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'S') { + DEBUG(5,("initialise_wins: Ignoring SELF name %s\n", line)); + SAFE_FREE(ip_list); + continue; + } + + if(nb_flags_str[strlen(nb_flags_str)-1] == 'R') + nb_flags_str[strlen(nb_flags_str)-1] = '\0'; + + /* Netbios name. # divides the name from the type (hex): netbios#xx */ + pstrcpy(name,name_str); + + if((p = strchr(name,'#')) != NULL) { + *p = 0; + sscanf(p+1,"%x",&type); + } + + /* Decode the netbios flags (hex) and the time-to-live (in seconds). */ + sscanf(nb_flags_str,"%x",&nb_flags); + sscanf(ttl_str,"%d",&ttl); + + /* add all entries that have 60 seconds or more to live */ + if ((ttl - 60) > time_now || ttl == PERMANENT_TTL) { + if(ttl != PERMANENT_TTL) + ttl -= time_now; + + DEBUG( 4, ("initialise_wins: add name: %s#%02x ttl = %d first IP %s flags = %2x\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + + (void)add_name_to_subnet( wins_server_subnet, name, type, nb_flags, + ttl, REGISTER_NAME, num_ips, ip_list ); + } else { + DEBUG(4, ("initialise_wins: not adding name (ttl problem) %s#%02x ttl = %d first IP %s flags = %2x\n", + name, type, ttl, inet_ntoa(ip_list[0]), nb_flags)); + } + + SAFE_FREE(ip_list); + } + + x_fclose(fp); + return True; +} + +/**************************************************************************** +Send a WINS WACK (Wait ACKnowledgement) response. +**************************************************************************/ + +static void send_wins_wack_response(int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + unsigned char rdata[2]; + + rdata[0] = rdata[1] = 0; + + /* Taken from nmblib.c - we need to send back almost + identical bytes from the requesting packet header. */ + + rdata[0] = (nmb->header.opcode & 0xF) << 3; + if (nmb->header.nm_flags.authoritative && nmb->header.response) + rdata[0] |= 0x4; + if (nmb->header.nm_flags.trunc) + rdata[0] |= 0x2; + if (nmb->header.nm_flags.recursion_desired) + rdata[0] |= 0x1; + if (nmb->header.nm_flags.recursion_available && nmb->header.response) + rdata[1] |= 0x80; + if (nmb->header.nm_flags.bcast) + rdata[1] |= 0x10; + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + NMB_WAIT_ACK, /* nmbd type code. */ + NMB_WACK_OPCODE, /* opcode. */ + ttl, /* ttl. */ + (char *)rdata, /* data to send. */ + 2); /* data length. */ +} + +/**************************************************************************** +Send a WINS name registration response. +**************************************************************************/ + +static void send_wins_name_registration_response(int rcode, int ttl, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_REG, /* nmbd type code. */ + NMB_NAME_REG_OPCODE, /* opcode. */ + ttl, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name refresh request to a WINS server. +************************************************************************/ + +void wins_process_name_refresh_request( struct subnet_record *subrec, + struct packet_struct *p ) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct name_record *namerec = NULL; + int ttl = get_ttl_from_packet(nmb); + struct in_addr from_ip; + struct in_addr our_fake_ip; + + zero_ip( &our_fake_ip ); + + putip( (char *)&from_ip, &nmb->additional->rdata[2] ); + + if(bcast) { + /* + * We should only get unicast name refresh packets here. + * Anyone trying to refresh broadcast should not be going + * to a WINS server. Log an error here. + */ + if( DEBUGLVL( 0 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Broadcast name refresh request received " ); + dbgtext( "for name %s ", nmb_namestr(question) ); + dbgtext( "from IP %s ", inet_ntoa(from_ip) ); + dbgtext( "on subnet %s. ", subrec->subnet_name ); + dbgtext( "Error - Broadcasts should not be sent " ); + dbgtext( "to a WINS server\n" ); + } + return; + } + + if( DEBUGLVL( 3 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name refresh for name %s IP %s\n", + nmb_namestr(question), inet_ntoa(from_ip) ); + } + + /* + * See if the name already exists. + * If not, handle it as a name registration and return. + */ + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * If this is a refresh request and the name doesn't exist then + * treat it like a registration request. This allows us to recover + * from errors (tridge) + */ + if(namerec == NULL) { + if( DEBUGLVL( 3 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name refresh for name %s ", + nmb_namestr( question ) ); + dbgtext( "and the name does not exist. Treating " ); + dbgtext( "as registration.\n" ); + } + wins_process_name_registration_request(subrec,p); + return; + } + + /* + * if the name is present but not active, simply remove it + * and treat the refresh request as a registration & return. + */ + if (namerec != NULL && !WINS_STATE_ACTIVE(namerec)) { + if( DEBUGLVL( 5 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name (%s) in WINS ", nmb_namestr(question) ); + dbgtext( "was not active - removing it.\n" ); + } + remove_name_from_namelist( subrec, namerec ); + namerec = NULL; + wins_process_name_registration_request( subrec, p ); + return; + } + + /* + * Check that the group bits for the refreshing name and the + * name in our database match. If not, refuse the refresh. + * [crh: Why RFS_ERR instead of ACT_ERR? Is this what MS does?] + */ + if( (namerec != NULL) && + ( (group && !NAME_GROUP(namerec)) + || (!group && NAME_GROUP(namerec)) ) ) { + if( DEBUGLVL( 3 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name %s ", nmb_namestr(question) ); + dbgtext( "group bit = %s does not match ", + group ? "True" : "False" ); + dbgtext( "group bit in WINS for this name.\n" ); + } + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * For a unique name check that the person refreshing the name is + * one of the registered IP addresses. If not - fail the refresh. + * Do the same for group names with a type of 0x1c. + * Just return success for unique 0x1d refreshes. For normal group + * names update the ttl and return success. + */ + if( (!group || (group && (question->name_type == 0x1c))) + && find_ip_in_name_record(namerec, from_ip) ) { + /* + * Update the ttl. + */ + update_name_ttl(namerec, ttl); + + /* + * if the record is a replica: + * we take ownership and update the version ID. + */ + if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) { + update_wins_owner(namerec, our_fake_ip); + get_global_id_and_update(&namerec->data.id, True); + } + + send_wins_name_registration_response(0, ttl, p); + wins_hook("refresh", namerec, ttl); + return; + } else if((group && (question->name_type == 0x1c))) { + /* + * Added by crh for bug #1079. + * Fix from Bert Driehuis + */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name refresh for name %s, ", + nmb_namestr(question) ); + dbgtext( "but IP address %s ", inet_ntoa(from_ip) ); + dbgtext( "is not yet associated with " ); + dbgtext( "that name. Treating as registration.\n" ); + } + wins_process_name_registration_request(subrec,p); + return; + } else if(group) { + /* + * Normal groups are all registered with an IP address of + * 255.255.255.255 so we can't search for the IP address. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } else if(!group && (question->name_type == 0x1d)) { + /* + * Special name type - just pretend the refresh succeeded. + */ + send_wins_name_registration_response(0, ttl, p); + return; + } else { + /* + * Fail the refresh. + */ + if( DEBUGLVL( 3 ) ) { + dbgtext( "wins_process_name_refresh_request: " ); + dbgtext( "Name refresh for name %s with IP %s ", + nmb_namestr(question), inet_ntoa(from_ip) ); + dbgtext( "and is IP is not known to the name.\n" ); + } + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } +} + +/*********************************************************************** + Deal with a name registration request query success to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The success here is actually a failure as it means + the client we queried wants to keep the name, so we must return + a registration failure to the original requestor. +************************************************************************/ + +static void wins_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_register_query_success: Original client at IP %s still wants the \ +name %s. Rejecting registration request.\n", inet_ntoa(ip), nmb_namestr(question_name) )); + + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. The failure here is actually a success as it means + the client we queried didn't want to keep the name, so we can remove + the old name record and then successfully add the new name. +************************************************************************/ + +static void wins_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + struct name_record *namerec = NULL; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + /* + * We want to just add the name, as we now know the original owner + * didn't want it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this timeout/error response. So we check that + * the name still exists and is in the same state - if so + * we remove it and call wins_process_name_registration_request() + * as we know it will do the right thing now. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if( (namerec != NULL) && (namerec->data.source == REGISTER_NAME) && ip_equal(rrec->packet->ip, *namerec->data.ip) ) { + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + if(namerec == NULL) + wins_process_name_registration_request(subrec, orig_reg_packet); + else + DEBUG(2,("wins_register_query_fail: The state of the WINS database changed between \ +querying for name %s in order to replace it and this reply.\n", nmb_namestr(question_name) )); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request to a WINS server. + + Use the following pseudocode : + + registering_group + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- add name (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + registering_unique + | + | + +--------name exists + | | + | | + | +--- existing name is group + | | | + | | | + | | +--- fail add (return). + | | + | | + | +--- exiting name is unique + | | + | | + | +--- query existing owner (return). + | + | + +--------name doesn't exist + | + | + +--- add name (return). + + As can be seen from the above, the two cases may be collapsed onto each + other with the exception of the case where the name already exists and + is a group name. This case we handle with an if statement. + +************************************************************************/ + +void wins_process_name_registration_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + unstring name; + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL registering_group_name = (nb_flags & NB_GROUP) ? True : False; + struct in_addr our_fake_ip; + + zero_ip( &our_fake_ip ); + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_registration_request: %s name registration for name %s \ +IP %s\n", registering_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) )); + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * if the record exists but NOT in active state, + * consider it dead. + */ + if ( (namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) { + DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \ +not active - removing it.\n", nmb_namestr(question) )); + remove_name_from_namelist( subrec, namerec ); + namerec = NULL; + } + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) { + DEBUG(5,("wins_process_name_registration_request: Name (%s) in WINS was \ +a dns lookup - removing it.\n", nmb_namestr(question) )); + remove_name_from_namelist( subrec, namerec ); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) { + DEBUG( 3, ( "wins_process_name_registration_request: Attempt \ +to register name %s. Name already exists in WINS with source type %d.\n", + nmb_namestr(question), namerec->data.source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Special policy decisions based on MS documentation. + * 1). All group names (except names ending in 0x1c) are added as 255.255.255.255. + * 2). All unique names ending in 0x1d are ignored, although a positive response is sent. + */ + + /* + * A group name is always added as the local broadcast address, except + * for group names ending in 0x1c. + * Group names with type 0x1c are registered with individual IP addresses. + */ + + if(registering_group_name && (question->name_type != 0x1c)) + one_ip( &from_ip ); + + /* + * Ignore all attempts to register a unique 0x1d name, although return success. + */ + + if(!registering_group_name && (question->name_type == 0x1d)) { + DEBUG(3,("wins_process_name_registration_request: Ignoring request \ +to register name %s from IP %s.\n", nmb_namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * Next two cases are the 'if statement' mentioned above. + */ + + if((namerec != NULL) && NAME_GROUP(namerec)) { + if(registering_group_name) { + /* + * If we are adding a group name, the name exists and is also a group entry just add this + * IP address to it and update the ttl. + */ + + DEBUG(3,("wins_process_name_registration_request: Adding IP %s to group name %s.\n", + inet_ntoa(from_ip), nmb_namestr(question) )); + + /* + * Check the ip address is not already in the group. + */ + + if(!find_ip_in_name_record(namerec, from_ip)) { + add_ip_to_name_record(namerec, from_ip); + /* we need to update the record for replication */ + get_global_id_and_update(&namerec->data.id, True); + + /* + * if the record is a replica, we must change + * the wins owner to us to make the replication updates + * it on the other wins servers. + * And when the partner will receive this record, + * it will update its own record. + */ + + update_wins_owner(namerec, our_fake_ip); + } + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } else { + + /* + * If we are adding a unique name, the name exists in the WINS db + * and is a group name then reject the registration. + * + * explanation: groups have a higher priority than unique names. + */ + + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", nmb_namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if ( namerec != NULL ) + pull_ascii_nstring(name, sizeof(name), namerec->name.name); + + if( is_myname(name) ) { + if(!ismyip(from_ip)) { + DEBUG(3,("wins_process_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } else { + /* + * It's one of our names and one of our IP's - update the ttl. + */ + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + wins_hook("refresh", namerec, ttl); + return; + } + } + + /* + * If the name exists and it is a unique registration and the registering IP + * is the same as the (single) already registered IP then just update the ttl. + * + * But not if the record is an active replica. IF it's a replica, it means it can be + * the same client which has moved and not yet expired. So we don't update + * the ttl in this case and go beyond to do a WACK and query the old client + */ + + if( !registering_group_name + && (namerec != NULL) + && (namerec->data.num_ips == 1) + && ip_equal( namerec->data.ip[0], from_ip ) + && ip_equal(namerec->data.wins_ip, our_fake_ip) ) { + update_name_ttl( namerec, ttl ); + send_wins_name_registration_response( 0, ttl, p ); + wins_hook("refresh", namerec, ttl); + return; + } + + /* + * Finally if the name exists do a query to the registering machine + * to see if they still claim to have the name. + */ + + if( namerec != NULL ) { + long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1]; + struct userdata_struct *userdata = (struct userdata_struct *)ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * Use the new call to send a query directly to an IP address. + * This sends the query directly to the IP address, and ensures + * the recursion desired flag is not set (you were right Luke :-). + * This function should *only* be called from the WINS server + * code. JRA. + */ + + pull_ascii_nstring(name, sizeof(name), question->name); + query_name_from_wins_server( *namerec->data.ip, + name, + question->name_type, + wins_register_query_success, + wins_register_query_fail, + userdata ); + return; + } + + /* + * Name did not exist - add it. + */ + + pull_ascii_nstring(name, sizeof(name), question->name); + add_name_to_subnet( subrec, name, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) { + get_global_id_and_update(&namerec->data.id, True); + update_wins_owner(namerec, our_fake_ip); + update_wins_flag(namerec, WINS_ACTIVE); + wins_hook("add", namerec, ttl); + } + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with a mutihomed name query success to the machine that + requested the multihomed name registration. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_success(struct subnet_record *subrec, + struct userdata_struct *userdata, + struct nmb_name *question_name, + struct in_addr ip, + struct res_rec *answers) +{ + struct packet_struct *orig_reg_packet; + struct nmb_packet *nmb; + struct name_record *namerec = NULL; + struct in_addr from_ip; + int ttl; + struct in_addr our_fake_ip; + + zero_ip( &our_fake_ip ); + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + nmb = &orig_reg_packet->packet.nmb; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + ttl = get_ttl_from_packet(nmb); + + /* + * We want to just add the new IP, as we now know the requesting + * machine claims to own it. But we can't just do that as an arbitary + * amount of time may have taken place between the name query + * request and this response. So we check that + * the name still exists and is in the same state - if so + * we just add the extra IP and update the ttl. + */ + + namerec = find_name_on_subnet(subrec, question_name, FIND_ANY_NAME); + + if( (namerec == NULL) || (namerec->data.source != REGISTER_NAME) || !WINS_STATE_ACTIVE(namerec) ) { + DEBUG(3,("wins_multihomed_register_query_success: name %s is not in the correct state to add \ +a subsequent IP address.\n", nmb_namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); + + return; + } + + if(!find_ip_in_name_record(namerec, from_ip)) + add_ip_to_name_record(namerec, from_ip); + + get_global_id_and_update(&namerec->data.id, True); + update_wins_owner(namerec, our_fake_ip); + update_wins_flag(namerec, WINS_ACTIVE); + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, orig_reg_packet); + wins_hook("add", namerec, ttl); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); +} + +/*********************************************************************** + Deal with a name registration request query failure to a client that + owned the name. + + We have a locked pointer to the original packet stashed away in the + userdata pointer. +************************************************************************/ + +static void wins_multihomed_register_query_fail(struct subnet_record *subrec, + struct response_record *rrec, + struct nmb_name *question_name, + int rcode) +{ + struct userdata_struct *userdata = rrec->userdata; + struct packet_struct *orig_reg_packet; + + memcpy((char *)&orig_reg_packet, userdata->data, sizeof(struct packet_struct *)); + + DEBUG(3,("wins_multihomed_register_query_fail: Registering machine at IP %s failed to answer \ +query successfully for name %s.\n", inet_ntoa(orig_reg_packet->ip), nmb_namestr(question_name) )); + send_wins_name_registration_response(RFS_ERR, 0, orig_reg_packet); + + orig_reg_packet->locked = False; + free_packet(orig_reg_packet); + return; +} + +/*********************************************************************** + Deal with a multihomed name registration request to a WINS server. + These cannot be group name registrations. +***********************************************************************/ + +void wins_process_multihomed_name_registration_request( struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + int ttl = get_ttl_from_packet(nmb); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL group = (nb_flags & NB_GROUP) ? True : False; + struct in_addr our_fake_ip; + unstring qname; + + zero_ip( &our_fake_ip ); + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_multihomed_name_registration_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + /* + * Only unique names should be registered multihomed. + */ + + if(group) { + DEBUG(0,("wins_process_multihomed_name_registration_request: group name registration request \ +received for name %s from IP %s on subnet %s. Errror - group names should not be multihomed.\n", + nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_multihomed_name_registration_request: name registration for name %s \ +IP %s\n", nmb_namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(question->name_type == 0x1d) { + DEBUG(3,("wins_process_multihomed_name_registration_request: Ignoring request \ +to register name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) )); + send_wins_name_registration_response(0, ttl, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + /* + * if the record exists but NOT in active state, + * consider it dead. + */ + + if ((namerec != NULL) && !WINS_STATE_ACTIVE(namerec)) { + DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was not active - removing it.\n", nmb_namestr(question))); + remove_name_from_namelist(subrec, namerec); + namerec = NULL; + } + + /* + * Deal with the case where the name found was a dns entry. + * Remove it as we now have a NetBIOS client registering the + * name. + */ + + if( (namerec != NULL) && ( (namerec->data.source == DNS_NAME) || (namerec->data.source == DNSFAIL_NAME) ) ) { + DEBUG(5,("wins_process_multihomed_name_registration_request: Name (%s) in WINS was a dns lookup \ +- removing it.\n", nmb_namestr(question) )); + remove_name_from_namelist( subrec, namerec); + namerec = NULL; + } + + /* + * Reject if the name exists and is not a REGISTER_NAME. + * (ie. Don't allow any static names to be overwritten. + */ + + if( (namerec != NULL) && (namerec->data.source != REGISTER_NAME) ) { + DEBUG( 3, ( "wins_process_multihomed_name_registration_request: Attempt \ +to register name %s. Name already exists in WINS with source type %d.\n", + nmb_namestr(question), namerec->data.source )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * Reject if the name exists and is a GROUP name and is active. + */ + + if((namerec != NULL) && NAME_GROUP(namerec) && WINS_STATE_ACTIVE(namerec)) { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +already exists in WINS as a GROUP name.\n", nmb_namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } + + /* + * From here on down we know that if the name exists in the WINS db it is + * a unique name, not a group name. + */ + + /* + * If the name exists and is one of our names then check the + * registering IP address. If it's not one of ours then automatically + * reject without doing the query - we know we will reject it. + */ + + if((namerec != NULL) && (is_myname(namerec->name.name)) ) { + if(!ismyip(from_ip)) { + DEBUG(3,("wins_process_multihomed_name_registration_request: Attempt to register name %s. Name \ +is one of our (WINS server) names. Denying registration.\n", nmb_namestr(question) )); + send_wins_name_registration_response(RFS_ERR, 0, p); + return; + } else { + /* + * It's one of our names and one of our IP's. Ensure the IP is in the record and + * update the ttl. Update the version ID to force replication. + */ + if(!find_ip_in_name_record(namerec, from_ip)) { + get_global_id_and_update(&namerec->data.id, True); + update_wins_owner(namerec, our_fake_ip); + update_wins_flag(namerec, WINS_ACTIVE); + + add_ip_to_name_record(namerec, from_ip); + wins_hook("add", namerec, ttl); + } else { + wins_hook("refresh", namerec, ttl); + } + + update_name_ttl(namerec, ttl); + send_wins_name_registration_response(0, ttl, p); + return; + } + } + + /* + * If the name exists and is active, check if the IP address is already registered + * to that name. If so then update the ttl and reply success. + */ + + if((namerec != NULL) && find_ip_in_name_record(namerec, from_ip) && WINS_STATE_ACTIVE(namerec)) { + update_name_ttl(namerec, ttl); + + /* + * If it's a replica, we need to become the wins owner + * to force the replication + */ + if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) { + get_global_id_and_update(&namerec->data.id, True); + update_wins_owner(namerec, our_fake_ip); + update_wins_flag(namerec, WINS_ACTIVE); + } + + send_wins_name_registration_response(0, ttl, p); + wins_hook("refresh", namerec, ttl); + return; + } + + /* + * If the name exists do a query to the owner + * to see if they still want the name. + */ + + if(namerec != NULL) { + long *ud[(sizeof(struct userdata_struct) + sizeof(struct packet_struct *))/sizeof(long *) + 1]; + struct userdata_struct *userdata = (struct userdata_struct *)ud; + + /* + * First send a WACK to the registering machine. + */ + + send_wins_wack_response(60, p); + + /* + * When the reply comes back we need the original packet. + * Lock this so it won't be freed and then put it into + * the userdata structure. + */ + + p->locked = True; + + userdata = (struct userdata_struct *)ud; + + userdata->copy_fn = NULL; + userdata->free_fn = NULL; + userdata->userdata_len = sizeof(struct packet_struct *); + memcpy(userdata->data, (char *)&p, sizeof(struct packet_struct *) ); + + /* + * Use the new call to send a query directly to an IP address. + * This sends the query directly to the IP address, and ensures + * the recursion desired flag is not set (you were right Luke :-). + * This function should *only* be called from the WINS server + * code. JRA. + * + * Note that this packet is sent to the current owner of the name, + * not the person who sent the packet + */ + + pull_ascii_nstring( qname, sizeof(qname), question->name); + query_name_from_wins_server( namerec->data.ip[0], + qname, + question->name_type, + wins_multihomed_register_query_success, + wins_multihomed_register_query_fail, + userdata ); + + return; + } + + /* + * Name did not exist - add it. + */ + + pull_ascii_nstring( qname, sizeof(qname), question->name); + add_name_to_subnet( subrec, qname, question->name_type, + nb_flags, ttl, REGISTER_NAME, 1, &from_ip); + + if ((namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME))) { + get_global_id_and_update(&namerec->data.id, True); + update_wins_owner(namerec, our_fake_ip); + update_wins_flag(namerec, WINS_ACTIVE); + wins_hook("add", namerec, ttl); + } + + send_wins_name_registration_response(0, ttl, p); +} + +/*********************************************************************** + Deal with the special name query for *<1b>. +***********************************************************************/ + +static void process_wins_dmb_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct name_record *namerec = NULL; + char *prdata; + int num_ips; + + /* + * Go through all the ACTIVE names in the WINS db looking for those + * ending in <1b>. Use this to calculate the number of IP + * addresses we need to return. + */ + + num_ips = 0; + for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); + namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) { + if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b ) + num_ips += namerec->data.num_ips; + } + + if(num_ips == 0) { + /* + * There are no 0x1b names registered. Return name query fail. + */ + send_wins_name_query_response(NAM_ERR, p, NULL); + return; + } + + if((prdata = (char *)malloc( num_ips * 6 )) == NULL) { + DEBUG(0,("process_wins_dmb_query_request: Malloc fail !.\n")); + return; + } + + /* + * Go through all the names again in the WINS db looking for those + * ending in <1b>. Add their IP addresses into the list we will + * return. + */ + + num_ips = 0; + for( namerec = (struct name_record *)ubi_trFirst( subrec->namelist ); + namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) { + if(WINS_STATE_ACTIVE(namerec) && namerec->name.name_type == 0x1b) { + int i; + for(i = 0; i < namerec->data.num_ips; i++) { + set_nb_flags(&prdata[num_ips * 6],namerec->data.nb_flags); + putip((char *)&prdata[(num_ips * 6) + 2], &namerec->data.ip[i]); + num_ips++; + } + } + } + + /* + * Send back the reply containing the IP list. + */ + + reply_netbios_packet(p, /* Packet to reply to. */ + 0, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + lp_min_wins_ttl(), /* ttl. */ + prdata, /* data to send. */ + num_ips*6); /* data length. */ + + SAFE_FREE(prdata); +} + +/**************************************************************************** +Send a WINS name query response. +**************************************************************************/ + +void send_wins_name_query_response(int rcode, struct packet_struct *p, + struct name_record *namerec) +{ + char rdata[6]; + char *prdata = rdata; + int reply_data_len = 0; + int ttl = 0; + int i; + + memset(rdata,'\0',6); + + if(rcode == 0) { + ttl = (namerec->data.death_time != PERMANENT_TTL) ? namerec->data.death_time - p->timestamp : lp_max_wins_ttl(); + + /* Copy all known ip addresses into the return data. */ + /* Optimise for the common case of one IP address so we don't need a malloc. */ + + if( namerec->data.num_ips == 1 ) { + prdata = rdata; + } else { + if((prdata = (char *)malloc( namerec->data.num_ips * 6 )) == NULL) { + DEBUG(0,("send_wins_name_query_response: malloc fail !\n")); + return; + } + } + + for(i = 0; i < namerec->data.num_ips; i++) { + set_nb_flags(&prdata[i*6],namerec->data.nb_flags); + putip((char *)&prdata[2+(i*6)], &namerec->data.ip[i]); + } + + sort_query_replies(prdata, i, p->ip); + reply_data_len = namerec->data.num_ips * 6; + } + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + WINS_QUERY, /* nmbd type code. */ + NMB_NAME_QUERY_OPCODE, /* opcode. */ + ttl, /* ttl. */ + prdata, /* data to send. */ + reply_data_len); /* data length. */ + + if(prdata != rdata) + SAFE_FREE(prdata); +} + +/*********************************************************************** + Deal with a name query. +***********************************************************************/ + +void wins_process_name_query_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + struct name_record *namerec = NULL; + unstring qname; + + DEBUG(3,("wins_process_name_query: name query for name %s from IP %s\n", + nmb_namestr(question), inet_ntoa(p->ip) )); + + /* + * Special name code. If the queried name is *<1b> then search + * the entire WINS database and return a list of all the IP addresses + * registered to any <1b> name. This is to allow domain master browsers + * to discover other domains that may not have a presence on their subnet. + */ + + pull_ascii_nstring(qname, sizeof(qname), question->name); + if(strequal( qname, "*") && (question->name_type == 0x1b)) { + process_wins_dmb_query_request( subrec, p); + return; + } + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if(namerec != NULL) { + /* + * If the name is not anymore in active state then reply not found. + * it's fair even if we keep it in the cache for days. + */ + if (!WINS_STATE_ACTIVE(namerec)) { + DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n", + nmb_namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + /* + * If it's a DNSFAIL_NAME then reply name not found. + */ + + if( namerec->data.source == DNSFAIL_NAME ) { + DEBUG(3,("wins_process_name_query: name query for name %s returning DNS fail.\n", + nmb_namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + /* + * If the name has expired then reply name not found. + */ + + if( (namerec->data.death_time != PERMANENT_TTL) && (namerec->data.death_time < p->timestamp) ) { + DEBUG(3,("wins_process_name_query: name query for name %s - name expired. Returning fail.\n", + nmb_namestr(question) )); + send_wins_name_query_response(NAM_ERR, p, namerec); + return; + } + + DEBUG(3,("wins_process_name_query: name query for name %s returning first IP %s.\n", + nmb_namestr(question), inet_ntoa(namerec->data.ip[0]) )); + + send_wins_name_query_response(0, p, namerec); + return; + } + + /* + * Name not found in WINS - try a dns query if it's a 0x20 name. + */ + + if(lp_dns_proxy() && ((question->name_type == 0x20) || question->name_type == 0)) { + DEBUG(3,("wins_process_name_query: name query for name %s not found - doing dns lookup.\n", + nmb_namestr(question) )); + + queue_dns_query(p, question, &namerec); + return; + } + + /* + * Name not found - return error. + */ + + send_wins_name_query_response(NAM_ERR, p, NULL); +} + +/**************************************************************************** +Send a WINS name release response. +**************************************************************************/ + +static void send_wins_name_release_response(int rcode, struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + char rdata[6]; + + memcpy(&rdata[0], &nmb->additional->rdata[0], 6); + + reply_netbios_packet(p, /* Packet to reply to. */ + rcode, /* Result code. */ + NMB_REL, /* nmbd type code. */ + NMB_NAME_RELEASE_OPCODE, /* opcode. */ + 0, /* ttl. */ + rdata, /* data to send. */ + 6); /* data length. */ +} + +/*********************************************************************** + Deal with a name release. +***********************************************************************/ + +void wins_process_name_release_request(struct subnet_record *subrec, + struct packet_struct *p) +{ + struct nmb_packet *nmb = &p->packet.nmb; + struct nmb_name *question = &nmb->question.question_name; + BOOL bcast = nmb->header.nm_flags.bcast; + uint16 nb_flags = get_nb_flags(nmb->additional->rdata); + struct name_record *namerec = NULL; + struct in_addr from_ip; + BOOL releasing_group_name = (nb_flags & NB_GROUP) ? True : False;; + + putip((char *)&from_ip,&nmb->additional->rdata[2]); + + if(bcast) { + /* + * We should only get unicast name registration packets here. + * Anyone trying to register broadcast should not be going to a WINS + * server. Log an error here. + */ + + DEBUG(0,("wins_process_name_release_request: broadcast name registration request \ +received for name %s from IP %s on subnet %s. Error - should not be sent to WINS server\n", + nmb_namestr(question), inet_ntoa(from_ip), subrec->subnet_name)); + return; + } + + DEBUG(3,("wins_process_name_release_request: %s name release for name %s \ +IP %s\n", releasing_group_name ? "Group" : "Unique", nmb_namestr(question), inet_ntoa(from_ip) )); + + /* + * Deal with policy regarding 0x1d names. + */ + + if(!releasing_group_name && (question->name_type == 0x1d)) { + DEBUG(3,("wins_process_name_release_request: Ignoring request \ +to release name %s from IP %s.", nmb_namestr(question), inet_ntoa(p->ip) )); + send_wins_name_release_response(0, p); + return; + } + + /* + * See if the name already exists. + */ + + namerec = find_name_on_subnet(subrec, question, FIND_ANY_NAME); + + if( (namerec == NULL) || ((namerec != NULL) && (namerec->data.source != REGISTER_NAME)) ) { + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Check that the sending machine has permission to release this name. + * If it's a group name not ending in 0x1c then just say yes and let + * the group time out. + */ + + if(releasing_group_name && (question->name_type != 0x1c)) { + send_wins_name_release_response(0, p); + return; + } + + /* + * Check that the releasing node is on the list of IP addresses + * for this name. Disallow the release if not. + */ + + if(!find_ip_in_name_record(namerec, from_ip)) { + DEBUG(3,("wins_process_name_release_request: Refusing request to \ +release name %s as IP %s is not one of the known IP's for this name.\n", + nmb_namestr(question), inet_ntoa(from_ip) )); + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Check if the record is active. IF it's already released + * or tombstoned, refuse the release. + */ + + if (!WINS_STATE_ACTIVE(namerec)) { + DEBUG(3,("wins_process_name_release_request: Refusing request to \ +release name %s as this record is not active anymore.\n", nmb_namestr(question) )); + send_wins_name_release_response(NAM_ERR, p); + return; + } + + /* + * Check if the record is a 0x1c group + * and has more then one ip + * remove only this address. + */ + + if(releasing_group_name && (question->name_type == 0x1c) && (namerec->data.num_ips > 1)) { + remove_ip_from_name_record(namerec, from_ip); + DEBUG(3,("wins_process_name_release_request: Remove IP %s from NAME: %s\n", + inet_ntoa(from_ip),nmb_namestr(question))); + send_wins_name_release_response(0, p); + return; + } + + /* + * Send a release response. + * Flag the name as released and update the ttl + */ + + send_wins_name_release_response(0, p); + + namerec->data.wins_flags |= WINS_RELEASED; + update_name_ttl(namerec, EXTINCTION_INTERVAL); + + wins_hook("delete", namerec, 0); +} + +/******************************************************************* + WINS time dependent processing. +******************************************************************/ + +void initiate_wins_processing(time_t t) +{ + static time_t lasttime = 0; + struct name_record *namerec; + struct name_record *next_namerec; + struct in_addr our_fake_ip; + + zero_ip( &our_fake_ip ); + + if (!lasttime) + lasttime = t; + if (t - lasttime < 20) + return; + + lasttime = t; + + if(!lp_we_are_a_wins_server()) + return; + + for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); + namerec; + namerec = next_namerec ) { + next_namerec = (struct name_record *)ubi_trNext( namerec ); + + if( (namerec->data.death_time != PERMANENT_TTL) + && (namerec->data.death_time < t) ) { + + if( namerec->data.source == SELF_NAME ) { + DEBUG( 3, ( "expire_names_on_subnet: Subnet %s not expiring SELF name %s\n", + wins_server_subnet->subnet_name, nmb_namestr(&namerec->name) ) ); + namerec->data.death_time += 300; + namerec->subnet->namelist_changed = True; + continue; + } + + /* handle records, samba is the wins owner */ + if (ip_equal(namerec->data.wins_ip, our_fake_ip)) { + switch (namerec->data.wins_flags | WINS_STATE_MASK) { + case WINS_ACTIVE: + namerec->data.wins_flags&=~WINS_STATE_MASK; + namerec->data.wins_flags|=WINS_RELEASED; + namerec->data.death_time = t + EXTINCTION_INTERVAL; + DEBUG(3,("initiate_wins_processing: expiring %s\n", nmb_namestr(&namerec->name))); + break; + case WINS_RELEASED: + namerec->data.wins_flags&=~WINS_STATE_MASK; + namerec->data.wins_flags|=WINS_TOMBSTONED; + namerec->data.death_time = t + EXTINCTION_TIMEOUT; + get_global_id_and_update(&namerec->data.id, True); + DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name))); + break; + case WINS_TOMBSTONED: + DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name))); + remove_name_from_namelist( wins_server_subnet, namerec ); + break; + } + } else { + switch (namerec->data.wins_flags | WINS_STATE_MASK) { + case WINS_ACTIVE: + /* that's not as MS says it should be */ + namerec->data.wins_flags&=~WINS_STATE_MASK; + namerec->data.wins_flags|=WINS_TOMBSTONED; + namerec->data.death_time = t + EXTINCTION_TIMEOUT; + DEBUG(3,("initiate_wins_processing: tombstoning %s\n", nmb_namestr(&namerec->name))); + case WINS_TOMBSTONED: + DEBUG(3,("initiate_wins_processing: deleting %s\n", nmb_namestr(&namerec->name))); + remove_name_from_namelist( wins_server_subnet, namerec ); + break; + case WINS_RELEASED: + DEBUG(0,("initiate_wins_processing: %s is in released state and\ +we are not the wins owner !\n", nmb_namestr(&namerec->name))); + break; + } + } + + } + } + + if(wins_server_subnet->namelist_changed) + wins_write_database(True); + + wins_server_subnet->namelist_changed = False; +} + +/******************************************************************* + Write out the current WINS database. +******************************************************************/ + +void wins_write_database(BOOL background) +{ + struct name_record *namerec; + pstring fname, fnamenew; + + XFILE *fp; + + if(!lp_we_are_a_wins_server()) + return; + + /* We will do the writing in a child process to ensure that the parent doesn't block while this is done */ + if (background) { + CatchChild(); + if (sys_fork()) { + return; + } + } + + slprintf(fname,sizeof(fname)-1,"%s/%s", lp_lockdir(), WINS_LIST); + all_string_sub(fname,"//", "/", 0); + slprintf(fnamenew,sizeof(fnamenew)-1,"%s.%u", fname, (unsigned int)sys_getpid()); + + if((fp = x_fopen(fnamenew,O_WRONLY|O_CREAT,0644)) == NULL) { + DEBUG(0,("wins_write_database: Can't open %s. Error was %s\n", fnamenew, strerror(errno))); + if (background) { + _exit(0); + } + return; + } + + DEBUG(4,("wins_write_database: Dump of WINS name list.\n")); + + x_fprintf(fp,"VERSION %d %u\n", WINS_VERSION, 0); + + for( namerec = (struct name_record *)ubi_trFirst( wins_server_subnet->namelist ); namerec; namerec = (struct name_record *)ubi_trNext( namerec ) ) { + int i; + struct tm *tm; + + DEBUGADD(4,("%-19s ", nmb_namestr(&namerec->name) )); + + if( namerec->data.death_time != PERMANENT_TTL ) { + char *ts, *nl; + + tm = LocalTime(&namerec->data.death_time); + ts = asctime(tm); + nl = strrchr( ts, '\n' ); + if( NULL != nl ) + *nl = '\0'; + DEBUGADD(4,("TTL = %s ", ts )); + } else { + DEBUGADD(4,("TTL = PERMANENT ")); + } + + for (i = 0; i < namerec->data.num_ips; i++) + DEBUGADD(4,("%15s ", inet_ntoa(namerec->data.ip[i]) )); + DEBUGADD(4,("%2x\n", namerec->data.nb_flags )); + + if( namerec->data.source == REGISTER_NAME ) { + unstring name; + pull_ascii_nstring(name, sizeof(name), namerec->name.name); + x_fprintf(fp, "\"%s#%02x\" %d ", name,namerec->name.name_type, /* Ignore scope. */ + (int)namerec->data.death_time); + + for (i = 0; i < namerec->data.num_ips; i++) + x_fprintf( fp, "%s ", inet_ntoa( namerec->data.ip[i] ) ); + x_fprintf( fp, "%2xR\n", namerec->data.nb_flags ); + } + } + + x_fclose(fp); + chmod(fnamenew,0644); + unlink(fname); + rename(fnamenew,fname); + if (background) { + _exit(0); + } +} + +/**************************************************************************** + Process a internal Samba message receiving a wins record. +***************************************************************************/ + +void nmbd_wins_new_entry(int msg_type, pid_t src, void *buf, size_t len) +{ + WINS_RECORD *record; + struct name_record *namerec = NULL; + struct name_record *new_namerec = NULL; + struct nmb_name question; + BOOL overwrite=False; + struct in_addr our_fake_ip; + int i; + + zero_ip( &our_fake_ip ); + + if (buf==NULL) + return; + + /* Record should use UNIX codepage. Ensure this is so in the wrepld code. JRA. */ + record=(WINS_RECORD *)buf; + + make_nmb_name(&question, record->name, record->type); + + namerec = find_name_on_subnet(wins_server_subnet, &question, FIND_ANY_NAME); + + /* record doesn't exist, add it */ + if (namerec == NULL) { + DEBUG(3,("nmbd_wins_new_entry: adding new replicated record: %s<%02x> for wins server: %s\n", + record->name, record->type, inet_ntoa(record->wins_ip))); + + new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags, + EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip); + if (new_namerec!=NULL) { + update_wins_owner(new_namerec, record->wins_ip); + update_wins_flag(new_namerec, record->wins_flags); + new_namerec->data.id=record->id; + + wins_server_subnet->namelist_changed = True; + } + } + + /* check if we have a conflict */ + if (namerec != NULL) { + /* both records are UNIQUE */ + if (namerec->data.wins_flags&WINS_UNIQUE && record->wins_flags&WINS_UNIQUE) { + + /* the database record is a replica */ + if (!ip_equal(namerec->data.wins_ip, our_fake_ip)) { + if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) { + if (ip_equal(namerec->data.wins_ip, record->wins_ip)) + overwrite=True; + } else + overwrite=True; + } else { + /* we are the wins owner of the database record */ + /* the 2 records have the same IP address */ + if (ip_equal(namerec->data.ip[0], record->ip[0])) { + if (namerec->data.wins_flags&WINS_ACTIVE && record->wins_flags&WINS_TOMBSTONED) + get_global_id_and_update(&namerec->data.id, True); + else + overwrite=True; + + } else { + /* the 2 records have different IP address */ + if (namerec->data.wins_flags&WINS_ACTIVE) { + if (record->wins_flags&WINS_TOMBSTONED) + get_global_id_and_update(&namerec->data.id, True); + if (record->wins_flags&WINS_ACTIVE) + /* send conflict challenge to the replica node */ + ; + } else + overwrite=True; + } + + } + } + + /* the replica is a standard group */ + if (record->wins_flags&WINS_NGROUP || record->wins_flags&WINS_SGROUP) { + /* if the database record is unique and active force a name release */ + if (namerec->data.wins_flags&WINS_UNIQUE) + /* send a release name to the unique node */ + ; + overwrite=True; + + } + + /* the replica is a special group */ + if (record->wins_flags&WINS_SGROUP && namerec->data.wins_flags&WINS_SGROUP) { + if (namerec->data.wins_flags&WINS_ACTIVE) { + for (i=0; inum_ips; i++) + if(!find_ip_in_name_record(namerec, record->ip[i])) + add_ip_to_name_record(namerec, record->ip[i]); + } else { + overwrite=True; + } + } + + /* the replica is a multihomed host */ + + /* I'm giving up on multi homed. Too much complex to understand */ + + if (record->wins_flags&WINS_MHOMED) { + if (! (namerec->data.wins_flags&WINS_ACTIVE)) { + if ( !(namerec->data.wins_flags&WINS_RELEASED) && !(namerec->data.wins_flags&WINS_NGROUP)) + overwrite=True; + } + else { + if (ip_equal(record->wins_ip, namerec->data.wins_ip)) + overwrite=True; + + if (ip_equal(namerec->data.wins_ip, our_fake_ip)) + if (namerec->data.wins_flags&WINS_UNIQUE) + get_global_id_and_update(&namerec->data.id, True); + + } + + if (record->wins_flags&WINS_ACTIVE && namerec->data.wins_flags&WINS_ACTIVE) + if (namerec->data.wins_flags&WINS_UNIQUE || + namerec->data.wins_flags&WINS_MHOMED) + if (ip_equal(record->wins_ip, namerec->data.wins_ip)) + overwrite=True; + + } + + if (overwrite == False) + DEBUG(3, ("nmbd_wins_new_entry: conflict in adding record: %s<%02x> from wins server: %s\n", + record->name, record->type, inet_ntoa(record->wins_ip))); + else { + DEBUG(3, ("nmbd_wins_new_entry: replacing record: %s<%02x> from wins server: %s\n", + record->name, record->type, inet_ntoa(record->wins_ip))); + + /* remove the old record and add a new one */ + remove_name_from_namelist( wins_server_subnet, namerec ); + new_namerec=add_name_to_subnet( wins_server_subnet, record->name, record->type, record->nb_flags, + EXTINCTION_INTERVAL, REGISTER_NAME, record->num_ips, record->ip); + if (new_namerec!=NULL) { + update_wins_owner(new_namerec, record->wins_ip); + update_wins_flag(new_namerec, record->wins_flags); + new_namerec->data.id=record->id; + + wins_server_subnet->namelist_changed = True; + } + + wins_server_subnet->namelist_changed = True; + } + + } +} diff --git a/SAMBA_3_2_MERGE/source/param/loadparm.c b/SAMBA_3_2_MERGE/source/param/loadparm.c index 9e6cdf0065b..7b4680d9ab8 100644 --- a/SAMBA_3_2_MERGE/source/param/loadparm.c +++ b/SAMBA_3_2_MERGE/source/param/loadparm.c @@ -4273,6 +4273,17 @@ void get_private_directory(pstring privdir) pstrcpy (privdir, lp_private_dir()); } +/*********************************************************** + Allow daemons such as winbindd to fix their logfile name. +************************************************************/ + +void lp_set_logfile(const char *name) +{ + string_set(&Globals.szLogFile, name); + pstrcpy(debugf, name); +} + + /******************************************************************* Return the NetBIOS called name, or my IP - but never global_myname(). ********************************************************************/ diff --git a/SAMBA_3_2_MERGE/source/smbd/chgpasswd.c b/SAMBA_3_2_MERGE/source/smbd/chgpasswd.c new file mode 100644 index 00000000000..db091d03be8 --- /dev/null +++ b/SAMBA_3_2_MERGE/source/smbd/chgpasswd.c @@ -0,0 +1,1007 @@ +/* + Unix SMB/CIFS implementation. + Samba utility functions + Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Andrew Bartlett 2001-2004 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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. +*/ + +/* These comments regard the code to change the user's unix password: */ + +/* fork a child process to exec passwd and write to its + * tty to change a users password. This is running as the + * user who is attempting to change the password. + */ + +/* + * This code was copied/borrowed and stolen from various sources. + * The primary source was the poppasswd.c from the authors of POPMail. This software + * was included as a client to change passwords using the 'passwd' program + * on the remote machine. + * + * This code has been hacked by Bob Nance (nance@niehs.nih.gov) and Evan Patterson + * (patters2@niehs.nih.gov) at the National Institute of Environmental Health Sciences + * and rights to modify, distribute or incorporate this change to the CAP suite or + * using it for any other reason are granted, so long as this disclaimer is left intact. + */ + +/* + This code was hacked considerably for inclusion in Samba, primarily + by Andrew.Tridgell@anu.edu.au. The biggest change was the addition + of the "password chat" option, which allows the easy runtime + specification of the expected sequence of events to change a + password. + */ + +#include "includes.h" + +extern struct passdb_ops pdb_ops; + +static NTSTATUS check_oem_password(const char *user, + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16], + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size); + +#if ALLOW_CHANGE_PASSWORD + +static int findpty(char **slave) +{ + int master; + static fstring line; + DIR *dirp; + const char *dpname; + +#if defined(HAVE_GRANTPT) + /* Try to open /dev/ptmx. If that fails, fall through to old method. */ + if ((master = sys_open("/dev/ptmx", O_RDWR, 0)) >= 0) + { + grantpt(master); + unlockpt(master); + *slave = (char *)ptsname(master); + if (*slave == NULL) + { + DEBUG(0, + ("findpty: Unable to create master/slave pty pair.\n")); + /* Stop fd leak on error. */ + close(master); + return -1; + } + else + { + DEBUG(10, + ("findpty: Allocated slave pty %s\n", *slave)); + return (master); + } + } +#endif /* HAVE_GRANTPT */ + + fstrcpy(line, "/dev/ptyXX"); + + dirp = opendir("/dev"); + if (!dirp) + return (-1); + while ((dpname = readdirname(dirp)) != NULL) + { + if (strncmp(dpname, "pty", 3) == 0 && strlen(dpname) == 5) + { + DEBUG(3, + ("pty: try to open %s, line was %s\n", dpname, + line)); + line[8] = dpname[3]; + line[9] = dpname[4]; + if ((master = sys_open(line, O_RDWR, 0)) >= 0) + { + DEBUG(3, ("pty: opened %s\n", line)); + line[5] = 't'; + *slave = line; + closedir(dirp); + return (master); + } + } + } + closedir(dirp); + return (-1); +} + +static int dochild(int master, const char *slavedev, const struct passwd *pass, + const char *passwordprogram, BOOL as_root) +{ + int slave; + struct termios stermios; + gid_t gid; + uid_t uid; + + if (pass == NULL) + { + DEBUG(0, + ("dochild: user doesn't exist in the UNIX password database.\n")); + return False; + } + + gid = pass->pw_gid; + uid = pass->pw_uid; + + gain_root_privilege(); + + /* Start new session - gets rid of controlling terminal. */ + if (setsid() < 0) + { + DEBUG(3, + ("Weirdness, couldn't let go of controlling terminal\n")); + return (False); + } + + /* Open slave pty and acquire as new controlling terminal. */ + if ((slave = sys_open(slavedev, O_RDWR, 0)) < 0) + { + DEBUG(3, ("More weirdness, could not open %s\n", slavedev)); + return (False); + } +#ifdef I_PUSH + ioctl(slave, I_PUSH, "ptem"); + ioctl(slave, I_PUSH, "ldterm"); +#elif defined(TIOCSCTTY) + if (ioctl(slave, TIOCSCTTY, 0) < 0) + { + DEBUG(3, ("Error in ioctl call for slave pty\n")); + /* return(False); */ + } +#endif + + /* Close master. */ + close(master); + + /* Make slave stdin/out/err of child. */ + + if (sys_dup2(slave, STDIN_FILENO) != STDIN_FILENO) + { + DEBUG(3, ("Could not re-direct stdin\n")); + return (False); + } + if (sys_dup2(slave, STDOUT_FILENO) != STDOUT_FILENO) + { + DEBUG(3, ("Could not re-direct stdout\n")); + return (False); + } + if (sys_dup2(slave, STDERR_FILENO) != STDERR_FILENO) + { + DEBUG(3, ("Could not re-direct stderr\n")); + return (False); + } + if (slave > 2) + close(slave); + + /* Set proper terminal attributes - no echo, canonical input processing, + no map NL to CR/NL on output. */ + + if (tcgetattr(0, &stermios) < 0) + { + DEBUG(3, + ("could not read default terminal attributes on pty\n")); + return (False); + } + stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + stermios.c_lflag |= ICANON; +#ifdef ONLCR + stermios.c_oflag &= ~(ONLCR); +#endif + if (tcsetattr(0, TCSANOW, &stermios) < 0) + { + DEBUG(3, ("could not set attributes of pty\n")); + return (False); + } + + /* make us completely into the right uid */ + if (!as_root) + { + become_user_permanently(uid, gid); + } + + DEBUG(10, + ("Invoking '%s' as password change program.\n", + passwordprogram)); + + /* execl() password-change application */ + if (execl("/bin/sh", "sh", "-c", passwordprogram, NULL) < 0) + { + DEBUG(3, ("Bad status returned from %s\n", passwordprogram)); + return (False); + } + return (True); +} + +static int expect(int master, char *issue, char *expected) +{ + pstring buffer; + int attempts, timeout, nread, len; + BOOL match = False; + + for (attempts = 0; attempts < 2; attempts++) { + if (!strequal(issue, ".")) { + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: sending [%s]\n", issue)); + + if ((len = write(master, issue, strlen(issue))) != strlen(issue)) { + DEBUG(2,("expect: (short) write returned %d\n", len )); + return False; + } + } + + if (strequal(expected, ".")) + return True; + + /* Initial timeout. */ + timeout = lp_passwd_chat_timeout() * 1000; + nread = 0; + buffer[nread] = 0; + + while ((len = read_socket_with_timeout(master, buffer + nread, 1, + sizeof(buffer) - nread - 1, + timeout)) > 0) { + nread += len; + buffer[nread] = 0; + + { + /* Eat leading/trailing whitespace before match. */ + pstring str; + pstrcpy( str, buffer); + trim_char( str, ' ', ' '); + + if ((match = (unix_wild_match(expected, str) == 0))) { + /* Now data has started to return, lower timeout. */ + timeout = lp_passwd_chat_timeout() * 100; + } + } + } + + if (lp_passwd_chat_debug()) + DEBUG(100, ("expect: expected [%s] received [%s] match %s\n", + expected, buffer, match ? "yes" : "no" )); + + if (match) + break; + + if (len < 0) { + DEBUG(2, ("expect: %s\n", strerror(errno))); + return False; + } + } + + DEBUG(10,("expect: returning %s\n", match ? "True" : "False" )); + return match; +} + +static void pwd_sub(char *buf) +{ + all_string_sub(buf, "\\n", "\n", 0); + all_string_sub(buf, "\\r", "\r", 0); + all_string_sub(buf, "\\s", " ", 0); + all_string_sub(buf, "\\t", "\t", 0); +} + +static int talktochild(int master, const char *seq) +{ + int count = 0; + fstring issue, expected; + + fstrcpy(issue, "."); + + while (next_token(&seq, expected, NULL, sizeof(expected))) + { + pwd_sub(expected); + count++; + + if (!expect(master, issue, expected)) + { + DEBUG(3, ("Response %d incorrect\n", count)); + return False; + } + + if (!next_token(&seq, issue, NULL, sizeof(issue))) + fstrcpy(issue, "."); + + pwd_sub(issue); + } + if (!strequal(issue, ".")) { + /* we have one final issue to send */ + fstrcpy(expected, "."); + if (!expect(master, issue, expected)) + return False; + } + + return (count > 0); +} + +static BOOL chat_with_program(char *passwordprogram, struct passwd *pass, + char *chatsequence, BOOL as_root) +{ + char *slavedev; + int master; + pid_t pid, wpid; + int wstat; + BOOL chstat = False; + + if (pass == NULL) { + DEBUG(0, ("chat_with_program: user doesn't exist in the UNIX password database.\n")); + return False; + } + + /* allocate a pseudo-terminal device */ + if ((master = findpty(&slavedev)) < 0) { + DEBUG(3, ("chat_with_program: Cannot Allocate pty for password change: %s\n", pass->pw_name)); + return (False); + } + + /* + * We need to temporarily stop CatchChild from eating + * SIGCLD signals as it also eats the exit status code. JRA. + */ + + CatchChildLeaveStatus(); + + if ((pid = sys_fork()) < 0) { + DEBUG(3, ("chat_with_program: Cannot fork() child for password change: %s\n", pass->pw_name)); + close(master); + CatchChild(); + return (False); + } + + /* we now have a pty */ + if (pid > 0) { /* This is the parent process */ + if ((chstat = talktochild(master, chatsequence)) == False) { + DEBUG(3, ("chat_with_program: Child failed to change password: %s\n", pass->pw_name)); + kill(pid, SIGKILL); /* be sure to end this process */ + } + + while ((wpid = sys_waitpid(pid, &wstat, 0)) < 0) { + if (errno == EINTR) { + errno = 0; + continue; + } + break; + } + + if (wpid < 0) { + DEBUG(3, ("chat_with_program: The process is no longer waiting!\n\n")); + close(master); + CatchChild(); + return (False); + } + + /* + * Go back to ignoring children. + */ + CatchChild(); + + close(master); + + if (pid != wpid) { + DEBUG(3, ("chat_with_program: We were waiting for the wrong process ID\n")); + return (False); + } + if (WIFEXITED(wstat) && (WEXITSTATUS(wstat) != 0)) { + DEBUG(3, ("chat_with_program: The process exited with status %d \ +while we were waiting\n", WEXITSTATUS(wstat))); + return (False); + } +#if defined(WIFSIGNALLED) && defined(WTERMSIG) + else if (WIFSIGNALLED(wstat)) { + DEBUG(3, ("chat_with_program: The process was killed by signal %d \ +while we were waiting\n", WTERMSIG(wstat))); + return (False); + } +#endif + } else { + /* CHILD */ + + /* + * Lose any oplock capabilities. + */ + oplock_set_capability(False, False); + + /* make sure it doesn't freeze */ + alarm(20); + + if (as_root) + become_root(); + + DEBUG(3, ("chat_with_program: Dochild for user %s (uid=%d,gid=%d) (as_root = %s)\n", pass->pw_name, + (int)getuid(), (int)getgid(), BOOLSTR(as_root) )); + chstat = dochild(master, slavedev, pass, passwordprogram, as_root); + + if (as_root) + unbecome_root(); + + /* + * The child should never return from dochild() .... + */ + + DEBUG(0, ("chat_with_program: Error: dochild() returned %d\n", chstat)); + exit(1); + } + + if (chstat) + DEBUG(3, ("chat_with_program: Password change %ssuccessful for user %s\n", + (chstat ? "" : "un"), pass->pw_name)); + return (chstat); +} + +BOOL chgpasswd(const char *name, const struct passwd *pass, + const char *oldpass, const char *newpass, BOOL as_root) +{ + pstring passwordprogram; + pstring chatsequence; + size_t i; + size_t len; + + if (!oldpass) { + oldpass = ""; + } + + DEBUG(3, ("chgpasswd: Password change (as_root=%s) for user: %s\n", BOOLSTR(as_root), name)); + +#if DEBUG_PASSWORD + DEBUG(100, ("chgpasswd: Passwords: old=%s new=%s\n", oldpass, newpass)); +#endif + + /* Take the passed information and test it for minimum criteria */ + + /* Password is same as old password */ + if (strcmp(oldpass, newpass) == 0) { + /* don't allow same password */ + DEBUG(2, ("chgpasswd: Password Change: %s, New password is same as old\n", name)); /* log the attempt */ + return (False); /* inform the user */ + } + + /* + * Check the old and new passwords don't contain any control + * characters. + */ + + len = strlen(oldpass); + for (i = 0; i < len; i++) { + if (iscntrl((int)oldpass[i])) { + DEBUG(0, ("chgpasswd: oldpass contains control characters (disallowed).\n")); + return False; + } + } + + len = strlen(newpass); + for (i = 0; i < len; i++) { + if (iscntrl((int)newpass[i])) { + DEBUG(0, ("chgpasswd: newpass contains control characters (disallowed).\n")); + return False; + } + } + +#ifdef WITH_PAM + if (lp_pam_password_change()) { + BOOL ret; + + if (as_root) + become_root(); + + if (pass) { + ret = smb_pam_passchange(pass->pw_name, oldpass, newpass); + } else { + ret = smb_pam_passchange(name, oldpass, newpass); + } + + if (as_root) + unbecome_root(); + + return ret; + } +#endif + + /* A non-PAM password change just doen't make sense without a valid local user */ + + if (pass == NULL) { + DEBUG(0, ("chgpasswd: user %s doesn't exist in the UNIX password database.\n", name)); + return False; + } + + pstrcpy(passwordprogram, lp_passwd_program()); + pstrcpy(chatsequence, lp_passwd_chat()); + + if (!*chatsequence) { + DEBUG(2, ("chgpasswd: Null chat sequence - no password changing\n")); + return (False); + } + + if (!*passwordprogram) { + DEBUG(2, ("chgpasswd: Null password program - no password changing\n")); + return (False); + } + + if (as_root) { + /* The password program *must* contain the user name to work. Fail if not. */ + if (strstr_m(passwordprogram, "%u") == NULL) { + DEBUG(0,("chgpasswd: Running as root the 'passwd program' parameter *MUST* contain \ +the string %%u, and the given string %s does not.\n", passwordprogram )); + return False; + } + } + + pstring_sub(passwordprogram, "%u", name); + /* note that we do NOT substitute the %o and %n in the password program + as this would open up a security hole where the user could use + a new password containing shell escape characters */ + + pstring_sub(chatsequence, "%u", name); + all_string_sub(chatsequence, "%o", oldpass, sizeof(pstring)); + all_string_sub(chatsequence, "%n", newpass, sizeof(pstring)); + return (chat_with_program + (passwordprogram, pass, chatsequence, as_root)); +} + +#else /* ALLOW_CHANGE_PASSWORD */ + +BOOL chgpasswd(const char *name, const struct passwd *pass, + const char *oldpass, const char *newpass, BOOL as_root) +{ + DEBUG(0, ("chgpasswd: Unix Password changing not compiled in (user=%s)\n", name)); + return (False); +} +#endif /* ALLOW_CHANGE_PASSWORD */ + +/*********************************************************** + Code to check the lanman hashed password. +************************************************************/ + +BOOL check_lanman_password(char *user, uchar * pass1, + uchar * pass2, SAM_ACCOUNT **hnd) +{ + uchar unenc_new_pw[16]; + uchar unenc_old_pw[16]; + SAM_ACCOUNT *sampass = NULL; + uint16 acct_ctrl; + const uint8 *lanman_pw; + BOOL ret; + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if (ret == False) { + DEBUG(0,("check_lanman_password: getsampwnam returned NULL\n")); + pdb_free_sam(&sampass); + return False; + } + + acct_ctrl = pdb_get_acct_ctrl (sampass); + lanman_pw = pdb_get_lanman_passwd (sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(0,("check_lanman_password: account %s disabled.\n", user)); + pdb_free_sam(&sampass); + return False; + } + + if (lanman_pw == NULL) { + if (acct_ctrl & ACB_PWNOTREQ) { + /* this saves the pointer for the caller */ + *hnd = sampass; + return True; + } else { + DEBUG(0, ("check_lanman_password: no lanman password !\n")); + pdb_free_sam(&sampass); + return False; + } + } + + /* Get the new lanman hash. */ + D_P16(lanman_pw, pass2, unenc_new_pw); + + /* Use this to get the old lanman hash. */ + D_P16(unenc_new_pw, pass1, unenc_old_pw); + + /* Check that the two old passwords match. */ + if (memcmp(lanman_pw, unenc_old_pw, 16)) { + DEBUG(0,("check_lanman_password: old password doesn't match.\n")); + pdb_free_sam(&sampass); + return False; + } + + /* this saves the pointer for the caller */ + *hnd = sampass; + return True; +} + +/*********************************************************** + Code to change the lanman hashed password. + It nulls out the NT hashed password as it will + no longer be valid. + NOTE this function is designed to be called as root. Check the old password + is correct before calling. JRA. +************************************************************/ + +BOOL change_lanman_password(SAM_ACCOUNT *sampass, uchar *pass2) +{ + static uchar null_pw[16]; + uchar unenc_new_pw[16]; + BOOL ret; + uint16 acct_ctrl; + const uint8 *pwd; + + if (sampass == NULL) { + DEBUG(0,("change_lanman_password: no smb password entry.\n")); + return False; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + pwd = pdb_get_lanman_passwd(sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(0,("change_lanman_password: account %s disabled.\n", + pdb_get_username(sampass))); + return False; + } + + if (pwd == NULL) { + if (acct_ctrl & ACB_PWNOTREQ) { + uchar no_pw[14]; + memset(no_pw, '\0', 14); + E_P16(no_pw, null_pw); + + /* Get the new lanman hash. */ + D_P16(null_pw, pass2, unenc_new_pw); + } else { + DEBUG(0,("change_lanman_password: no lanman password !\n")); + return False; + } + } else { + /* Get the new lanman hash. */ + D_P16(pwd, pass2, unenc_new_pw); + } + + if (!pdb_set_lanman_passwd(sampass, unenc_new_pw, PDB_CHANGED)) { + return False; + } + + if (!pdb_set_nt_passwd (sampass, NULL, PDB_CHANGED)) { + return False; /* We lose the NT hash. Sorry. */ + } + + if (!pdb_set_pass_changed_now (sampass)) { + pdb_free_sam(&sampass); + /* Not quite sure what this one qualifies as, but this will do */ + return False; + } + + /* Now flush the sam_passwd struct to persistent storage */ + ret = pdb_update_sam_account (sampass); + + return ret; +} + +/*********************************************************** + Code to check and change the OEM hashed password. +************************************************************/ + +NTSTATUS pass_oem_change(char *user, + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16]) +{ + pstring new_passwd; + SAM_ACCOUNT *sampass = NULL; + NTSTATUS nt_status = check_oem_password(user, password_encrypted_with_lm_hash, + old_lm_hash_encrypted, + password_encrypted_with_nt_hash, + old_nt_hash_encrypted, + &sampass, new_passwd, sizeof(new_passwd)); + + if (!NT_STATUS_IS_OK(nt_status)) + return nt_status; + + /* We've already checked the old password here.... */ + become_root(); + nt_status = change_oem_password(sampass, NULL, new_passwd, True); + unbecome_root(); + + memset(new_passwd, 0, sizeof(new_passwd)); + + pdb_free_sam(&sampass); + + return nt_status; +} + +/*********************************************************** + Decrypt and verify a user password change. + + The 516 byte long buffers are encrypted with the old NT and + old LM passwords, and if the NT passwords are present, both + buffers contain a unicode string. + + After decrypting the buffers, check the password is correct by + matching the old hashed passwords with the passwords in the passdb. + +************************************************************/ + +static NTSTATUS check_oem_password(const char *user, + uchar password_encrypted_with_lm_hash[516], + const uchar old_lm_hash_encrypted[16], + uchar password_encrypted_with_nt_hash[516], + const uchar old_nt_hash_encrypted[16], + SAM_ACCOUNT **hnd, char *new_passwd, + int new_passwd_size) +{ + static uchar null_pw[16]; + static uchar null_ntpw[16]; + SAM_ACCOUNT *sampass = NULL; + char *password_encrypted; + const char *encryption_key; + const uint8 *lanman_pw, *nt_pw; + uint16 acct_ctrl; + uint32 new_pw_len; + uchar new_nt_hash[16]; + uchar old_nt_hash_plain[16]; + uchar new_lm_hash[16]; + uchar old_lm_hash_plain[16]; + char no_pw[2]; + BOOL ret; + + BOOL nt_pass_set = (password_encrypted_with_nt_hash && old_nt_hash_encrypted); + BOOL lm_pass_set = (password_encrypted_with_lm_hash && old_lm_hash_encrypted); + + *hnd = NULL; + + pdb_init_sam(&sampass); + + become_root(); + ret = pdb_getsampwnam(sampass, user); + unbecome_root(); + + if (ret == False) { + DEBUG(0, ("check_oem_password: getsmbpwnam returned NULL\n")); + pdb_free_sam(&sampass); + return NT_STATUS_NO_SUCH_USER; + } + + acct_ctrl = pdb_get_acct_ctrl(sampass); + + if (acct_ctrl & ACB_DISABLED) { + DEBUG(2,("check_lanman_password: account %s disabled.\n", user)); + pdb_free_sam(&sampass); + return NT_STATUS_ACCOUNT_DISABLED; + } + + if (acct_ctrl & ACB_PWNOTREQ && lp_null_passwords()) { + /* construct a null password (in case one is needed */ + no_pw[0] = 0; + no_pw[1] = 0; + nt_lm_owf_gen(no_pw, null_ntpw, null_pw); + lanman_pw = null_pw; + nt_pw = null_pw; + + } else { + /* save pointers to passwords so we don't have to keep looking them up */ + if (lp_lanman_auth()) { + lanman_pw = pdb_get_lanman_passwd(sampass); + } else { + lanman_pw = NULL; + } + nt_pw = pdb_get_nt_passwd(sampass); + } + + if (nt_pw && nt_pass_set) { + /* IDEAL Case: passwords are in unicode, and we can + * read use the password encrypted with the NT hash + */ + password_encrypted = password_encrypted_with_nt_hash; + encryption_key = nt_pw; + } else if (lanman_pw && lm_pass_set) { + /* password may still be in unicode, but use LM hash version */ + password_encrypted = password_encrypted_with_lm_hash; + encryption_key = lanman_pw; + } else if (nt_pass_set) { + DEBUG(1, ("NT password change supplied for user %s, but we have no NT password to check it with\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } else if (lm_pass_set) { + DEBUG(1, ("LM password change supplied for user %s, but we have no LanMan password to check it with\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } else { + DEBUG(1, ("password change requested for user %s, but no password supplied!\n", + user)); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * Decrypt the password with the key + */ + SamOEMhash( password_encrypted, encryption_key, 516); + + if ( !decode_pw_buffer(password_encrypted, new_passwd, new_passwd_size, &new_pw_len, + nt_pass_set ? STR_UNICODE : STR_ASCII)) { + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } + + /* + * To ensure we got the correct new password, hash it and + * use it as a key to test the passed old password. + */ + + if (nt_pass_set) { + /* NT passwords, verify the NT hash. */ + + /* Calculate the MD4 hash (NT compatible) of the password */ + memset(new_nt_hash, '\0', 16); + E_md4hash(new_passwd, new_nt_hash); + + if (nt_pw) { + /* + * Now use new_nt_hash as the key to see if the old + * password matches. + */ + D_P16(new_nt_hash, old_nt_hash_encrypted, old_nt_hash_plain); + + if (memcmp(nt_pw, old_nt_hash_plain, 16)) { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } + + /* We could check the LM password here, but there is + * little point, we already know the password is + * correct, and the LM password might not even be + * present. */ + + /* Further, LM hash generation algorithms + * differ with charset, so we could + * incorrectly fail a perfectly valid password + * change */ +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + *hnd = sampass; + return NT_STATUS_OK; + } + + if (lanman_pw) { + /* + * Now use new_nt_hash as the key to see if the old + * LM password matches. + */ + D_P16(new_nt_hash, old_lm_hash_encrypted, old_lm_hash_plain); + + if (memcmp(lanman_pw, old_lm_hash_plain, 16)) { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + *hnd = sampass; + return NT_STATUS_OK; + } + } + + if (lanman_pw && lm_pass_set) { + + E_deshash(new_passwd, new_lm_hash); + + /* + * Now use new_lm_hash as the key to see if the old + * password matches. + */ + D_P16(new_lm_hash, old_lm_hash_encrypted, old_lm_hash_plain); + + if (memcmp(lanman_pw, old_lm_hash_plain, 16)) { + DEBUG(0,("check_oem_password: old lm password doesn't match.\n")); + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; + } + +#ifdef DEBUG_PASSWORD + DEBUG(100, + ("check_oem_password: password %s ok\n", new_passwd)); +#endif + *hnd = sampass; + return NT_STATUS_OK; + } + + /* should not be reached */ + pdb_free_sam(&sampass); + return NT_STATUS_WRONG_PASSWORD; +} + +/*********************************************************** + Code to change the oem password. Changes both the lanman + and NT hashes. Old_passwd is almost always NULL. + NOTE this function is designed to be called as root. Check the old password + is correct before calling. JRA. +************************************************************/ + +NTSTATUS change_oem_password(SAM_ACCOUNT *hnd, char *old_passwd, char *new_passwd, BOOL as_root) +{ + struct passwd *pass; + + BOOL ret; + uint32 min_len; + + if (time(NULL) < pdb_get_pass_can_change_time(hnd)) { + DEBUG(1, ("user %s cannot change password now, must wait until %s\n", + pdb_get_username(hnd), http_timestring(hnd->mem_ctx, pdb_get_pass_can_change_time(hnd)))); + return NT_STATUS_PASSWORD_RESTRICTION; + } + + if (account_policy_get(AP_MIN_PASSWORD_LEN, &min_len) && (strlen(new_passwd) < min_len)) { + DEBUG(1, ("user %s cannot change password - password too short\n", + pdb_get_username(hnd))); + DEBUGADD(1, (" account policy min password len = %d\n", min_len)); + return NT_STATUS_PASSWORD_RESTRICTION; +/* return NT_STATUS_PWD_TOO_SHORT; */ + } + + /* Take the passed information and test it for minimum criteria */ + /* Minimum password length */ + if (strlen(new_passwd) < lp_min_passwd_length()) { + /* too short, must be at least MINPASSWDLENGTH */ + DEBUG(1, ("Password Change: user %s, New password is shorter than minimum password length = %d\n", + pdb_get_username(hnd), lp_min_passwd_length())); + return NT_STATUS_PASSWORD_RESTRICTION; +/* return NT_STATUS_PWD_TOO_SHORT; */ + } + + pass = Get_Pwnam(pdb_get_username(hnd)); + if (!pass) { + DEBUG(1, ("check_oem_password: Username does not exist in system !?!\n")); + } + + /* + * If unix password sync was requested, attempt to change + * the /etc/passwd database first. Return failure if this cannot + * be done. + * + * This occurs before the oem change, because we don't want to + * update it if chgpasswd failed. + * + * Conditional on lp_unix_password_sync() because we don't want + * to touch the unix db unless we have admin permission. + */ + + if(lp_unix_password_sync() && + !chgpasswd(pdb_get_username(hnd), pass, old_passwd, new_passwd, as_root)) { + return NT_STATUS_ACCESS_DENIED; + } + + if (!pdb_set_plaintext_passwd (hnd, new_passwd)) { + return NT_STATUS_ACCESS_DENIED; + } + + /* Now write it into the file. */ + ret = pdb_update_sam_account (hnd); + + if (!ret) { + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} diff --git a/SAMBA_3_2_MERGE/source/smbd/session.c b/SAMBA_3_2_MERGE/source/smbd/session.c index 91ebaeb830b..d7a32692d36 100644 --- a/SAMBA_3_2_MERGE/source/smbd/session.c +++ b/SAMBA_3_2_MERGE/source/smbd/session.c @@ -183,7 +183,7 @@ void session_yield(user_struct *vuser) memcpy(&sessionid, dbuf.dptr, sizeof(sessionid)); - client_ip = interpret_addr2(sessionid.ip_addr); + client_ip = interpret_addr2_x(sessionid.ip_addr); SAFE_FREE(dbuf.dptr); diff --git a/SAMBA_3_2_MERGE/source/utils/testparm.c b/SAMBA_3_2_MERGE/source/utils/testparm.c new file mode 100644 index 00000000000..8d0c457e471 --- /dev/null +++ b/SAMBA_3_2_MERGE/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 = ' \ +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...] [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); +} -- cgit