"Fossies" - the Fresh Open Source Software Archive

Member "jailkit-2.21/py/jk_jailuser.in" (26 Aug 2019, 12045 Bytes) of package /linux/privat/jailkit-2.21.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "jk_jailuser.in": 2.20_vs_2.21.

    1 #!/usr/bin/python
    2 #
    3 #Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008 Olivier Sessink
    4 #All rights reserved.
    5 #
    6 #Redistribution and use in source and binary forms, with or without
    7 #modification, are permitted provided that the following conditions 
    8 #are met:
    9 #  * Redistributions of source code must retain the above copyright 
   10 #    notice, this list of conditions and the following disclaimer.
   11 #  * Redistributions in binary form must reproduce the above 
   12 #    copyright notice, this list of conditions and the following 
   13 #    disclaimer in the documentation and/or other materials provided 
   14 #    with the distribution.
   15 #  * The names of its contributors may not be used to endorse or 
   16 #    promote products derived from this software without specific 
   17 #    prior written permission.
   18 #
   19 #THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
   20 #"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
   21 #LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
   22 #FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
   23 #COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
   24 #INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
   25 #BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
   26 #LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
   27 #CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
   28 #LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
   29 #ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
   30 #POSSIBILITY OF SUCH DAMAGE.
   31 #
   32 
   33 from __future__ import print_function
   34 
   35 import os.path
   36 import grp
   37 import pwd
   38 import sys
   39 import getopt
   40 import string
   41 import shutil
   42 
   43 PREFIX='/usr'
   44 INIPREFIX='/etc/jailkit'
   45 LIBDIR='/usr/share/jailkit'
   46 sys.path.append(LIBDIR)
   47 import jk_lib
   48 
   49 def striptrailingslash(dir):
   50     if (dir[-1] == '/'):
   51         return dir[:-1]
   52     return dir
   53 
   54 def dirinjail(testdir, jail):
   55     if (testdir[-1]!= '/'):
   56             testdir = testdir+'/'
   57     return (jail == testdir[:len(jail)])
   58     
   59 
   60 def addgrouptojail(jail, group_id, user, config):
   61     jail = striptrailingslash(jail)
   62     gr = grp.getgrgid(group_id)
   63     if (not jk_lib.test_group_exist(gr.gr_name, jail+'/etc/group')):
   64         file = jail+'/etc/group'
   65         if (config['verbose'] == 1):
   66             print('adding group '+gr[0]+' to '+file)
   67         try:
   68             tmp = gr[0]+':x:'+str(gr[2])+':'
   69             if (type(user)==str and len(user)>0):
   70                 tmp = tmp + user
   71             tmp = tmp + '\n'
   72             fd = open(file, 'a')
   73             fd.write(tmp)
   74             fd.close()
   75         except IOError:
   76             sys.stderr.write('ERROR: failed to write group '+gr[0]+' to '+file+'\n')
   77             return 0
   78     return 1
   79 
   80 def addusertogroupinjail(jail, group_id, user, config):
   81     ret = addgrouptojail(jail, group_id, user, config)
   82     if (ret == 0):
   83         return 0
   84     try:
   85         file = jail+'/etc/group'
   86         fd = open(file, 'r+')
   87         line = fd.readline()
   88         if sys.version_info > (3, 0) and isinstance(line, bytes):                                               #Python 3
   89             line = line.decode('utf-8', 'replace')
   90         while (len(line)>0):
   91             splitted = line.split(':')
   92             if (len(splitted)==4 and int(splitted[2]) == group_id):
   93                 users = splitted[3][:-1].split(',')
   94                 if (user in users):
   95                     fd.close()
   96                     return 1
   97                 else :
   98                     if (config['verbose']):
   99                         print('Adding user '+user+' to group '+splitted[0])
  100                     pos = fd.tell()
  101                     buf = fd.read()
  102                     fd.seek(pos-len(line))
  103                     if (len(users)==1 and users[0] == ''):
  104                         users = [user]
  105                     else:
  106                         users.append(user)
  107                     tmp = splitted[0]+':x:'+splitted[2]+':'
  108                     tmp2 = ','.join(users)
  109                     tmp += tmp2+'\n'+buf
  110                     fd.write(tmp)
  111                     fd.close()
  112                     return 1
  113             line = fd.readline()
  114             if sys.version_info > (3, 0) and isinstance(line, bytes):                                               #Python 3
  115                 line = line.decode('utf-8', 'replace')
  116     except IOError:
  117         sys.stderr.write('ERROR: failed to add user '+user+' to group '+str(group_id)+' in '+file+'\n')
  118         return 0
  119     return 0
  120     
  121 def addusertojail(jail, user, shell, config):
  122     jail = striptrailingslash(jail)
  123     if (jk_lib.test_user_exist(user, jail+'/etc/passwd')):
  124         if (config['verbose']):
  125             sys.stderr.write('user '+user+' already exists in '+jail+'/etc/passwd\n')
  126         return 1
  127     pw = pwd.getpwnam(user)
  128     # the next check MUST include a trailing slash! (otherwise if a user
  129     # has a homedir that starts with the same string as the jail directory
  130     # the check has the wrong result)
  131     if (dirinjail(pw[5], jail)):
  132         if (pw[5][0:len(jail)+3] == jail+'/./'):
  133             jailhome = pw[5][len(jail)+2:]
  134         else:
  135             jailhome = pw[5][len(jail):]
  136     else:
  137         jailhome = pw[5]
  138     try:
  139         if (sys.platform[4:7] == 'bsd'):
  140             file = jail+'/etc/master.passwd'
  141             if (config['verbose'] == 1):
  142                 print('adding user '+user+' to '+file+' with shell '+shell)
  143             fd = open(file, 'a')
  144             fd.write(user+':x:'+str(pw[2])+':'+str(pw[3])+'::0:0:'+pw[4]+':'+jailhome+':'+shell+'\n')
  145             fd.close()
  146             # adding -u user might speed up the next command, but if the password files do not exist
  147             # yet (and jail/etc/spwd.db does not exist) this generates an error
  148             postcommand = 'pwd_mkdb -p -d '+jail+'/etc '+jail+'/etc/master.passwd && rm '+jail+'/etc/spwd.db'
  149         else:
  150         #if (sys.platform[:5] == 'linux'):
  151             file  = jail+'/etc/passwd'
  152             if (config['verbose'] == 1):
  153                 print('adding user '+user+' to '+file+' with shell '+shell)
  154             fd = open(file, 'a')
  155             fd.write(user+':x:'+str(pw[2])+':'+str(pw[3])+':'+pw[4]+':'+jailhome+':'+shell+'\n')
  156             fd.close()
  157             postcommand = None
  158     except IOError:
  159         sys.stderr.write('failed to write to '+file+'\n')
  160         return 0
  161     if (postcommand != None):
  162         ret = os.system(postcommand)
  163         if (ret != 0):
  164             sys.stderr.write('failed to execute '+postcommand+'\n')
  165             return 0
  166     return 1
  167 
  168 def moduser(user, home, shell=PREFIX+'/sbin/jk_chrootsh'):
  169     if (sys.platform[:7] == 'freebsd'):
  170         command = 'pw usermod '+user+' -d '+home+' -s '+shell
  171     elif (sys.platform[:5] == 'linux') or (sys.platform[:7] == 'openbsd') or (sys.platform[:6] == 'sunos5'):
  172         command = 'usermod -d '+home+' -s '+shell+' '+user
  173     else:
  174         sys.stderr.write('please report that user modding on platform '+sys.platform+' is not yet handled\n')
  175         sys.stderr.write('to the jailkit developers, and suggest which command is needed to modify users\n')
  176         return 0
  177     if (os.system(command)!=0):
  178         sys.stderr.write('failed to execute '+command+'\n')
  179         return 0
  180     return 1
  181 
  182 def jailuser(jail, user, movehome, config):
  183     pw = pwd.getpwnam(user)
  184     if (jail[-1] != '/'):
  185         jail = jail + '/'
  186     # add the user in the jail
  187     if (not addusertojail(jail, user, config['shell'], config)):
  188         sys.exit(2)
  189     # lookup the primary group and make sure it also exists in the jail
  190     if not addgrouptojail(jail, pw[3], None, config):
  191         return 0
  192     # look up all other groups
  193     groups = grp.getgrall()
  194     for gr in groups:
  195         if (user in gr.gr_mem):
  196             ret = addusertogroupinjail(jail, gr.gr_gid, user, config)
  197             if not ret:
  198                 return 0
  199     # change the shell and the homedir
  200     if (dirinjail(pw[5], jail)):
  201         # the home is within in the jail already, does it have the /./ sequence?
  202         if (pw[5][:len(jail)+2] == jail+'./'):
  203             newhome = pw[5]
  204             #print('no need to change home ',newhome)
  205         else:
  206             newhome = jail+'.'+pw[5][len(jail)-1:]
  207             #print('add jail separator, newhome=',newhome)
  208     else:
  209         newhome = jail+'.'+pw[5]
  210         #print('user not in jail, newhome=',newhome)
  211     newhome = striptrailingslash(newhome)
  212     oldhome = striptrailingslash(pw[5])
  213     if (oldhome != newhome or pw[6] != PREFIX+'/sbin/jk_chrootsh'):
  214         if (config['verbose'] == 1):
  215             print('modify user '+user+'; dir '+newhome+' and shell '+PREFIX+'/sbin/jk_chrootsh')
  216         if (not moduser(user,newhome,PREFIX+'/sbin/jk_chrootsh')):
  217             sys.stderr.write('failed to modify user '+user+'\n')
  218     else:
  219         if (config['verbose'] == 1):
  220             print('user '+user+' has a correct home directory and shell already')
  221     #move directory contents
  222     if (movehome == 1):
  223         if (oldhome == newhome):
  224             print('home directory '+oldhome+' is already inside the jail')
  225         else:
  226             if (not os.path.exists(oldhome)):
  227                 sys.stderr.write('home directory '+oldhome+' does not exist, nothing moved\n')
  228             else:
  229                 # test if the base directory for newhome exists
  230                 tmp = os.path.dirname(oldhome)
  231                 #tmp = jk_lib.nextpathup(oldhome)
  232                 jk_lib.create_parent_path(jail,tmp, config['verbose'], copy_permissions=1, allow_suid=0, copy_ownership=0)
  233                 if (config['verbose'] == 1):
  234                     print('Moving files from '+oldhome+' to '+newhome)
  235                 jk_lib.move_dir_with_permissions_and_owner(oldhome,newhome,(config['verbose']==1))
  236 
  237 def user_exists(user):
  238     try:
  239         pw= pwd.getpwnam(user)
  240         return 1
  241     except:
  242         return 0
  243     return 0
  244 
  245 def usage():
  246     print
  247     print('Usage: '+sys.argv[0]+' [OPTIONS] username [more usernames]')
  248     print()
  249     print(' -j | --jail= jaildir   : jail directory')
  250     print(' -v | --verbose        : verbose output')
  251     print(' -n | --noninteractive : no user interaction')
  252     print(' -s | --shell= shell    : set shell inside jail ('+PREFIX+'/sbin/jk_lsh default)')
  253     print(' -m | --move           : move home if home outside jail')
  254     print(' -h | --help           : this message')
  255     print()
  256 
  257 def testjail(jail, shell):
  258     if (type(jail) != str or len(jail)==0):
  259         return 0
  260     if (jail[-1:] == '/'):
  261         jail = jail[:-1]
  262     if (not os.path.exists(jail)):
  263         sys.stderr.write(jail+' does not exist\n')
  264         return 0
  265     if (not os.path.exists(jail+'/etc/passwd')):
  266         sys.stderr.write('invalid jail, '+jail+'/etc/passwd does not exist\n')
  267         return 0
  268     if (not os.path.exists(jail+shell)):
  269         sys.stderr.write('invalid shell, '+jail+shell+' does not exist\n')
  270         return 0
  271     if (not os.access(jail+shell,os.X_OK)):
  272         sys.stderr.write('invalid shell, '+jail+shell+' is not executable\n')
  273         return 0
  274     return 1
  275 
  276 def getjail(jail, config):
  277     """returns a jail that will have a slash appended"""
  278     while (1):
  279         if (type(jail) == str and len(jail)>0 and jail[0] != '/'):
  280             tmp = os.getcwd()+'/'+jail
  281             if (os.path.exists(tmp)):
  282                 jail = tmp
  283         if (type(jail) == str and len(jail)>0 and jail[-1] != '/'):
  284             jail=jail+'/'
  285         test = testjail(jail, config['shell'])
  286         if (test == 1):
  287             return jail
  288         else:
  289             if (not test):
  290                 jail = None
  291             if (jail == None):
  292                 if (config['interactive'] == 1):
  293                     if sys.version_info > (3, 0):                                               #Python 3
  294                         jail = input('enter jail directory: ')
  295                     else:
  296                         jail = raw_input('enter jail directory: ')
  297                 else:
  298                     sys.exit(33)
  299 
  300 def getmovehome(jail,user,config):
  301     pw = pwd.getpwnam(user)
  302     if (pw[5][0:len(jail)] == jail): # pw[5] == pw.pw_dir
  303         return 0
  304     print('home directory '+pw.pw_dir+' is not within '+jail+', move the directory contents?')
  305     if sys.version_info > (3, 0):                                               #Python 3
  306         tmp = input('[Y]/[n]')
  307     else:
  308         tmp = raw_input('[Y]/[n]')
  309     if (tmp == 'Y' or tmp == 'y' or tmp == ''):
  310         return 1
  311     return 0
  312 
  313 def main():
  314     try:
  315         opts, args = getopt.getopt(sys.argv[1:],"vs:j:nmh?",['help', 'verbose', 'shell=', 'nomove', 'move', 'jail='])
  316     except getopt.GetoptError:
  317         usage()
  318         sys.exit(1)
  319     config = {}
  320     config['verbose'] = 0
  321     config['interactive'] = 1
  322     config['movehome'] = -1 # -1 = interactive
  323     config['shell'] = PREFIX+'/sbin/jk_lsh' # default shell
  324     jail = None
  325     for o, a in opts:
  326         if o in ("-h", "-?", "--help"):
  327             usage()
  328             sys.exit()
  329         elif o in ("-v", "--verbose"):
  330             config['verbose'] = 1
  331         elif o in ('-s', '--shell'):
  332             config['shell'] = a
  333         elif o in ("-m", "--move"):
  334             config['movehome'] = 1
  335         elif o in ("-n", "--noninteractive"):
  336             config['interactive'] = 0
  337         elif o in ('-j', '--jail'):
  338             jail = a
  339     if (config['interactive'] == 0 and config['movehome'] == -1):
  340         config['movehome'] = 0
  341     if (len(args)==0):
  342         print
  343         print('aborted, no username specified')
  344         sys.exit(2)
  345     try:
  346         jail = getjail(jail,config)
  347         for username in args:
  348             if user_exists(username):
  349                 if (config['movehome'] == -1):
  350                     movehome = getmovehome(jail, username, config)
  351                 else:
  352                     movehome = config['movehome']
  353                 jailuser(jail, username, movehome, config)
  354             else:
  355                 print('user '+username+' does not exist')
  356     except KeyboardInterrupt:
  357         print
  358         print('aborted.. ')
  359         sys.exit(1)
  360 
  361 if __name__ == "__main__":
  362     main()
  363