#!/usr/bin/env python # vim: expandtab ft=python # # 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 # Updated for Samba4 by Andrew Bartlett 2006 # Converted to Python by Jelmer Vernooij 2010 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # 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. # import logging import optparse import os import sys # Find right directory when running from source tree sys.path.insert(0, "bin/python") import samba from samba import getopt as options # Here we do a set of 'hard coded' checks for bad # configuration settings. def do_global_checks(lp, logger): valid = True netbios_name = lp.get("netbios name") if not samba.valid_netbios_name(netbios_name): logger.error("netbios name %s is not a valid netbios name", netbios_name) valid = False workgroup = lp.get("workgroup") if not samba.valid_netbios_name(workgroup): logger.error("workgroup name %s is not a valid netbios name", workgroup) valid = False lockdir = lp.get("lockdir") if not os.path.isdir(lockdir): logger.error("lock directory %s does not exist", lockdir) valid = False piddir = lp.get("pid directory") if not os.path.isdir(piddir): logger.error("pid directory %s does not exist", piddir) valid = False winbind_separator = lp.get("winbind separator") if len(winbind_separator) != 1: logger.error("the 'winbind separator' parameter must be a single " "character.") valid = False if winbind_separator == '+': logger.error("'winbind separator = +' might cause problems with group " "membership.") valid = False return valid def allow_access(deny_list, allow_list, cname, caddr): raise NotImplementedError(allow_access) def do_share_checks(lp, logger): valid = True for s in lp.services(): if len(s) > 12: logger.warning("You have some share names that are longer than 12 " "characters. These may not be accessible to some older " "clients. (Eg. Windows9x, WindowsMe, and not listed in " "smbclient in Samba 3.0.)") break for s in lp.services(): deny_list = lp.get("hosts deny", s) allow_list = lp.get("hosts allow", s) if deny_list: for entry in deny_list: if "*" in entry or "?" in entry: logger.error("Invalid character (* or ?) in hosts deny " "list (%s) for service %s.", entry, s) valid = False if allow_list: for entry in allow_list: if "*" in entry or "?" in entry: logger.error("Invalid character (* or ?) in hosts allow " "list (%s) for service %s.", entry, s) valid = False return valid def check_client_access(lp, cname, caddr): # this is totally ugly, a real `quick' hack for s in lp.services(): if (allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname, caddr) and allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s), cname, caddr)): logger.info("Allow connection from %s (%s) to %s", cname, caddr, s) else: logger.info("Deny connection from %s (%s) to %s", cname, caddr, s) if __name__ == '__main__': parser = optparse.OptionParser("testparm [OPTION...] [host-name] [host-ip]") parser.add_option("--section-name", type="string", metavar="SECTION", help="Limit testparm to a named section") parser.add_option("--parameter-name", type="string", metavar="PARAMETER", help="Limit testparm to a named parameter") parser.add_option("--client-name", type="string", metavar="HOSTNAME", help="Client DNS name for 'hosts allow' checking " "(should match reverse lookup)") parser.add_option("--client-ip", type="string", metavar="IP", help="Client IP address for 'hosts allow' checking") parser.add_option("--suppress-prompt", action="store_true", default=False, help="Suppress prompt for enter") parser.add_option("-v", "--verbose", action="store_true", default=False, help="Show default options too") parser.add_option_group(options.VersionOptions(parser)) # We need support for smb.conf macros before this will work again parser.add_option("--server", type="string", help="Set %%L macro to servername") # These are harder to do with the new code structure parser.add_option("--show-all-parameters", action="store_true", default=False, help="Show the parameters, type, possible values") sambaopts = options.SambaOptions(parser) parser.add_option_group(sambaopts) opts, args = parser.parse_args() # # if (show_all_parameters) { # show_parameter_list() # exit(0) # } if len(args) > 0: cname = args[0] else: cname = None if len(args) > 1: caddr = args[1] else: caddr = None if cname is not None and caddr is None: print "Both a DNS name and an IP address are required for the host " \ "access check." sys.exit(1) # FIXME: We need support for smb.conf macros before this will work again # # if (new_local_machine) { # set_local_machine_name(new_local_machine, True) # } lp = sambaopts.get_loadparm() # We need this to force the output samba.set_debug_level(2) logger = logging.getLogger("testparm") logger.addHandler(logging.StreamHandler(sys.stdout)) logger.info("Loaded smb config files from %s", lp.configfile) logger.info("Loaded services file OK.") valid = do_global_checks(lp, logger) valid = valid and do_share_checks(lp, logger) if cname is not None: check_client_access(lp, cname, caddr) else: if opts.section_name is not None or opts.parameter_name is not None: if opts.parameter_name is None: lp[opts.section_name].dump(sys.stdout, lp.default_service, opts.verbose) else: print lp.get(opts.parameter_name, opts.section_name) else: if not opts.suppress_prompt: print "Press enter to see a dump of your service definitions\n" sys.stdin.readline() lp.dump(sys.stdout, opts.verbose) if valid: sys.exit(0) else: sys.exit(1)