Server IP : 195.201.23.43 / Your IP : 18.216.116.226 Web Server : Apache System : Linux webserver2.vercom.be 5.4.0-192-generic #212-Ubuntu SMP Fri Jul 5 09:47:39 UTC 2024 x86_64 User : kdecoratie ( 1041) PHP Version : 7.1.33-63+ubuntu20.04.1+deb.sury.org+1 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /sbin/ |
Upload File : |
#!/usr/bin/python3 # #Copyright (c) 2003, 2004, 2005, 2006 Olivier Sessink #All rights reserved. # #Redistribution and use in source and binary forms, with or without #modification, are permitted provided that the following conditions #are met: # * Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # * Redistributions in binary form must reproduce the above # copyright notice, this list of conditions and the following # disclaimer in the documentation and/or other materials provided # with the distribution. # * The names of its contributors may not be used to endorse or # promote products derived from this software without specific # prior written permission. # #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS #"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT #LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS #FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, #INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, #BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; #LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT #LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN #ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #POSSIBILITY OF SUCH DAMAGE. # from __future__ import print_function import sys if sys.version_info > (3, 0): #Python 3 from configparser import ConfigParser else: #Python 2 from ConfigParser import ConfigParser import os import string from stat import * import hashlib import getopt import random import stat INIPREFIX='/etc/jailkit' LIBDIR='/usr/share/jailkit' EXEPREFIX='/usr' sys.path.append(LIBDIR) import jk_lib def testchrootdir(config, path): if (config['verbose']): print("testing basedir "+path) jk_lib.chroot_is_safe(path) def md5Digest(filename, startAt): """Returns md5 digest of a 1024 bytes blobk in filename, at position startAt.""" t = 0 m = hashlib.md5() f=open(filename, 'rb') f.seek(startAt) buf = f.read(1024) f.close() m.update(buf) return m.digest() def file_in_list(thelist, file): for tmp in thelist: if (tmp == file): return 1 return 0 def filebasedir_in_list(thelist, file): for ipath in thelist: # print('is '+ipath+'=='+file[:len(ipath)]) if (file[:len(ipath)] == ipath): return 1 return 0 def testfilepermissions(config,path): try: statbuf = os.lstat(path) except OSError: sys.stderr.write('ERROR: cannot lstat() '+path+'\n') return 1 if (config['verbose']): print("testing file permissions for "+path) if (statbuf[ST_UID] == 0 or statbuf[ST_GID] == 0): # we have a root:root file if (statbuf[ST_MODE] & S_ISUID or statbuf[ST_MODE] & S_ISGID): # it is setuid or setgid if (statbuf[ST_MODE] & S_IXOTH): if (file_in_list(config['ignoresetuidexecuteforothers'], path)==0): print("WARNING: "+path+" is executable for others and setuid root!!") if (statbuf[ST_MODE] & S_IXGRP): if (file_in_list(config['ignoresetuidexecuteforgroup'], path)==0): print("WARNING: "+path+" is executable for group and setuid root!!") if (statbuf[ST_MODE] & S_IXUSR): if (file_in_list(config['ignoresetuidexecuteforuser'], path)==0): print("WARNING: "+path+" is executable for user and setuid root!!") def testdirpermissions(config,path): try: statbuf = os.lstat(path) except OSError: sys.stderr.write('ERROR: cannot lstat() '+path+'\n') return 1 if (config['verbose']): print("testing directory permissions for "+path) if (statbuf[ST_MODE] & S_IWOTH): if (filebasedir_in_list(config['ignorewritableforothers'], path) == 0): print("WARNING: "+path+" is writable for others!") if (statbuf[ST_MODE] & S_IWGRP): if (filebasedir_in_list(config['ignorewritableforgroup'], path) == 0): print("WARNING: "+path+" is writable for group!") def comparefiles(config,first, second): if (config['verbose']): print("comparing "+first+" and "+second) try: sb1 = os.lstat(first) sb2 = os.lstat(second) if (sb1[stat.ST_SIZE] != sb2[stat.ST_SIZE]): sys.stderr.write('ERROR: '+first+' and '+second+' have a different size!\n') return if (stat.S_ISLNK(sb1[stat.ST_MODE]) != stat.S_ISLNK(sb1[stat.ST_MODE])): print('ERROR: '+first+' and '+second+' are not both symlinks!') return if (stat.S_ISLNK(sb1[stat.ST_MODE])): if (os.readlink(first) != os.readlink(second)): print('ERROR: symlinks '+first+' and '+second+' point to different files!') return else: if (sb1[stat.ST_SIZE] > 1024): startAt = random.randint(0,sb1[stat.ST_SIZE]-1024) else: startAt = 0 if (md5Digest(first, startAt) != md5Digest(second, startAt)): print('ERROR: '+first+' and '+second+' are not the same!') except IOError: print('ERROR: cannot read '+first+' or '+second+' !') except OSError: print('ERROR: cannot stat() '+first+' or '+second+' !') def testchrootfiles(config, chroothome, path=''): try: if (not os.path.exists(chroothome+path)): print('cannot check files in '+chroothome+path+', it does not exist') return testdirpermissions(config,chroothome+path) for f in os.listdir(chroothome+path): chrootpath = chroothome+path+f if (filebasedir_in_list(config['ignorepatheverywhere'],chrootpath) == 0): if os.path.isfile(chrootpath): if (filebasedir_in_list(config['ignorepathoncompare'],chrootpath) == 0): realpath = '/'+path+f comparefiles(config,chrootpath, realpath) testfilepermissions(config,chrootpath) elif os.path.isdir(chrootpath) and not os.path.islink(chrootpath): testchrootfiles(config, chroothome, path+f+'/') else: if (config['verbose']): print("ignoring path "+chrootpath) except OSError: print('ERROR, failed to test any files in '+chroothome+path) def testjailusers(config, chroothome): if (os.path.exists(chroothome+'/etc/passwd')): jailep = open(chroothome+'/etc/passwd','r') realep = open('/etc/passwd','r') jline = jailep.readline() if sys.version_info > (3, 0) and isinstance(jline, bytes): #Python 3 jline = jline.decode('utf-8', 'replace') while (len(jline)>0): jpw = jline.split(':') if (jpw[0] == 'root'): if (config['verbose']): print('ignoring entry for user root in jail/etc/passwd') else: if (config['verbose']): print('comparing jailed user '+jpw[0]+' with the real user') # we test for the username, uid and primary gid found = 0 realep.seek(0) rline = realep.readline() if sys.version_info > (3, 0) and isinstance(rline, bytes): #Python 3 rline = rline.decode('utf-8', 'replace') while (len(rline)>0 and found ==0): rpw = rline.split(':') if (rpw[0] == jpw[0]): found = 1 if (rpw[2] != jpw[2] or rpw[3] != jpw[3]): print('ERROR: user '+rpw[0]+' is changed in jail '+chroothome) # now check if the jail is correct if (rpw[5] != chroothome+'.'+jpw[5]): print('ERROR: the real homedir and jail homedir for user '+jpw[0]+' do not correspond in jail '+chroothome) ## test for jk_lsh and test if the user/group is in the config file rline = realep.readline() if sys.version_info > (3, 0) and isinstance(rline, bytes): #Python 3 rline = rline.decode('utf-8', 'replace') if (found == 0): print('ERROR: user '+jpw[0]+' in jail '+chroothome+' does not exist on the real system') jline = jailep.readline() if sys.version_info > (3, 0) and isinstance(jline, bytes): #Python 3 jline = jline.decode('utf-8', 'replace') def testrealpasswd(): """reads the real /etc/passwd and looks for users that have a jail configured, returns a list of jails""" rep = open('/etc/passwd') rline = rep.readline() if sys.version_info > (3, 0) and isinstance(rline, bytes): #Python 3 rline = rline.decode('utf-8', 'replace') retval = [] while (len(rline)>0): rpw = rline.split(':') if (len(rpw)>=6): # test if the entry has any jail characteristics jaildot = rpw[5].find('/./') if (jaildot != -1): chroot = rpw[5][:jaildot+1] retval += [chroot] # test if the user actually exists in the chroot if (not jk_lib.test_user_exist(rpw[0], chroot+'/etc/passwd')): print('ERROR: user '+rpw[0]+' does not exist in '+chroot+'/etc/passwd') chrootsh = os.path.join(EXEPREFIX, 'sbin/jk_chrootsh') if (rpw[6].strip() != chrootsh): print('ERROR: user '+rpw[0]+' has a /./ but does not have the '+chrootsh+' shell') rline = rep.readline() if sys.version_info > (3, 0) and isinstance(rline, bytes): #Python 3 rline = rline.decode('utf-8', 'replace') return retval def get_list_option(cfgparser, sectionname, optionname): retval = [] if (cfgparser.has_option(sectionname,optionname)): inputstr = cfgparser.get(sectionname,optionname) for tmp in inputstr.split(','): retval += [tmp.strip()] return retval def activateConfig(configfile, verbose): cfg = ConfigParser() if (os.path.isfile(configfile)): cfg.read([configfile]) else: print('WARNING: '+configfile+' does not exist, only checking jails found in /etc/passwd') jails = testrealpasswd() for jail in jails: if ((jail not in cfg.sections()) and (jail[:-1] not in cfg.sections())): cfg.add_section(jail) for section in cfg.sections(): chrootdir = section if (chrootdir[-1:] != '/'): chrootdir += '/' config = {} config['verbose'] = verbose config['ignorepathoncompare'] = get_list_option(cfg,section, 'ignorepathoncompare') config['ignoresetuidexecuteforuser'] = get_list_option(cfg,section, 'ignoresetuidexecuteforuser') config['ignoresetuidexecuteforgroup'] = get_list_option(cfg,section, 'ignoresetuidexecuteforgroup') config['ignoresetuidexecuteforothers'] = get_list_option(cfg,section, 'ignoresetuidexecuteforothers') config['ignorewritableforothers'] = get_list_option(cfg,section, 'ignorewritableforothers') config['ignorewritableforgroup'] = get_list_option(cfg,section, 'ignorewritableforgroup') config['ignorepatheverywhere'] = get_list_option(cfg,section, 'ignorepatheverywhere') testchrootdir(config, chrootdir) testchrootfiles(config, chrootdir) testjailusers(config, chrootdir) def clean_exit(errno, message): print("** FAILURE **") print(message) print("") sys.exit(errno) def usage(): print("Usage: "+sys.argv[0]+" [OPTIONS]") print("") print("-h --help : this help screen") print("-c, --configfile=FILE : specify configfile location") print("-v, --verbose : show what is being tested") print("") def main(): try: opts, args = getopt.getopt(sys.argv[1:], "vhc:", ["help", "configfile=", "verbose"]) except getopt.GetoptError: usage() sys.exit(1) configfile = INIPREFIX+'/jk_check.ini' verbose = 0 for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-c", "--configfile"): configfile = a if o in ("-v", "--verbose"): verbose = 1 activateConfig(configfile, verbose) if __name__ == "__main__": main()Private