libnet: Fix Coverity ID 1634803 Dereference after null check
[Samba.git] / python / samba / netcmd / testparm.py
bloba419ddf126064de8bb6da2660f8dc08781aabee6
1 # Unix SMB/CIFS implementation.
2 # Test validity of smb.conf
3 # Copyright (C) 2010-2011 Jelmer Vernooij <jelmer@samba.org>
5 # Based on the original in C:
6 # Copyright (C) Karl Auer 1993, 1994-1998
7 # Extensively modified by Andrew Tridgell, 1995
8 # Converted to popt by Jelmer Vernooij (jelmer@nl.linux.org), 2002
9 # Updated for Samba4 by Andrew Bartlett <abartlet@samba.org> 2006
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 3 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # Testbed for loadparm.c/params.c
26 # This module simply loads a specified configuration file and
27 # if successful, dumps it's contents to stdout. Note that the
28 # operation is performed with DEBUGLEVEL at 3.
30 # Useful for a quick 'syntax check' of a configuration file.
33 import os
34 import sys
36 import samba
37 import samba.getopt as options
38 from samba.netcmd import Command, CommandError, Option
41 class cmd_testparm(Command):
42 """Syntax check the configuration file."""
44 synopsis = "%prog [options]"
46 takes_optiongroups = {
47 "sambaopts": options.SambaOptions,
48 "versionopts": options.VersionOptions
51 takes_options = [
52 Option("--section-name", type=str,
53 help="Limit testparm to a named section"),
54 Option("--parameter-name", type=str,
55 help="Limit testparm to a named parameter"),
56 Option("--client-name", type=str,
57 help="Client DNS name for 'hosts allow' checking "
58 "(should match reverse lookup)"),
59 Option("--client-ip", type=str,
60 help="Client IP address for 'hosts allow' checking"),
61 Option("--suppress-prompt", action="store_true", default=False,
62 help="Suppress prompt for enter"),
63 Option("-v", "--verbose", action="store_true",
64 default=False, help="Show default options too"),
65 # We need support for smb.conf macros before this will work again
66 Option("--server", type=str, help="Set %L macro to servername"),
67 # These are harder to do with the new code structure
68 Option("--show-all-parameters", action="store_true", default=False,
69 help="Show the parameters, type, possible values")
72 takes_args = []
74 def run(self, sambaopts, versionopts, section_name=None,
75 parameter_name=None, client_ip=None, client_name=None,
76 verbose=False, suppress_prompt=None, show_all_parameters=False,
77 server=None):
78 if server:
79 raise NotImplementedError("--server not yet implemented")
80 if show_all_parameters:
81 raise NotImplementedError("--show-all-parameters not yet implemented")
82 if client_name is not None and client_ip is None:
83 raise CommandError("Both a DNS name and an IP address are "
84 "required for the host access check")
86 try:
87 lp = sambaopts.get_loadparm()
88 except RuntimeError as err:
89 raise CommandError(err)
91 # We need this to force the output
92 samba.set_debug_level(2)
94 logger = self.get_logger("testparm")
96 logger.info("Loaded smb config files from %s", lp.configfile)
97 logger.info("Loaded services file OK.")
99 valid = self.do_global_checks(lp, logger)
100 valid = valid and self.do_share_checks(lp, logger)
101 if client_name is not None and client_ip is not None:
102 self.check_client_access(lp, logger, client_name, client_ip)
103 else:
104 if section_name is not None or parameter_name is not None:
105 if parameter_name is None:
106 try:
107 section = lp[section_name]
108 except KeyError:
109 if section_name in ['global', 'globals']:
110 lp.dump_globals()
111 else:
112 raise CommandError(f"Unknown section {section_name}")
113 else:
114 section.dump(lp.default_service, verbose)
115 else:
116 try:
117 lp.dump_a_parameter(parameter_name, section_name)
118 except RuntimeError as e:
119 raise CommandError(e)
120 else:
121 if not suppress_prompt:
122 self.outf.write("Press enter to see a dump of your service definitions\n")
123 sys.stdin.readline()
124 lp.dump(verbose)
125 if valid:
126 return
127 else:
128 raise CommandError("Invalid smb.conf")
130 def do_global_checks(self, lp, logger):
131 valid = True
133 netbios_name = lp.get("netbios name")
134 if not samba.valid_netbios_name(netbios_name):
135 logger.error("netbios name %s is not a valid netbios name",
136 netbios_name)
137 valid = False
139 workgroup = lp.get("workgroup")
140 if not samba.valid_netbios_name(workgroup):
141 logger.error("workgroup name %s is not a valid netbios name",
142 workgroup)
143 valid = False
145 lockdir = lp.get("lockdir")
147 if not os.path.isdir(lockdir):
148 logger.error("lock directory %s does not exist", lockdir)
149 valid = False
151 piddir = lp.get("pid directory")
153 if not os.path.isdir(piddir):
154 logger.error("pid directory %s does not exist", piddir)
155 valid = False
157 winbind_separator = lp.get("winbind separator")
159 if len(winbind_separator) != 1:
160 logger.error("the 'winbind separator' parameter must be a single "
161 "character.")
162 valid = False
164 if winbind_separator == '+':
165 logger.error(
166 "'winbind separator = +' might cause problems with group "
167 "membership.")
168 valid = False
170 role = lp.get("server role")
172 if role in ["active directory domain controller", "domain controller", "dc"]:
173 charset = lp.get("unix charset").upper()
174 if charset not in ["UTF-8", "UTF8"]:
175 logger.warning(
176 "When acting as Active Directory domain controller, "
177 "unix charset is expected to be UTF-8.")
178 vfsobjects = lp.get("vfs objects")
179 if vfsobjects:
180 for entry in ['dfs_samba4', 'acl_xattr']:
181 if entry not in vfsobjects:
182 logger.warning(
183 "When acting as Active Directory domain controller, " +
184 entry + " should be in vfs objects.")
186 strong_auth = lp.get("ldap server require strong auth")
187 if strong_auth == "allow_sasl_over_tls":
188 logger.warning(
189 "WARNING: You have not configured "
190 "'ldap server require strong auth = "
191 "allow_sasl_over_tls'.\n"
192 "Please change to 'yes' (preferred) or "
193 "'allow_sasl_without_tls_channel_bindings' "
194 "(if really needed).")
196 return valid
198 def allow_access(self, deny_list, allow_list, cname, caddr):
199 raise NotImplementedError(self.allow_access)
201 def do_share_checks(self, lp, logger):
202 valid = True
203 for s in lp.services():
204 if len(s) > 12:
205 logger.warning(
206 "You have some share names that are longer than 12 "
207 "characters. These may not be accessible to some older "
208 "clients. (Eg. Windows9x, WindowsMe, and not listed in "
209 "smbclient in Samba 3.0.)")
210 break
212 for s in lp.services():
213 deny_list = lp.get("hosts deny", s)
214 allow_list = lp.get("hosts allow", s)
215 if deny_list:
216 for entry in deny_list:
217 if "*" in entry or "?" in entry:
218 logger.error("Invalid character (* or ?) in hosts deny "
219 "list (%s) for service %s.", entry, s)
220 valid = False
222 if allow_list:
223 for entry in allow_list:
224 if "*" in entry or "?" in entry:
225 logger.error("Invalid character (* or ?) in hosts allow "
226 "list (%s) for service %s.", entry, s)
227 valid = False
228 return valid
230 def check_client_access(self, lp, logger, cname, caddr):
231 # this is totally ugly, a real `quick' hack
232 for s in lp.services():
233 if (self.allow_access(lp.get("hosts deny"), lp.get("hosts allow"), cname,
234 caddr) and
235 self.allow_access(lp.get("hosts deny", s), lp.get("hosts allow", s),
236 cname, caddr)):
237 logger.info("Allow connection from %s (%s) to %s", cname, caddr, s)
238 else:
239 logger.info("Deny connection from %s (%s) to %s", cname, caddr, s)
241 ## FIXME: We need support for smb.conf macros before this will work again
243 ## if (new_local_machine) {
244 ## set_local_machine_name(new_local_machine, True)
245 ## }