class DCKeytabTests(tests.TestCase): def setUp(self): super(DCKeytabTests, self).setUp() self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) self.ktfile = os.path.join(self.lp.get('private dir'), 'test.keytab') self.principal = self.creds.get_principal() def tearDown(self): super(DCKeytabTests, self).tearDown() os.remove(self.ktfile) def test_export_keytab(self): net = Net(None, self.lp) net.export_keytab(keytab=self.ktfile, principal=self.principal) assert os.path.exists(self.ktfile), 'keytab was not created' with open_bytes(self.ktfile) as bytes_kt: result = '' for c in bytes_kt.read(): if c in string.printable: result += c principal_parts = self.principal.split('@') assert principal_parts[0] in result and \ principal_parts[1] in result, \ 'Principal not found in generated keytab'
def main(): lp = LoadParm() lp.load_default() print("\nLoaded loadparm:") print(" samdb_url: ", lp.samdb_url()) print(" server_role: ", lp.server_role()) creds = Credentials() creds.guess(lp) print("\nCredentials:") creds.set_kerberos_state(MUST_USE_KERBEROS) # If MUST_USE_KERBEROS and we have no ticket, yields this error: # "Failed to connect to 'ldap://dc1' with backend 'ldap': LDAP client internal error: NT_STATUS_INVALID_PARAMETER" # Local #samdb = SamDB(lp=lp) # Remote samdb = SamDB(lp=lp, url='ldap://dc1', credentials=creds) print("\nOpened SAM DB:") print(" domain_dn: ", samdb.domain_dn()) print(" domain_dns_name:", samdb.domain_dns_name()) for q in sys.argv[1:]: query(samdb, q)
def user_policy_apply(user, password): user = user.split('\\')[-1] logger = logging.getLogger('gpupdate') logger.addHandler(logging.StreamHandler(sys.stdout)) logger.setLevel(logging.CRITICAL) log_level = lp.log_level() if log_level == 1: logger.setLevel(logging.ERROR) elif log_level == 2: logger.setLevel(logging.WARNING) elif log_level == 3: logger.setLevel(logging.INFO) elif log_level >= 4: logger.setLevel(logging.DEBUG) lp = LoadParm() lp.load_default() creds = Credentials() creds.guess(lp) creds.set_username(user) creds.set_password(password) _, user_exts = get_gp_client_side_extensions(logger) gp_extensions = [] for ext in user_exts: gp_extensions.append(ext(logger, lp, creds, store)) cache_dir = lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) try: apply_gp(lp, creds, logger, store, gp_extensions) except NTSTATUSError as e: logger.info(e.message)
class SambaOptions(optparse.OptionGroup): """General Samba-related command line options.""" def __init__(self, parser): from samba.param import LoadParm optparse.OptionGroup.__init__(self, parser, "Samba Common Options") self.add_option("-s", "--configfile", action="callback", type=str, metavar="FILE", help="Configuration file", callback=self._load_configfile) self.add_option("-d", "--debuglevel", action="callback", type=int, metavar="DEBUGLEVEL", help="debug level", callback=self._set_debuglevel) self.add_option("--option", action="callback", type=str, metavar="OPTION", help="set smb.conf option from command line", callback=self._set_option) self.add_option("--realm", action="callback", type=str, metavar="REALM", help="set the realm name", callback=self._set_realm) self._configfile = None self._lp = LoadParm() self.realm = None def get_loadparm_path(self): """Return path to the smb.conf file specified on the command line.""" return self._configfile def _load_configfile(self, option, opt_str, arg, parser): self._configfile = arg def _set_debuglevel(self, option, opt_str, arg, parser): if arg < 0: raise optparse.OptionValueError("invalid %s option value: %s" % (opt_str, arg)) self._lp.set('debug level', str(arg)) def _set_realm(self, option, opt_str, arg, parser): self._lp.set('realm', arg) self.realm = arg def _set_option(self, option, opt_str, arg, parser): if arg.find('=') == -1: raise optparse.OptionValueError( "--option option takes a 'a=b' argument") a = arg.split('=') try: self._lp.set(a[0], a[1]) except Exception as e: raise optparse.OptionValueError( "invalid --option option value %r: %s" % (arg, e)) def get_loadparm(self): """Return loadparm object with data specified on the command line.""" if self._configfile is not None: self._lp.load(self._configfile) elif os.getenv("SMB_CONF_PATH") is not None: self._lp.load(os.getenv("SMB_CONF_PATH")) else: self._lp.load_default() return self._lp
class SambaOptions(optparse.OptionGroup): """General Samba-related command line options.""" def __init__(self, parser): from samba import fault_setup fault_setup() from samba.param import LoadParm optparse.OptionGroup.__init__(self, parser, "Samba Common Options") self.add_option("-s", "--configfile", action="callback", type=str, metavar="FILE", help="Configuration file", callback=self._load_configfile) self.add_option("-d", "--debuglevel", action="callback", type=str, metavar="DEBUGLEVEL", help="debug level", callback=self._set_debuglevel) self.add_option("--option", action="callback", type=str, metavar="OPTION", help="set smb.conf option from command line", callback=self._set_option) self.add_option("--realm", action="callback", type=str, metavar="REALM", help="set the realm name", callback=self._set_realm) self._configfile = None self._lp = LoadParm() self.realm = None def get_loadparm_path(self): """Return path to the smb.conf file specified on the command line.""" return self._configfile def _load_configfile(self, option, opt_str, arg, parser): self._configfile = arg def _set_debuglevel(self, option, opt_str, arg, parser): self._lp.set('debug level', arg) parser.values.debuglevel = arg def _set_realm(self, option, opt_str, arg, parser): self._lp.set('realm', arg) self.realm = arg def _set_option(self, option, opt_str, arg, parser): if arg.find('=') == -1: raise optparse.OptionValueError( "--option option takes a 'a=b' argument") a = arg.split('=') try: self._lp.set(a[0], a[1]) except Exception as e: raise optparse.OptionValueError( "invalid --option option value %r: %s" % (arg, e)) def get_loadparm(self): """Return loadparm object with data specified on the command line.""" if self._configfile is not None: self._lp.load(self._configfile) elif os.getenv("SMB_CONF_PATH") is not None: self._lp.load(os.getenv("SMB_CONF_PATH")) else: self._lp.load_default() return self._lp
class AuthSMB4(object): def __init__(self, user, password): self.user = user self.password = password _isLastErrorAvailable = False self.lp = LoadParm() self.lp.load_default() self.ip = '127.0.0.1' self.WorkGroup = str(self.lp.get("workgroup")) self.creds = credentials.Credentials() self.creds.set_username(self.user) self.creds.set_password(self.password) self.creds.set_domain(self.WorkGroup) self.creds.set_workstation("") self.logger = logging.getLogger(__name__) self.logger.addHandler(logging.StreamHandler(sys.stdout)) self.logger.setLevel(logging.INFO) def Autenticate(self): try: session_info_flags = (AUTH_SESSION_INFO_DEFAULT_GROUPS | AUTH_SESSION_INFO_AUTHENTICATED) LdapConn = samba.Ldb("ldap://%s" % self.ip, lp=self.lp, credentials=self.creds) DomainDN = LdapConn.get_default_basedn() search_filter = "sAMAccountName=%s" % self.user res = LdapConn.search(base=DomainDN, scope=SCOPE_SUBTREE, expression=search_filter, attrs=["dn"]) if len(res) == 0: return False user_dn = res[0].dn session = samba.auth.user_session( LdapConn, lp_ctx=self.lp, dn=user_dn, session_info_flags=session_info_flags) token = session.security_token if (token.has_builtin_administrators()): return True if (token.is_system()): return True except Exception, e: if (len(e.args) > 1): self.logger.info("%s %s" % (e.args[1], e.args[0])) self.SetError(e.args[1], e.args[0]) else: self.logger.info("%s " % (e.args[0])) self.SetError(e.args, 0) return False
class SambaOCHelper(object): def __init__(self): self.samba_lp = LoadParm() self.samba_lp.set('debug level', '0') self.samba_lp.load_default() url = self.samba_lp.get('dcerpc_mapiproxy:samdb_url') or \ self.samba_lp.private_path("sam.ldb") self.samdb = SamDB(url=url, lp=self.samba_lp, session_info=system_session()) self.conn = self._open_mysql_connection() def _open_mysql_connection(self): connection_string = self.samba_lp.get('mapiproxy:openchangedb') if not connection_string: raise Exception( "Not found mapiproxy:openchangedb on samba configuration") # mysql://openchange:password@localhost/openchange m = re.search( r'(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@(?P<host>.+)/(?P<db>.+)', connection_string) if not m: raise Exception("Unable to parse mapiproxy:openchangedb: %s" % connection_string) group_dict = m.groupdict() if group_dict['scheme'] != 'mysql': raise Exception( "mapiproxy:openchangedb should start with mysql:// (we got %s)", group_dict['scheme']) conn = MySQLdb.connect(host=group_dict['host'], user=group_dict['user'], passwd=group_dict['pass'], db=group_dict['db']) conn.autocommit(True) return conn def invalid_user(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return len(ret) != 1 def find_email_of(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, attrs=["mail"], expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return ret[0]["mail"][0] def active_openchange_users(self): c = self.conn.cursor() c.execute("SELECT name FROM mailboxes") return sorted([row[0] for row in c.fetchall()])
def parse_gpext_conf(smb_conf): lp = LoadParm() if smb_conf is not None: lp.load(smb_conf) else: lp.load_default() ext_conf = lp.state_path('gpext.conf') parser = ConfigParser() parser.read(ext_conf) return lp, parser
class SambaOCHelper(object): def __init__(self): self.samba_lp = LoadParm() self.samba_lp.set('debug level', '0') self.samba_lp.load_default() url = self.samba_lp.get('dcerpc_mapiproxy:samdb_url') or \ self.samba_lp.private_path("sam.ldb") self.samdb = SamDB(url=url, lp=self.samba_lp, session_info=system_session()) self.conn = self._open_mysql_connection() def _open_mysql_connection(self): connection_string = self.samba_lp.get('mapiproxy:openchangedb') if not connection_string: raise Exception("Not found mapiproxy:openchangedb on samba configuration") # mysql://openchange:password@localhost/openchange m = re.search(r'(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@(?P<host>.+)/(?P<db>.+)', connection_string) if not m: raise Exception("Unable to parse mapiproxy:openchangedb: %s" % connection_string) group_dict = m.groupdict() if group_dict['scheme'] != 'mysql': raise Exception("mapiproxy:openchangedb should start with mysql:// (we got %s)", group_dict['scheme']) conn = MySQLdb.connect(host=group_dict['host'], user=group_dict['user'], passwd=group_dict['pass'], db=group_dict['db']) conn.autocommit(True) return conn def invalid_user(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return len(ret) != 1 def find_email_of(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, attrs=["mail"], expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return ret[0]["mail"][0] def active_openchange_users(self): c = self.conn.cursor() c.execute("SELECT name FROM mailboxes") return sorted([row[0] for row in c.fetchall()]) def get_indexing_cache(self): memcached_server = self.samba_lp.get('mapistore:indexing_cache') if not memcached_server: return "127.0.0.1:11211" # This should has a format like: --SERVER=11.22.33.44:11211 return memcached_server.split('=')[1]
def parametros(fichero=False): """ Intento ser un poco cuidadoso con la forma en que obtenemos la configuración """ try: lp = LoadParm() lp.load_default() if fichero is False else lp.load(fichero) return lp except RuntimeError as e: log.warning(e.message) raise ConfiguracionException(e)
class GPOTests(tests.TestCase): def setUp(self): super(GPOTests, self).setUp() self.server = os.environ["SERVER"] self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) def tearDown(self): super(GPOTests, self).tearDown() def test_gpo_list(self): global poldir, dspath ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' names = ['Local Policy', guid] file_sys_paths = [None, '%s\\%s' % (poldir, guid)] ds_paths = [None, 'CN=%s,%s' % (guid, dspath)] for i in range(0, len(gpos)): assert gpos[i].name == names[i], \ 'The gpo name did not match expected name %s' % gpos[i].name assert gpos[i].file_sys_path == file_sys_paths[i], \ 'file_sys_path did not match expected %s' % gpos[i].file_sys_path assert gpos[i].ds_path == ds_paths[i], \ 'ds_path did not match expected %s' % gpos[i].ds_path def test_gpo_ads_does_not_segfault(self): try: ads = gpo.ADS_STRUCT(self.server, 42, self.creds) except: pass def test_gpt_version(self): global gpt_data local_path = self.lp.get("path", "sysvol") policies = 'addom.samba.example.com/Policies' guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpo_path = os.path.join(local_path, policies, guid) old_vers = gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % 42) assert gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] == 42, \ 'gpo_get_sysvol_gpt_version() did not return the expected version' with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % old_vers) assert gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] == old_vers, \ 'gpo_get_sysvol_gpt_version() did not return the expected version'
def main(): args = parse_args() logging.basicConfig(level=logging.DEBUG if args.debug else logging.WARNING) inpipe = sys.stdin outpipe = sys.stdout if args.unix: import socket try: os.remove(args.unix) except OSError: pass sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(args.unix) os.chmod(args.unix, 0o777) logger.debug("Bound unix socket: {}".format(args.unix)) logger.info("Waiting for connection at {}".format(args.unix)) sock.listen(1) csock, client_address = sock.accept() logger.info("Accepted connection from {}".format(client_address)) inpipe = outpipe = csock.makefile() lp = LoadParm() lp.load_default() creds = Credentials() creds.guess(lp) creds.set_kerberos_state(MUST_USE_KERBEROS) # If MUST_USE_KERBEROS and we have no ticket, yields this error: # "Failed to connect to 'ldap://dc1' with backend 'ldap': LDAP client # internal error: NT_STATUS_INVALID_PARAMETER" # lp is required by ldap_connect_send() -> lpcfg_resolve_context() samdb = SamDB(lp=lp, url=args.url, credentials=creds) logger.debug("Opened SAM DB:") logger.debug(" domain_dn: {}".format(samdb.domain_dn())) logger.debug(" domain_dns_name: {}".format(samdb.domain_dns_name())) try: r = SambaResponder(samdb, inpipe, outpipe) r.run() finally: if args.unix: try: os.remove(args.unix) except OSError: pass
class AuthSMB4(object): def __init__(self,user,password): self.user = user self.password = password _isLastErrorAvailable=False self.lp = LoadParm() self.lp.load_default() self.ip = '127.0.0.1' self.WorkGroup = str(self.lp.get("workgroup")) self.creds = credentials.Credentials() self.creds.set_username(self.user) self.creds.set_password(self.password) self.creds.set_domain(self.WorkGroup) self.creds.set_workstation("") self.logger = logging.getLogger(__name__) self.logger.addHandler(logging.StreamHandler(sys.stdout)) self.logger.setLevel(logging.INFO) def Autenticate(self): try: session_info_flags = ( AUTH_SESSION_INFO_DEFAULT_GROUPS | AUTH_SESSION_INFO_AUTHENTICATED ) LdapConn = samba.Ldb("ldap://%s" % self.ip,lp=self.lp,credentials=self.creds) DomainDN = LdapConn.get_default_basedn() search_filter="sAMAccountName=%s" % self.user res = LdapConn.search(base=DomainDN, scope=SCOPE_SUBTREE,expression=search_filter, attrs=["dn"]) if len(res) == 0: return False user_dn = res[0].dn session = samba.auth.user_session(LdapConn, lp_ctx=self.lp, dn=user_dn,session_info_flags=session_info_flags) token = session.security_token if (token.has_builtin_administrators()): return True if(token.is_system()): return True except Exception,e: if(len(e.args)>1): self.logger.info("%s %s" % (e.args[1],e.args[0])) self.SetError(e.args[1],e.args[0]) else: self.logger.info("%s " % (e.args[0])) self.SetError(e.args,0) return False
class Options: def __init__(self): self.function_level = None self.use_xattrs = "auto" self.lp = LoadParm(); self.lp.load_default(); self.realm = self.lp.get('realm') #default self.domain = self.lp.get('workgroup') #default self.adminpass = '' self.smbconf = self.lp.configfile self.server_role = self.lp.get("server role") #default self.samdb_fill = FILL_FULL #default self.blank = False self.partitions_only = False
class SambaOCHelper(object): def __init__(self): self.samba_lp = LoadParm() self.samba_lp.set("debug level", "0") self.samba_lp.load_default() url = self.samba_lp.get("dcerpc_mapiproxy:samdb_url") or self.samba_lp.private_path("sam.ldb") self.samdb = SamDB(url=url, lp=self.samba_lp, session_info=system_session()) self.conn = self._open_mysql_connection() def _open_mysql_connection(self): connection_string = self.samba_lp.get("mapiproxy:openchangedb") if not connection_string: raise Exception("Not found mapiproxy:openchangedb on samba configuration") # mysql://openchange:password@localhost/openchange m = re.search(r"(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@(?P<host>.+)/(?P<db>.+)", connection_string) if not m: raise Exception("Unable to parse mapiproxy:openchangedb: %s" % connection_string) group_dict = m.groupdict() if group_dict["scheme"] != "mysql": raise Exception("mapiproxy:openchangedb should start with mysql:// (we got %s)", group_dict["scheme"]) conn = MySQLdb.connect( host=group_dict["host"], user=group_dict["user"], passwd=group_dict["pass"], db=group_dict["db"] ) conn.autocommit(True) return conn def invalid_user(self, username): ret = self.samdb.search( base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression="(sAMAccountName=%s)" % ldb.binary_encode(username), ) return len(ret) != 1 def find_email_of(self, username): ret = self.samdb.search( base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, attrs=["mail"], expression="(sAMAccountName=%s)" % ldb.binary_encode(username), ) return ret[0]["mail"][0] def active_openchange_users(self): c = self.conn.cursor() c.execute("SELECT name FROM mailboxes") return sorted([row[0] for row in c.fetchall()])
class auth_base(object): def __init__(self, user, password): self.user = user self.password = password self.lp = LoadParm() self.lp.load_default() self.ip = '127.0.0.1' self.WorkGroup = str(self.lp.get("workgroup")) self.creds = credentials.Credentials() self.creds.set_username(self.user) self.creds.set_password(self.password) self.creds.set_domain(self.WorkGroup) self.creds.set_workstation("") def autenticate(self): try: session_info_flags = (df_gp1 | df_gp2) LdapConn = samba.Ldb("ldap://%s" % self.ip, lp=self.lp, credentials=self.creds) DomainDN = LdapConn.get_default_basedn() search_filter = "sAMAccountName=%s" % self.user res = LdapConn.search(base=DomainDN, scope=SCOPE_SUBTREE, expression=search_filter, attrs=["dn"]) if len(res) == 0: return False user_dn = res[0].dn session = samba.auth.user_session( LdapConn, lp_ctx=self.lp, dn=user_dn, session_info_flags=session_info_flags) token = session.security_token if (token.has_builtin_administrators()): return True if (token.is_system()): return True except Exception: return False return False
def get_samba_sam_ldb_path(self): """ Returns the 'sam.ldb' path using samba conf or defaults. """ print( "\nObtaining the Samba configuration to determine Samba private path" ) smb_conf_path = getenv("SMB_CONF_PATH") SambaLP = LoadParm() if smb_conf_path: SambaLP.load(smb_conf_path) else: SambaLP.load_default() return SambaLP.private_path('sam.ldb')
def apply(cls, cur, **kwargs): # Mimetise what mapistore_interface.c (mapistore_init) does # to get the mapping path if 'lp' in kwargs: mapping_path = kwargs['lp'].private_path("mapistore") else: lp = LoadParm() lp.load_default() mapping_path = lp.private_path("mapistore") if mapping_path is None: return cur.execute("START TRANSACTION") try: # Get all mailboxes cur.execute("SELECT name FROM mailboxes") for row in cur.fetchall(): username = row[0] path = "{0}/{1}/replica_mapping.tdb".format( mapping_path, username) try: tdb_file = tdb.Tdb(path, 0, tdb.DEFAULT, os.O_RDONLY) for k in tdb_file.iterkeys(): # Check if the key is an integer try: repl_id = int(k, base=16) cls._insert_map(cur, username, repl_id, tdb_file[k]) except ValueError: # Cannot convert to int, so no repl_id continue except IOError: # Cannot read any replica mapping continue cur.execute("COMMIT") except Exception as e: print("Error migrating TDB files into the database {}, rollback". format(e), file=sys.stderr) cur.execute("ROLLBACK") raise
class GPOTests(tests.TestCase): def setUp(self): super(GPOTests, self).setUp() self.server = os.environ["SERVER"] self.dc_account = self.server.upper() + '$' self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) def tearDown(self): super(GPOTests, self).tearDown() def test_sec_ext_load_on_member(self): logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) try: gp_access_ext(logger, self.lp, self.creds, store) except Exception: self.fail('Initializing gp_access_ext should not require ad-dc')
def apply(cls, cur, **kwargs): # Mimetise what mapistore_interface.c (mapistore_init) does # to get the mapping path if 'lp' in kwargs: mapping_path = kwargs['lp'].private_path("mapistore") else: lp = LoadParm() lp.load_default() mapping_path = lp.private_path("mapistore") if mapping_path is None: return cur.execute("START TRANSACTION") try: # Get all mailboxes cur.execute("SELECT name FROM mailboxes") for row in cur.fetchall(): username = row[0] path = "{0}/{1}/replica_mapping.tdb".format(mapping_path, username) try: tdb_file = tdb.Tdb(path, 0, tdb.DEFAULT, os.O_RDONLY) for k in tdb_file.iterkeys(): # Check if the key is an integer try: repl_id = int(k, base=16) cls._insert_map(cur, username, repl_id, tdb_file[k]) except ValueError: # Cannot convert to int, so no repl_id continue except IOError: # Cannot read any replica mapping continue cur.execute("COMMIT") except Exception as e: print("Error migrating TDB files into the database {}, rollback".format(e), file=sys.stderr) cur.execute("ROLLBACK") raise
class AuthBase(object): def __init__(self,user,password): self.user = user self.password = password _isLastErrorAvailable=False self.lp = LoadParm() self.lp.load_default() self.logger = logging.getLogger(__name__) self.logger.addHandler(logging.StreamHandler(sys.stdout)) self.logger.setLevel(logging.INFO) def Autenticate(self): pass def SetError(self,message,number=-1): self.LastErrorStr = message self.LastErrorNumber = number #self.Log.LogError(message) self._isLastErrorAvailable=True def IHaveError(self): return self._isLastErrorAvailable
class AuthBase(object): def __init__(self, user, password): self.user = user self.password = password _isLastErrorAvailable = False self.lp = LoadParm() self.lp.load_default() self.logger = logging.getLogger(__name__) self.logger.addHandler(logging.StreamHandler(sys.stdout)) self.logger.setLevel(logging.INFO) def Autenticate(self): pass def SetError(self, message, number=-1): self.LastErrorStr = message self.LastErrorNumber = number #self.Log.LogError(message) self._isLastErrorAvailable = True def IHaveError(self): return self._isLastErrorAvailable
credopts.add_option('--password', dest='password', help='Password') credopts.add_option('-U', '--username', dest='username', help='Username') credopts.add_option('--krb5-ccache', dest='krb5_ccache', help='Kerberos Credentials cache') parser.add_option_group(credopts) # Set the options and the arguments (opts, args) = parser.parse_args() # Set the loadparm context lp = LoadParm() if os.getenv("SMB_CONF_PATH") is not None: lp.load(os.getenv("SMB_CONF_PATH")) else: lp.load_default() # Initialize the session creds = Credentials() if opts.username and opts.password: creds.set_username(opts.username) creds.set_password(opts.password) elif opts.krb5_ccache: creds.set_named_ccache(opts.krb5_ccache) creds.guess(lp) from dialogs import GPMC, GPME from yast import UISequencer s = UISequencer(lp, creds) funcs = [(lambda lp, creds: GPMC(lp, creds).Show()), (lambda gpo, lp, creds: GPME(gpo, lp, creds).Show())]
class GPOTests(tests.TestCase): def setUp(self): super(GPOTests, self).setUp() self.server = os.environ["SERVER"] self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) def tearDown(self): super(GPOTests, self).tearDown() def test_gpo_list(self): global poldir, dspath ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' names = ['Local Policy', guid] file_sys_paths = [None, '%s\\%s' % (poldir, guid)] ds_paths = [None, 'CN=%s,%s' % (guid, dspath)] for i in range(0, len(gpos)): self.assertEquals( gpos[i].name, names[i], 'The gpo name did not match expected name %s' % gpos[i].name) self.assertEquals( gpos[i].file_sys_path, file_sys_paths[i], 'file_sys_path did not match expected %s' % gpos[i].file_sys_path) self.assertEquals( gpos[i].ds_path, ds_paths[i], 'ds_path did not match expected %s' % gpos[i].ds_path) def test_gpo_ads_does_not_segfault(self): try: ads = gpo.ADS_STRUCT(self.server, 42, self.creds) except: pass def test_gpt_version(self): global gpt_data local_path = self.lp.cache_path('gpo_cache') policies = 'ADDOM.SAMBA.EXAMPLE.COM/POLICIES' guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpo_path = os.path.join(local_path, policies, guid) old_vers = gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % 42) self.assertEquals( gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], 42, 'gpo_get_sysvol_gpt_version() did not return the expected version') with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % old_vers) self.assertEquals( gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], old_vers, 'gpo_get_sysvol_gpt_version() did not return the expected version') def test_check_refresh_gpo_list(self): cache = self.lp.cache_path('gpo_cache') ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) check_refresh_gpo_list(self.server, self.lp, self.creds, gpos) self.assertTrue(os.path.exists(cache), 'GPO cache %s was not created' % cache) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpt_ini = os.path.join(cache, 'ADDOM.SAMBA.EXAMPLE.COM/POLICIES', guid, 'GPT.INI') self.assertTrue(os.path.exists(gpt_ini), 'GPT.INI was not cached for %s' % guid) def test_check_refresh_gpo_list_malicious_paths(self): # the path cannot contain .. path = '/usr/local/samba/var/locks/sysvol/../../../../../../root/' self.assertRaises(OSError, check_safe_path, path) self.assertEqual(check_safe_path('/etc/passwd'), 'etc/passwd') self.assertEqual(check_safe_path('\\\\etc/\\passwd'), 'etc/passwd') # there should be no backslashes used to delineate paths before = 'sysvol/addom.samba.example.com\\Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}\\GPT.INI' after = 'addom.samba.example.com/Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}/GPT.INI' result = check_safe_path(before) self.assertEquals( result, after, 'check_safe_path() didn\'t' ' correctly convert \\ to /') def test_gpt_ext_register(self): this_path = os.path.dirname(os.path.realpath(__file__)) samba_path = os.path.realpath(os.path.join(this_path, '../../../')) ext_path = os.path.join(samba_path, 'python/samba/gp_sec_ext.py') ext_guid = '{827D319E-6EAC-11D2-A4EA-00C04F79F83A}' ret = register_gp_extension(ext_guid, 'gp_sec_ext', ext_path, smb_conf=self.lp.configfile, machine=True, user=False) self.assertTrue(ret, 'Failed to register a gp ext') gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid in gp_exts.keys(), 'Failed to list gp exts') self.assertEquals(gp_exts[ext_guid]['DllName'], ext_path, 'Failed to list gp exts') unregister_gp_extension(ext_guid) gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid not in gp_exts.keys(), 'Failed to unregister gp exts') self.assertTrue(check_guid(ext_guid), 'Failed to parse valid guid') self.assertFalse(check_guid('AAAAAABBBBBBBCCC'), 'Parsed invalid guid') lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue(lp and parser, 'parse_gpext_conf() invalid return') parser.add_section('test_section') parser.set('test_section', 'test_var', ext_guid) atomic_write_conf(lp, parser) lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue('test_section' in parser.sections(), 'test_section not found in gpext.conf') self.assertEquals(parser.get('test_section', 'test_var'), ext_guid, 'Failed to find test variable in gpext.conf') parser.remove_section('test_section') atomic_write_conf(lp, parser) def test_gp_log_get_applied(self): local_path = self.lp.get('path', 'sysvol') guids = [ '{31B2F340-016D-11D2-945F-00C04FB984F9}', '{6AC1786C-016F-11D2-945F-00C04FB984F9}' ] gpofile = '%s/addom.samba.example.com/Policies/%s/MACHINE/Microsoft/' \ 'Windows NT/SecEdit/GptTmpl.inf' stage = '[System Access]\nMinimumPasswordAge = 998\n' cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) for guid in guids: gpttmpl = gpofile % (local_path, guid) ret = stage_file(gpttmpl, stage) self.assertTrue(ret, 'Could not create the target %s' % gpttmpl) ret = gpupdate_force(self.lp) self.assertEquals(ret, 0, 'gpupdate force failed') gp_db = store.get_gplog('ADDC$') applied_guids = gp_db.get_applied_guids() self.assertEquals(len(applied_guids), 2, 'The guids were not found') self.assertIn(guids[0], applied_guids, '%s not in applied guids' % guids[0]) self.assertIn(guids[1], applied_guids, '%s not in applied guids' % guids[1]) applied_settings = gp_db.get_applied_settings(applied_guids) for policy in applied_settings: self.assertIn('System Access', policy[1], 'System Access policies not set') self.assertIn('minPwdAge', policy[1]['System Access'], 'minPwdAge policy not set') if policy[0] == guids[0]: self.assertEqual(int(policy[1]['System Access']['minPwdAge']), days2rel_nttime(1), 'minPwdAge policy not set') elif policy[0] == guids[1]: self.assertEqual(int(policy[1]['System Access']['minPwdAge']), days2rel_nttime(998), 'minPwdAge policy not set') ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list('ADDC$') del_gpos = get_deleted_gpos_list(gp_db, gpos[:-1]) self.assertEqual(len(del_gpos), 1, 'Returned delete gpos is incorrect') self.assertEqual(guids[-1], del_gpos[0][0], 'GUID for delete gpo is incorrect') self.assertIn('System Access', del_gpos[0][1], 'System Access policies not set for removal') self.assertIn('minPwdAge', del_gpos[0][1]['System Access'], 'minPwdAge policy not set for removal') for guid in guids: gpttmpl = gpofile % (local_path, guid) unstage_file(gpttmpl) ret = gpupdate_unapply(self.lp) self.assertEquals(ret, 0, 'gpupdate unapply failed') def test_process_group_policy(self): local_path = self.lp.cache_path('gpo_cache') guids = [ '{31B2F340-016D-11D2-945F-00C04FB984F9}', '{6AC1786C-016F-11D2-945F-00C04FB984F9}' ] gpofile = '%s/ADDOM.SAMBA.EXAMPLE.COM/POLICIES/%s/MACHINE/MICROSOFT/' \ 'WINDOWS NT/SECEDIT/GPTTMPL.INF' logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() # Initialize the group policy extension ext = gp_sec_ext(logger, self.lp, machine_creds, store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) stage = '[Kerberos Policy]\nMaxTicketAge = %d\n' opts = [100, 200] for i in range(0, 2): gpttmpl = gpofile % (local_path, guids[i]) ret = stage_file(gpttmpl, stage % opts[i]) self.assertTrue(ret, 'Could not create the target %s' % gpttmpl) # Process all gpos ext.process_group_policy([], gpos) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, opts[1], 'Higher priority policy was not set') # Remove policy gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, []) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, None, 'MaxTicketAge should not have applied') # Process just the first gpo ext.process_group_policy([], gpos[:-1]) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, opts[0], 'Lower priority policy was not set') # Remove policy ext.process_group_policy(del_gpos, []) for guid in guids: gpttmpl = gpofile % (local_path, guid) unstage_file(gpttmpl)
class MigrationFix(object): def __init__(self, username=None, no_dry_run=False, mysql_string=None): self.lp = LoadParm() self.lp.set('debug level', '0') self.lp.load_default() self.username = username self.no_dry_run = no_dry_run self.conn = self._open_mysql_conn(mysql_string) def _open_mysql_conn(self, mysql_conn): if mysql_conn is None: conn_str = self.lp.get('mapiproxy:openchangedb') else: conn_str = mysql_conn if not conn_str: raise Exception("No mysql connection string specified and no mapiproxy:openchangedb param option found") # mysql://openchange:password@localhost/openchange m = re.search('(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@' '(?P<host>.+)/(?P<db>.+)', conn_str) if not m: raise Exception("Unable to parse mysql connection string: %s" % conn_str) group_dict = m.groupdict() if group_dict['scheme'] != 'mysql': raise Exception("mysql connection string should start with mysql:// (got %s)", group_dict['scheme']) conn = MySQLdb.connect(host=group_dict['host'], user=group_dict['user'], passwd=group_dict['pass'], db=group_dict['db']) conn.autocommit(True) return conn def fix(self, username, folder_id, uri, mailbox_id, ou_id): error = False c = self.conn.cursor() c.execute("SELECT fmid FROM mapistore_indexing WHERE username=%s AND url=%s", (username,uri)) result = c.fetchone() if result is None: print '[KO]: %s: could not find fmid for %s' % (username, uri) return True fmid = result[0] if str(fmid) != str(folder_id): error = True if self.no_dry_run is True: c = self.conn.cursor() c.execute("UPDATE folders SET folder_id=%s WHERE MAPIStoreURI=%s AND mailbox_id=%s AND ou_id=%s", (fmid, uri, mailbox_id, ou_id)) print '[FIX]: %s: folder_id for %s has been fixed and is now set to %s' % (username, uri, folder_id) else: print '[KO]: %s: Mismatch for %s: found %s, expected %s' % (username, uri, folder_id, fmid) return error def run_all(self): c = self.conn.cursor() c.execute("SELECT id,ou_id,name FROM mailboxes") rows = c.fetchall() for row in rows: mailbox_id = row[0] ou_id = row[1] username = row[2] # Retrieve all MAPIStoreURI from folders table for username c = self.conn.cursor() c.execute("SELECT folder_id,MAPIStoreURI FROM folders WHERE mailbox_id=%s AND ou_id=%s AND MAPIStoreURI!=\"\"", (mailbox_id,ou_id)) frows = c.fetchall() # Now check in mapistore_indexing if fmid are matching for given URI gl_error = False for frow in frows: folder_id = frow[0] uri = frow[1] error = self.fix(username, folder_id, uri, mailbox_id, ou_id) if error is True: gl_error = True if gl_error is False: print '[OK]: %s is OK' % (username) def run(self): # Retrieve username id, ou_id c = self.conn.cursor() c.execute("SELECT id,ou_id FROM mailboxes WHERE name=%s", self.username) (mailbox_id,ou_id) = c.fetchone() # Retrieve all MAPIStoreURI from folders table for username c = self.conn.cursor() c.execute("SELECT folder_id,MAPIStoreURI FROM folders WHERE mailbox_id=%s AND ou_id=%s AND MAPIStoreURI!=\"\"", (mailbox_id,ou_id)) rows = c.fetchall() # Now check in mapistore_indexing if fmid are matching for given URI for row in rows: folder_id = row[0] uri = row[1] self.fix(self.username, folder_id, uri, mailbox_id, ou_id)
class MigrationFix(object): def __init__(self, username=None, no_dry_run=False, mysql_string=None): self.lp = LoadParm() self.lp.set('debug level', '0') self.lp.load_default() self.username = username self.no_dry_run = no_dry_run self.conn = self._open_mysql_conn(mysql_string) def _open_mysql_conn(self, mysql_conn): if mysql_conn is None: conn_str = self.lp.get('mapiproxy:openchangedb') else: conn_str = mysql_conn if not conn_str: raise Exception( "No mysql connection string specified and no mapiproxy:openchangedb param option found" ) # mysql://openchange:password@localhost/openchange m = re.search( '(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@' '(?P<host>.+)/(?P<db>.+)', conn_str) if not m: raise Exception("Unable to parse mysql connection string: %s" % conn_str) group_dict = m.groupdict() if group_dict['scheme'] != 'mysql': raise Exception( "mysql connection string should start with mysql:// (got %s)", group_dict['scheme']) conn = MySQLdb.connect(host=group_dict['host'], user=group_dict['user'], passwd=group_dict['pass'], db=group_dict['db']) conn.autocommit(True) return conn def fix(self, username, folder_id, uri, mailbox_id, ou_id): error = False c = self.conn.cursor() c.execute( "SELECT fmid FROM mapistore_indexing WHERE username=%s AND url=%s", (username, uri)) result = c.fetchone() if result is None: print '[KO]: %s: could not find fmid for %s' % (username, uri) return True fmid = result[0] if str(fmid) != str(folder_id): error = True if self.no_dry_run is True: c = self.conn.cursor() c.execute( "UPDATE folders SET folder_id=%s WHERE MAPIStoreURI=%s AND mailbox_id=%s AND ou_id=%s", (fmid, uri, mailbox_id, ou_id)) print '[FIX]: %s: folder_id for %s has been fixed and is now set to %s' % ( username, uri, folder_id) else: print '[KO]: %s: Mismatch for %s: found %s, expected %s' % ( username, uri, folder_id, fmid) return error def run_all(self): c = self.conn.cursor() c.execute("SELECT id,ou_id,name FROM mailboxes") rows = c.fetchall() for row in rows: mailbox_id = row[0] ou_id = row[1] username = row[2] # Retrieve all MAPIStoreURI from folders table for username c = self.conn.cursor() c.execute( "SELECT folder_id,MAPIStoreURI FROM folders WHERE mailbox_id=%s AND ou_id=%s AND MAPIStoreURI!=\"\"", (mailbox_id, ou_id)) frows = c.fetchall() # Now check in mapistore_indexing if fmid are matching for given URI gl_error = False for frow in frows: folder_id = frow[0] uri = frow[1] error = self.fix(username, folder_id, uri, mailbox_id, ou_id) if error is True: gl_error = True if gl_error is False: print '[OK]: %s is OK' % (username) def run(self): # Retrieve username id, ou_id c = self.conn.cursor() c.execute("SELECT id,ou_id FROM mailboxes WHERE name=%s", self.username) (mailbox_id, ou_id) = c.fetchone() # Retrieve all MAPIStoreURI from folders table for username c = self.conn.cursor() c.execute( "SELECT folder_id,MAPIStoreURI FROM folders WHERE mailbox_id=%s AND ou_id=%s AND MAPIStoreURI!=\"\"", (mailbox_id, ou_id)) rows = c.fetchall() # Now check in mapistore_indexing if fmid are matching for given URI for row in rows: folder_id = row[0] uri = row[1] self.fix(self.username, folder_id, uri, mailbox_id, ou_id)
class SambaOCHelper(object): def __init__(self): self.samba_lp = LoadParm() self.samba_lp.set('debug level', '0') self.samba_lp.load_default() self.next_fmid = None url = self.samba_lp.get('dcerpc_mapiproxy:samdb_url') or \ self.samba_lp.private_path("sam.ldb") self.samdb = SamDB(url=url, lp=self.samba_lp, session_info=system_session()) self.conn = self._open_mysql_connection() def _open_mysql_connection(self): connection_string = self.samba_lp.get('mapiproxy:openchangedb') if not connection_string: raise Exception("Not found mapiproxy:openchangedb on samba configuration") # mysql://openchange:password@localhost/openchange m = re.search(r'(?P<scheme>.+)://(?P<user>.+):(?P<pass>.+)@(?P<host>.+)/(?P<db>.+)', connection_string) if not m: raise Exception("Unable to parse mapiproxy:openchangedb: %s" % connection_string) group_dict = m.groupdict() if group_dict['scheme'] != 'mysql': raise Exception("mapiproxy:openchangedb should start with mysql:// (we got %s)", group_dict['scheme']) conn = MySQLdb.connect(host=group_dict['host'], user=group_dict['user'], passwd=group_dict['pass'], db=group_dict['db']) conn.autocommit(True) return conn def invalid_user(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return len(ret) != 1 def find_email_of(self, username): ret = self.samdb.search(base=self.samdb.domain_dn(), scope=ldb.SCOPE_SUBTREE, attrs=["mail"], expression="(sAMAccountName=%s)" % ldb.binary_encode(username)) return ret[0]["mail"][0] def active_openchange_users(self): c = self.conn.cursor() c.execute("SELECT name FROM mailboxes") return sorted([row[0] for row in c.fetchall()]) def allocate_fmids(self, count, username): if self.next_fmid is None: c = self.conn.cursor() c.execute("SELECT next_fmid FROM mapistore_indexes WHERE username = '******'" % username) self.next_fmid = c.fetchone()[0] if self.next_fmid is not None: self.next_fmid = int(self.next_fmid) + count return (int(self.next_fmid) - count, self.next_fmid) def create_indexes(self, username): c = self.conn.cursor() c.execute("INSERT INTO mapistore_indexes (username,next_fmid) VALUES('%s','1024')" % (username)) return def commit_start(self): self.conn.autocommit(False) def insert_indexing(self, username, fmid, url): c = self.conn.cursor() c.execute("INSERT INTO mapistore_indexing (username,fmid,url,soft_deleted) VALUES('%s','%s','%s', '0')" % (username, str(fmid), url)) def update_indexes(self, count, username): c = self.conn.cursor() updated_number = int(count) + int(self.next_fmid) print "Updating next_fmid to %s" % str(updated_number) c.execute("UPDATE mapistore_indexes SET next_fmid='%s' WHERE username='******'" % (str(updated_number), username)) def commit_end(self): c = self.conn.cursor() self.conn.commit() c.close() self.conn.autocommit(True)
class GPOTests(tests.TestCase): def setUp(self): super(GPOTests, self).setUp() self.server = os.environ["SERVER"] self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) def tearDown(self): super(GPOTests, self).tearDown() def test_gpo_list(self): global poldir, dspath ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' names = ['Local Policy', guid] file_sys_paths = [None, '%s\\%s' % (poldir, guid)] ds_paths = [None, 'CN=%s,%s' % (guid, dspath)] for i in range(0, len(gpos)): self.assertEquals(gpos[i].name, names[i], 'The gpo name did not match expected name %s' % gpos[i].name) self.assertEquals(gpos[i].file_sys_path, file_sys_paths[i], 'file_sys_path did not match expected %s' % gpos[i].file_sys_path) self.assertEquals(gpos[i].ds_path, ds_paths[i], 'ds_path did not match expected %s' % gpos[i].ds_path) def test_gpo_ads_does_not_segfault(self): try: ads = gpo.ADS_STRUCT(self.server, 42, self.creds) except: pass def test_gpt_version(self): global gpt_data local_path = self.lp.cache_path('gpo_cache') policies = 'ADDOM.SAMBA.EXAMPLE.COM/POLICIES' guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpo_path = os.path.join(local_path, policies, guid) old_vers = gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % 42) self.assertEquals(gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], 42, 'gpo_get_sysvol_gpt_version() did not return the expected version') with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % old_vers) self.assertEquals(gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], old_vers, 'gpo_get_sysvol_gpt_version() did not return the expected version') def test_check_refresh_gpo_list(self): cache = self.lp.cache_path('gpo_cache') ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) check_refresh_gpo_list(self.server, self.lp, self.creds, gpos) self.assertTrue(os.path.exists(cache), 'GPO cache %s was not created' % cache) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpt_ini = os.path.join(cache, 'ADDOM.SAMBA.EXAMPLE.COM/POLICIES', guid, 'GPT.INI') self.assertTrue(os.path.exists(gpt_ini), 'GPT.INI was not cached for %s' % guid) def test_check_refresh_gpo_list_malicious_paths(self): # the path cannot contain .. path = '/usr/local/samba/var/locks/sysvol/../../../../../../root/' self.assertRaises(OSError, check_safe_path, path) self.assertEqual(check_safe_path('/etc/passwd'), 'etc/passwd') self.assertEqual(check_safe_path('\\\\etc/\\passwd'), 'etc/passwd') # there should be no backslashes used to delineate paths before = 'sysvol/addom.samba.example.com\\Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}\\GPT.INI' after = 'addom.samba.example.com/Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}/GPT.INI' result = check_safe_path(before) self.assertEquals(result, after, 'check_safe_path() didn\'t' ' correctly convert \\ to /') def test_gpt_ext_register(self): this_path = os.path.dirname(os.path.realpath(__file__)) samba_path = os.path.realpath(os.path.join(this_path, '../../../')) ext_path = os.path.join(samba_path, 'python/samba/gp_sec_ext.py') ext_guid = '{827D319E-6EAC-11D2-A4EA-00C04F79F83A}' ret = register_gp_extension(ext_guid, 'gp_sec_ext', ext_path, smb_conf=self.lp.configfile, machine=True, user=False) self.assertTrue(ret, 'Failed to register a gp ext') gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid in gp_exts.keys(), 'Failed to list gp exts') self.assertEquals(gp_exts[ext_guid]['DllName'], ext_path, 'Failed to list gp exts') unregister_gp_extension(ext_guid) gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid not in gp_exts.keys(), 'Failed to unregister gp exts') self.assertTrue(check_guid(ext_guid), 'Failed to parse valid guid') self.assertFalse(check_guid('AAAAAABBBBBBBCCC'), 'Parsed invalid guid') lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue(lp and parser, 'parse_gpext_conf() invalid return') parser.add_section('test_section') parser.set('test_section', 'test_var', ext_guid) atomic_write_conf(lp, parser) lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue('test_section' in parser.sections(), 'test_section not found in gpext.conf') self.assertEquals(parser.get('test_section', 'test_var'), ext_guid, 'Failed to find test variable in gpext.conf') parser.remove_section('test_section') atomic_write_conf(lp, parser)
class SambaOptions(optparse.OptionGroup): """General Samba-related command line options.""" def __init__(self, parser): from samba.param import LoadParm optparse.OptionGroup.__init__(self, parser, "Samba Common Options") self.add_option("-s", "--configfile", action="callback", type=str, metavar="FILE", help="Configuration file", callback=self._load_configfile) self.add_option("-d", "--debuglevel", action="callback", type=int, metavar="DEBUGLEVEL", help="debug level", callback=self._set_debuglevel) self.add_option("--option", action="callback", type=str, metavar="OPTION", help="set smb.conf option from command line", callback=self._set_option) self.add_option("--realm", action="callback", type=str, metavar="REALM", help="set the realm name", callback=self._set_realm) self._configfile = None self._lp = LoadParm() def get_loadparm_path(self): """Return the path to the smb.conf file specified on the command line. """ return self._configfile def _load_configfile(self, option, opt_str, arg, parser): self._configfile = arg def _set_debuglevel(self, option, opt_str, arg, parser): self._lp.set('debug level', str(arg)) def _set_realm(self, option, opt_str, arg, parser): self._lp.set('realm', arg) def _set_option(self, option, opt_str, arg, parser): if arg.find('=') == -1: print("--option takes a 'a=b' argument") sys.exit(1) a = arg.split('=') self._lp.set(a[0], a[1]) def get_loadparm(self): """Return a loadparm object with data specified on the command line. """ if self._configfile is not None: self._lp.load(self._configfile) elif os.getenv("SMB_CONF_PATH") is not None: self._lp.load(os.getenv("SMB_CONF_PATH")) else: self._lp.load_default() return self._lp def get_hostconfig(self): return Hostconfig(self.get_loadparm())
class GPOTests(tests.TestCase): def setUp(self): super(GPOTests, self).setUp() self.server = os.environ["SERVER"] self.dc_account = self.server.upper() + '$' self.lp = LoadParm() self.lp.load_default() self.creds = self.insta_creds(template=self.get_credentials()) def tearDown(self): super(GPOTests, self).tearDown() def test_gpo_list(self): global poldir, dspath ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' names = ['Local Policy', guid] file_sys_paths = [None, '%s\\%s' % (poldir, guid)] ds_paths = [None, 'CN=%s,%s' % (guid, dspath)] for i in range(0, len(gpos)): self.assertEqual( gpos[i].name, names[i], 'The gpo name did not match expected name %s' % gpos[i].name) self.assertEqual( gpos[i].file_sys_path, file_sys_paths[i], 'file_sys_path did not match expected %s' % gpos[i].file_sys_path) self.assertEqual( gpos[i].ds_path, ds_paths[i], 'ds_path did not match expected %s' % gpos[i].ds_path) def test_gpo_ads_does_not_segfault(self): try: ads = gpo.ADS_STRUCT(self.server, 42, self.creds) except: pass def test_gpt_version(self): global gpt_data local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpo_path = os.path.join(local_path, policies, guid) old_vers = gpo.gpo_get_sysvol_gpt_version(gpo_path)[1] with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % 42) self.assertEqual( gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], 42, 'gpo_get_sysvol_gpt_version() did not return the expected version') with open(os.path.join(gpo_path, 'GPT.INI'), 'w') as gpt: gpt.write(gpt_data % old_vers) self.assertEqual( gpo.gpo_get_sysvol_gpt_version(gpo_path)[1], old_vers, 'gpo_get_sysvol_gpt_version() did not return the expected version') def test_check_refresh_gpo_list(self): cache = self.lp.cache_path('gpo_cache') ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.creds.get_username()) check_refresh_gpo_list(self.server, self.lp, self.creds, gpos) self.assertTrue(os.path.exists(cache), 'GPO cache %s was not created' % cache) guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' gpt_ini = os.path.join(cache, policies, guid, 'GPT.INI') self.assertTrue(os.path.exists(gpt_ini), 'GPT.INI was not cached for %s' % guid) def test_check_refresh_gpo_list_malicious_paths(self): # the path cannot contain .. path = '/usr/local/samba/var/locks/sysvol/../../../../../../root/' self.assertRaises(OSError, check_safe_path, path) self.assertEqual(check_safe_path('/etc/passwd'), 'etc/passwd') self.assertEqual(check_safe_path('\\\\etc/\\passwd'), 'etc/passwd') # there should be no backslashes used to delineate paths before = 'sysvol/' + realm + '\\Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}\\GPT.INI' after = realm + '/Policies/' \ '{31B2F340-016D-11D2-945F-00C04FB984F9}/GPT.INI' result = check_safe_path(before) self.assertEqual( result, after, 'check_safe_path() didn\'t' ' correctly convert \\ to /') def test_gpt_ext_register(self): this_path = os.path.dirname(os.path.realpath(__file__)) samba_path = os.path.realpath(os.path.join(this_path, '../../../')) ext_path = os.path.join(samba_path, 'python/samba/gp_sec_ext.py') ext_guid = '{827D319E-6EAC-11D2-A4EA-00C04F79F83A}' ret = register_gp_extension(ext_guid, 'gp_access_ext', ext_path, smb_conf=self.lp.configfile, machine=True, user=False) self.assertTrue(ret, 'Failed to register a gp ext') gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid in gp_exts.keys(), 'Failed to list gp exts') self.assertEqual(gp_exts[ext_guid]['DllName'], ext_path, 'Failed to list gp exts') unregister_gp_extension(ext_guid) gp_exts = list_gp_extensions(self.lp.configfile) self.assertTrue(ext_guid not in gp_exts.keys(), 'Failed to unregister gp exts') self.assertTrue(check_guid(ext_guid), 'Failed to parse valid guid') self.assertFalse(check_guid('AAAAAABBBBBBBCCC'), 'Parsed invalid guid') lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue(lp and parser, 'parse_gpext_conf() invalid return') parser.add_section('test_section') parser.set('test_section', 'test_var', ext_guid) atomic_write_conf(lp, parser) lp, parser = parse_gpext_conf(self.lp.configfile) self.assertTrue('test_section' in parser.sections(), 'test_section not found in gpext.conf') self.assertEqual(parser.get('test_section', 'test_var'), ext_guid, 'Failed to find test variable in gpext.conf') parser.remove_section('test_section') atomic_write_conf(lp, parser) def test_gp_log_get_applied(self): local_path = self.lp.get('path', 'sysvol') guids = [ '{31B2F340-016D-11D2-945F-00C04FB984F9}', '{6AC1786C-016F-11D2-945F-00C04FB984F9}' ] gpofile = '%s/' + realm + '/Policies/%s/MACHINE/Microsoft/' \ 'Windows NT/SecEdit/GptTmpl.inf' stage = '[System Access]\nMinimumPasswordAge = 998\n' cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) for guid in guids: gpttmpl = gpofile % (local_path, guid) ret = stage_file(gpttmpl, stage) self.assertTrue(ret, 'Could not create the target %s' % gpttmpl) ret = gpupdate_force(self.lp) self.assertEqual(ret, 0, 'gpupdate force failed') gp_db = store.get_gplog(self.dc_account) applied_guids = gp_db.get_applied_guids() self.assertEqual(len(applied_guids), 2, 'The guids were not found') self.assertIn(guids[0], applied_guids, '%s not in applied guids' % guids[0]) self.assertIn(guids[1], applied_guids, '%s not in applied guids' % guids[1]) applied_settings = gp_db.get_applied_settings(applied_guids) for policy in applied_settings: self.assertIn('System Access', policy[1], 'System Access policies not set') self.assertIn('minPwdAge', policy[1]['System Access'], 'minPwdAge policy not set') if policy[0] == guids[0]: self.assertEqual(int(policy[1]['System Access']['minPwdAge']), days2rel_nttime(1), 'minPwdAge policy not set') elif policy[0] == guids[1]: self.assertEqual(int(policy[1]['System Access']['minPwdAge']), days2rel_nttime(998), 'minPwdAge policy not set') ads = gpo.ADS_STRUCT(self.server, self.lp, self.creds) if ads.connect(): gpos = ads.get_gpo_list(self.dc_account) del_gpos = get_deleted_gpos_list(gp_db, gpos[:-1]) self.assertEqual(len(del_gpos), 1, 'Returned delete gpos is incorrect') self.assertEqual(guids[-1], del_gpos[0][0], 'GUID for delete gpo is incorrect') self.assertIn('System Access', del_gpos[0][1], 'System Access policies not set for removal') self.assertIn('minPwdAge', del_gpos[0][1]['System Access'], 'minPwdAge policy not set for removal') for guid in guids: gpttmpl = gpofile % (local_path, guid) unstage_file(gpttmpl) ret = gpupdate_unapply(self.lp) self.assertEqual(ret, 0, 'gpupdate unapply failed') def test_process_group_policy(self): local_path = self.lp.cache_path('gpo_cache') guids = [ '{31B2F340-016D-11D2-945F-00C04FB984F9}', '{6AC1786C-016F-11D2-945F-00C04FB984F9}' ] gpofile = '%s/' + policies + '/%s/MACHINE/MICROSOFT/' \ 'WINDOWS NT/SECEDIT/GPTTMPL.INF' logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() # Initialize the group policy extension ext = gp_krb_ext(logger, self.lp, machine_creds, store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) stage = '[Kerberos Policy]\nMaxTicketAge = %d\n' opts = [100, 200] for i in range(0, 2): gpttmpl = gpofile % (local_path, guids[i]) ret = stage_file(gpttmpl, stage % opts[i]) self.assertTrue(ret, 'Could not create the target %s' % gpttmpl) # Process all gpos ext.process_group_policy([], gpos) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, opts[1], 'Higher priority policy was not set') # Remove policy gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, []) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, None, 'MaxTicketAge should not have applied') # Process just the first gpo ext.process_group_policy([], gpos[:-1]) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, opts[0], 'Lower priority policy was not set') # Remove policy ext.process_group_policy(del_gpos, []) for guid in guids: gpttmpl = gpofile % (local_path, guid) unstage_file(gpttmpl) def test_gp_scripts(self): local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' reg_pol = os.path.join(local_path, policies, guid, 'MACHINE/REGISTRY.POL') logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() # Initialize the group policy extension ext = gp_scripts_ext(logger, self.lp, machine_creds, store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) reg_key = b'Software\\Policies\\Samba\\Unix Settings' sections = { b'%s\\Daily Scripts' % reg_key: '.cron.daily', b'%s\\Monthly Scripts' % reg_key: '.cron.monthly', b'%s\\Weekly Scripts' % reg_key: '.cron.weekly', b'%s\\Hourly Scripts' % reg_key: '.cron.hourly' } for keyname in sections.keys(): # Stage the Registry.pol file with test data stage = preg.file() e = preg.entry() e.keyname = keyname e.valuename = b'Software\\Policies\\Samba\\Unix Settings' e.type = 1 e.data = b'echo hello world' stage.num_entries = 1 stage.entries = [e] ret = stage_file(reg_pol, ndr_pack(stage)) self.assertTrue(ret, 'Could not create the target %s' % reg_pol) # Process all gpos, with temp output directory with TemporaryDirectory(sections[keyname]) as dname: ext.process_group_policy([], gpos, dname) scripts = os.listdir(dname) self.assertEquals( len(scripts), 1, 'The %s script was not created' % keyname.decode()) out, _ = Popen([os.path.join(dname, scripts[0])], stdout=PIPE).communicate() self.assertIn(b'hello world', out, '%s script execution failed' % keyname.decode()) # Remove policy gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, []) self.assertEquals(len(os.listdir(dname)), 0, 'Unapply failed to cleanup scripts') # Unstage the Registry.pol file unstage_file(reg_pol) def test_gp_sudoers(self): local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' reg_pol = os.path.join(local_path, policies, guid, 'MACHINE/REGISTRY.POL') logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() # Initialize the group policy extension ext = gp_sudoers_ext(logger, self.lp, machine_creds, store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) # Stage the Registry.pol file with test data stage = preg.file() e = preg.entry() e.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Sudo Rights' e.valuename = b'Software\\Policies\\Samba\\Unix Settings' e.type = 1 e.data = b'fakeu ALL=(ALL) NOPASSWD: ALL' stage.num_entries = 1 stage.entries = [e] ret = stage_file(reg_pol, ndr_pack(stage)) self.assertTrue(ret, 'Could not create the target %s' % reg_pol) # Process all gpos, with temp output directory with TemporaryDirectory() as dname: ext.process_group_policy([], gpos, dname) sudoers = os.listdir(dname) self.assertEquals(len(sudoers), 1, 'The sudoer file was not created') self.assertIn(e.data, open(os.path.join(dname, sudoers[0]), 'r').read(), 'The sudoers entry was not applied') # Remove policy gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, []) self.assertEquals(len(os.listdir(dname)), 0, 'Unapply failed to cleanup scripts') # Unstage the Registry.pol file unstage_file(reg_pol) def test_gp_inf_ext_utf(self): logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() ext = gp_inf_ext(logger, self.lp, machine_creds, store) test_data = '[Kerberos Policy]\nMaxTicketAge = 99\n' with NamedTemporaryFile() as f: with codecs.open(f.name, 'w', 'utf-16') as w: w.write(test_data) try: inf_conf = ext.read(f.name) except UnicodeDecodeError: self.fail('Failed to parse utf-16') self.assertIn('Kerberos Policy', inf_conf.keys(), 'Kerberos Policy was not read from the file') self.assertEquals(inf_conf.get('Kerberos Policy', 'MaxTicketAge'), '99', 'MaxTicketAge was not read from the file') with NamedTemporaryFile() as f: with codecs.open(f.name, 'w', 'utf-8') as w: w.write(test_data) inf_conf = ext.read(f.name) self.assertIn('Kerberos Policy', inf_conf.keys(), 'Kerberos Policy was not read from the file') self.assertEquals(inf_conf.get('Kerberos Policy', 'MaxTicketAge'), '99', 'MaxTicketAge was not read from the file') def test_rsop(self): logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') local_path = self.lp.cache_path('gpo_cache') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) gp_extensions = [] gp_extensions.append(gp_krb_ext) gp_extensions.append(gp_scripts_ext) gp_extensions.append(gp_sudoers_ext) gp_extensions.append(gp_smb_conf_ext) gp_extensions.append(gp_msgs_ext) # Create registry stage data reg_pol = os.path.join(local_path, policies, '%s/MACHINE/REGISTRY.POL') reg_stage = preg.file() e = preg.entry() e.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Daily Scripts' e.valuename = b'Software\\Policies\\Samba\\Unix Settings' e.type = 1 e.data = b'echo hello world' e2 = preg.entry() e2.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Sudo Rights' e2.valuename = b'Software\\Policies\\Samba\\Unix Settings' e2.type = 1 e2.data = b'fakeu ALL=(ALL) NOPASSWD: ALL' e3 = preg.entry() e3.keyname = 'Software\\Policies\\Samba\\smb_conf\\apply group policies' e3.type = 4 e3.data = 1 e3.valuename = 'apply group policies' e4 = preg.entry() e4.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Messages' e4.valuename = b'issue' e4.type = 1 e4.data = b'Welcome to \\s \\r \\l' reg_stage.num_entries = 4 reg_stage.entries = [e, e2, e3, e4] # Create krb stage date gpofile = os.path.join(local_path, policies, '%s/MACHINE/MICROSOFT/' \ 'WINDOWS NT/SECEDIT/GPTTMPL.INF') krb_stage = '[Kerberos Policy]\nMaxTicketAge = 99\n' for g in [g for g in gpos if g.file_sys_path]: ret = stage_file(gpofile % g.name, krb_stage) self.assertTrue( ret, 'Could not create the target %s' % (gpofile % g.name)) ret = stage_file(reg_pol % g.name, ndr_pack(reg_stage)) self.assertTrue( ret, 'Could not create the target %s' % (reg_pol % g.name)) for ext in gp_extensions: ext = ext(logger, self.lp, machine_creds, store) ret = ext.rsop(g) self.assertEquals( len(ret.keys()), 1, 'A single policy should have been displayed') # Check the Security Extension if type(ext) == gp_krb_ext: self.assertIn('Kerberos Policy', ret.keys(), 'Kerberos Policy not found') self.assertIn('MaxTicketAge', ret['Kerberos Policy'], 'MaxTicketAge setting not found') self.assertEquals(ret['Kerberos Policy']['MaxTicketAge'], '99', 'MaxTicketAge was not set to 99') # Check the Scripts Extension elif type(ext) == gp_scripts_ext: self.assertIn('Daily Scripts', ret.keys(), 'Daily Scripts not found') self.assertIn('echo hello world', ret['Daily Scripts'], 'Daily script was not created') # Check the Sudoers Extension elif type(ext) == gp_sudoers_ext: self.assertIn('Sudo Rights', ret.keys(), 'Sudoers not found') self.assertIn('fakeu ALL=(ALL) NOPASSWD: ALL', ret['Sudo Rights'], 'Sudoers policy not created') # Check the smb.conf Extension elif type(ext) == gp_smb_conf_ext: self.assertIn('smb.conf', ret.keys(), 'apply group policies was not applied') self.assertIn(e3.valuename, ret['smb.conf'], 'apply group policies was not applied') self.assertEquals(ret['smb.conf'][e3.valuename], e3.data, 'apply group policies was not set') # Check the Messages Extension elif type(ext) == gp_msgs_ext: self.assertIn('/etc/issue', ret, 'Login Prompt Message not applied') self.assertEquals(ret['/etc/issue'], e4.data, 'Login Prompt Message not set') unstage_file(gpofile % g.name) unstage_file(reg_pol % g.name) # Check that a call to gpupdate --rsop also succeeds ret = rsop(self.lp) self.assertEquals(ret, 0, 'gpupdate --rsop failed!') def test_gp_unapply(self): logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) gp_extensions = [] gp_extensions.append(gp_krb_ext) gp_extensions.append(gp_scripts_ext) gp_extensions.append(gp_sudoers_ext) # Create registry stage data reg_pol = os.path.join(local_path, policies, '%s/MACHINE/REGISTRY.POL') reg_stage = preg.file() e = preg.entry() e.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Daily Scripts' e.valuename = b'Software\\Policies\\Samba\\Unix Settings' e.type = 1 e.data = b'echo hello world' e2 = preg.entry() e2.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Sudo Rights' e2.valuename = b'Software\\Policies\\Samba\\Unix Settings' e2.type = 1 e2.data = b'fakeu ALL=(ALL) NOPASSWD: ALL' reg_stage.num_entries = 2 reg_stage.entries = [e, e2] # Create krb stage date gpofile = os.path.join(local_path, policies, '%s/MACHINE/MICROSOFT/' \ 'WINDOWS NT/SECEDIT/GPTTMPL.INF') krb_stage = '[Kerberos Policy]\nMaxTicketAge = 99\n' ret = stage_file(gpofile % guid, krb_stage) self.assertTrue(ret, 'Could not create the target %s' % (gpofile % guid)) ret = stage_file(reg_pol % guid, ndr_pack(reg_stage)) self.assertTrue(ret, 'Could not create the target %s' % (reg_pol % guid)) # Process all gpos, with temp output directory remove = [] with TemporaryDirectory() as dname: for ext in gp_extensions: ext = ext(logger, self.lp, machine_creds, store) if type(ext) == gp_krb_ext: ext.process_group_policy([], gpos) ret = store.get_int('kdc:user_ticket_lifetime') self.assertEqual(ret, 99, 'Kerberos policy was not set') elif type(ext) in [gp_scripts_ext, gp_sudoers_ext]: ext.process_group_policy([], gpos, dname) gp_db = store.get_gplog(machine_creds.get_username()) applied_settings = gp_db.get_applied_settings([guid]) for _, fname in applied_settings[-1][-1][str(ext)].items(): self.assertIn(dname, fname, 'Test file not created in tmp dir') self.assertTrue(os.path.exists(fname), 'Test file not created') remove.append(fname) # Unapply policy, and ensure policies are removed gpupdate_unapply(self.lp) for fname in remove: self.assertFalse(os.path.exists(fname), 'Unapply did not remove test file') ret = store.get_int('kdc:user_ticket_lifetime') self.assertNotEqual(ret, 99, 'Kerberos policy was not unapplied') unstage_file(gpofile % guid) unstage_file(reg_pol % guid) def test_smb_conf_ext(self): local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' reg_pol = os.path.join(local_path, policies, guid, 'MACHINE/REGISTRY.POL') logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) entries = [] e = preg.entry() e.keyname = 'Software\\Policies\\Samba\\smb_conf\\template homedir' e.type = 1 e.data = '/home/samba/%D/%U' e.valuename = 'template homedir' entries.append(e) e = preg.entry() e.keyname = 'Software\\Policies\\Samba\\smb_conf\\apply group policies' e.type = 4 e.data = 1 e.valuename = 'apply group policies' entries.append(e) e = preg.entry() e.keyname = 'Software\\Policies\\Samba\\smb_conf\\ldap timeout' e.type = 4 e.data = 9999 e.valuename = 'ldap timeout' entries.append(e) stage = preg.file() stage.num_entries = len(entries) stage.entries = entries ret = stage_file(reg_pol, ndr_pack(stage)) self.assertTrue(ret, 'Failed to create the Registry.pol file') with NamedTemporaryFile(suffix='_smb.conf') as f: copyfile(self.lp.configfile, f.name) lp = LoadParm(f.name) # Initialize the group policy extension ext = gp_smb_conf_ext(logger, lp, machine_creds, store) ext.process_group_policy([], gpos) lp = LoadParm(f.name) template_homedir = lp.get('template homedir') self.assertEquals(template_homedir, '/home/samba/%D/%U', 'template homedir was not applied') apply_group_policies = lp.get('apply group policies') self.assertTrue(apply_group_policies, 'apply group policies was not applied') ldap_timeout = lp.get('ldap timeout') self.assertEquals(ldap_timeout, 9999, 'ldap timeout was not applied') # Remove policy gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, []) lp = LoadParm(f.name) template_homedir = lp.get('template homedir') self.assertEquals(template_homedir, self.lp.get('template homedir'), 'template homedir was not unapplied') apply_group_policies = lp.get('apply group policies') self.assertEquals(apply_group_policies, self.lp.get('apply group policies'), 'apply group policies was not unapplied') ldap_timeout = lp.get('ldap timeout') self.assertEquals(ldap_timeout, self.lp.get('ldap timeout'), 'ldap timeout was not unapplied') # Unstage the Registry.pol file unstage_file(reg_pol) def test_gp_motd(self): local_path = self.lp.cache_path('gpo_cache') guid = '{31B2F340-016D-11D2-945F-00C04FB984F9}' reg_pol = os.path.join(local_path, policies, guid, 'MACHINE/REGISTRY.POL') logger = logging.getLogger('gpo_tests') cache_dir = self.lp.get('cache directory') store = GPOStorage(os.path.join(cache_dir, 'gpo.tdb')) machine_creds = Credentials() machine_creds.guess(self.lp) machine_creds.set_machine_account() # Initialize the group policy extension ext = gp_msgs_ext(logger, self.lp, machine_creds, store) ads = gpo.ADS_STRUCT(self.server, self.lp, machine_creds) if ads.connect(): gpos = ads.get_gpo_list(machine_creds.get_username()) # Stage the Registry.pol file with test data stage = preg.file() e1 = preg.entry() e1.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Messages' e1.valuename = b'motd' e1.type = 1 e1.data = b'Have a lot of fun!' stage.num_entries = 2 e2 = preg.entry() e2.keyname = b'Software\\Policies\\Samba\\Unix Settings\\Messages' e2.valuename = b'issue' e2.type = 1 e2.data = b'Welcome to \\s \\r \\l' stage.entries = [e1, e2] ret = stage_file(reg_pol, ndr_pack(stage)) self.assertTrue(ret, 'Could not create the target %s' % reg_pol) # Process all gpos, with temp output directory with TemporaryDirectory() as dname: ext.process_group_policy([], gpos, dname) motd_file = os.path.join(dname, 'motd') self.assertTrue(os.path.exists(motd_file), 'Message of the day file not created') data = open(motd_file, 'r').read() self.assertEquals(data, e1.data, 'Message of the day not applied') issue_file = os.path.join(dname, 'issue') self.assertTrue(os.path.exists(issue_file), 'Login Prompt Message file not created') data = open(issue_file, 'r').read() self.assertEquals(data, e2.data, 'Login Prompt Message not applied') # Unapply policy, and ensure the test files are removed gp_db = store.get_gplog(machine_creds.get_username()) del_gpos = get_deleted_gpos_list(gp_db, []) ext.process_group_policy(del_gpos, [], dname) data = open(motd_file, 'r').read() self.assertFalse(data, 'Message of the day file not removed') data = open(issue_file, 'r').read() self.assertFalse(data, 'Login Prompt Message file not removed') # Unstage the Registry.pol file unstage_file(reg_pol)
class AdminToolsTestCase(unittest.TestCase): def assertSeen(self, what, msg=None, timeout=10): try: self.at.await_text(what, timeout=timeout) except hecate.hecate.Timeout: pass self.assertRegex(self.at.screenshot(), what, msg) def assertNotSeen(self, what, msg=None, timeout=10): sleep(.5) slept = 0 while slept < timeout: slept += .1 if not re.search(what, self.at.screenshot()): break sleep(.1) self.assertNotRegex(self.at.screenshot(), what, msg) def press(self, msg): self.at.press(msg) sleep(.1) def __validate_kinit(self): out, _ = Popen(['klist'], stdout=PIPE, stderr=PIPE).communicate() m = re.findall(six.b('Ticket cache:\s*(.*)'), out) if len(m) != 1: return False self.creds.set_named_ccache(m[0].decode()) m = re.findall(six.b('Default principal:\s*(\w+)@([\w\.]+)'), out) if len(m) == 0: return False user, realm = m[0] self.creds.set_username(user.decode()) self.creds.set_domain(realm.decode()) with Popen(['klist', '-s'], stdout=PIPE, stderr=PIPE) as p: if p.wait() != 0: return False self.creds.set_kerberos_state(MUST_USE_KERBEROS) return True def get_password(self): if not self.creds.get_password(): self.creds.set_password( getpass('Password for %s: ' % self.creds.get_username())) return self.creds.get_password() def kinit(self): while not self.__validate_kinit(): print( 'Domain administrator credentials are required to run the test.' ) upn = '%s@%s' % (self.creds.get_username(), self.creds.get_domain( )) if self.creds.get_username() and self.creds.get_domain( ) else None username = input('Domain user principal name%s: ' % (' (%s)' % upn if upn else '')) if username: self.creds.set_username(username) else: self.creds.set_username(upn) self.creds.set_password( getpass('Password for %s: ' % self.creds.get_username())) kinit_for_gssapi(self.creds, None) def setUp(self): self.lp = LoadParm() try: self.lp.load_default() except RuntimeError: pass self.creds = Credentials() self.config = ConfigParser() self.config.read('.tcreds') if self.config.has_section('creds'): self.creds.set_username('%s@%s' % (self.config.get( 'creds', 'username'), self.config.get('creds', 'domain'))) self.creds.set_domain(self.config.get('creds', 'domain')) self.creds.set_password(self.config.get('creds', 'password')) kinit_for_gssapi(self.creds, None) self.kinit() self.lp.set('realm', self.creds.get_domain()) self.at = hecate.Runner("admin-tools", width=120, height=50) def tearDown(self): self.at.shutdown() if self.creds.get_password(): if not self.config.has_section('creds'): self.config.add_section('creds') self.config.set('creds', 'username', self.creds.get_username()) self.config.set('creds', 'domain', self.creds.get_domain()) self.config.set('creds', 'password', self.creds.get_password()) with open('.tcreds', 'w') as w: self.config.write(w)