diff -urN cvs.orig/sbin/chroot.README cvs/sbin/chroot.README --- cvs.orig/sbin/chroot.README 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/chroot.README 2003-02-25 14:38:17.000000000 -0800 @@ -0,0 +1,13 @@ +Unix Client setup +================= + + To access to the project ${PROJECT}, the unix developers need to + set the environment variable CVS_RSH to ssh: + + + export CVS_RSH=ssh + + To download the latest sources from the project ${PROJECT}, they + may then use: + + cvs -d :ext:LOGIN@`hostname -f`:/cvs checkout MODULENAME diff -urN cvs.orig/sbin/common.py cvs/sbin/common.py --- cvs.orig/sbin/common.py 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/common.py 2003-02-28 00:13:59.000000000 -0800 @@ -0,0 +1,124 @@ +#!/usr/bin/env python +#****************************************************************************** +#FILE: common.py +#LANGUAGE: python +#SYSTEM: UNIX +#USER-INTERFACE: None +#DESCRIPTION +# This scripts defines variables common to the other +# chrooted thru ssh cvs administration scripts. +#USAGE +# import common +#AUTHORS +# Gregory M. Leblanc +# Pascal J. Bourguignon +#MODIFICATIONS +# 2003/02/09 Converted to python +# 2000/05/31 Creation. +#BUGS +#LEGAL +# Copyright (c) Gregory M. Lebkanc 2003 +# Copyright Pascal J. Bourguignon 2001 - 2001 +# +# This script is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; see the file COPYING.LIB. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#****************************************************************************** + +##------------------------------------------------------------## +## Start of customizations. +## + +base = '/var/cvs' +cvsadmu = 'fedoraadm' +cvsadmg = 'fedoraadmg' +cvsdevg = 'fedoradevs' + +#set this to the location where you installed these scripts. +pdir = '/home/gleblanc/projects/cvs/cvs/sbin' + +## +## End of Customizations +##------------------------------------------------------------## + + + +#Set the various exit status codes +ex_success = 0 +ex_syntax = 1 +ex_badparam = 2 +ex_unknown = 3 +ex_privilege = 4 +ex_install = 5 +ex_aborted = 6 +ex_error = 7 + +import grp +import pwd +import os + + +#function to check for the existance of a group +# +def group_exists(group): + #get all of the group data into groups + groups = grp.getgrall() + for i in range(0, len(groups)): + if groups[i][0] == group: + return 1 + return 0 + +#function to check for the existance of a user +# +def user_exists( username ): + retval = 0 + #get all of the users into users (this probably isn't memory + #friendly, but I don't really care + try: + user_list = pwd.getpwall() + except: + # we got an error of some nature + # retval still = 0 + print Error + pass + else: + #iterate through the list + for user in user_list: + # check the name if it matches break + if user[ 0 ] == username: + retval = 1 + break +# retval was initalized at 0 and will stay unless +# set to 1 by the looping construct + + +#function to return the unique values in a list +# +def uniq(l): + #the first one creates the result as an empty list. No idea on the + #second part FIXME + result = []; d = {} + #iterate over all of the items in the list l + for i in l: + #if d doesn't have the current item, append it to 'result' + if not d.has_key(i): + result.append(i); d[i] = 1 + #once we're done, return result, which contains a list of the uniq strings + return result + +#function to create directories, set ownerships, and set permissions +def makedirs_plus(path, owner, group, permissions): + os.makedirs(path) + os.chown(path, owner, group) + os.chmod(path, permissions) diff -urN cvs.orig/sbin/copy-passwd.py cvs/sbin/copy-passwd.py --- cvs.orig/sbin/copy-passwd.py 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/copy-passwd.py 2003-02-21 00:26:24.000000000 -0800 @@ -0,0 +1,133 @@ +#!/usr/bin/env python +#****************************************************************************** +#FILE: copy-passwd +#LANGUAGE: python +#SYSTEM: UNIX +#USER-INTERFACE: None +#DESCRIPTION +# This scripts copies information from /etc/passwd to the CVS project +# specific passwd file for the chrooted environment. +#USAGE +# copy-passwd [--no-login] PROJECT-NAME USER... +#AUTHORS +# Gregory M. Leblanc +# Pascal J. Bourguignon +#MODIFICATIONS +# 2003/02/20 Ported to Python +# 2000/05/31 Creation. +#BUGS +#LEGAL +# Copyright (c) Gregory M. Leblanc 2003 +# Copyright Pascal J. Bourguignon 2001 +# +# This script is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; see the file COPYING.LIB. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#****************************************************************************** +import os +import sys +import pwd +import re + +#import our custom functions +import common + +#set progname to the name of this executable +pname = os.path.basename(sys.argv[0]) + +#set pdir to the path to this executable +pdir = os.path.dirname(sys.argv[0]) + +def usage(): + print pname, " usage:" + print " ", pname, " [-h|--help|-n|--no-login] PROJECT-NAME USER-NAME..." + +#set nologin to false (allow logins) +nologin = 0 + +#project is empty +project = "" + +#users is empty +users = [] + +#remove the program name from the list of arguments +del sys.argv[0] + +#loop over the arguments +for arg in sys.argv: + #if the argument is -n or --no-login, set the nologin variable to true + if re.search("-n$", arg) or re.search("--no-login$", arg): + nologin = 1 + #otherwise, if it's -h or --help, print the help message and exit with + #a successful error code + elif re.search("-h$", arg) or re.search("--help$", arg): + usage() + sys.exit(common.ex_success) + #otherwise, if it start with - then it's an invalid option, so print + #a usage message and quit + elif re.search("^-", arg): + print pname, ": Invalid option ", arg + usage + sys.exit(common.ex_syntax) + #otherwise + else: + #if the project isn't set yet, use that argument as the project + if project == "": + project = arg + #if project is set, add the current argument to the list of users + else: + users.append(arg) + +#set projectdir to the base directory plus the name of the project +projectdir = os.path.join(common.base, project) + +#if the chrooted-cvs directory under the project director doesn't exist +#exit with an error +if not os.path.exists(os.path.join(projectdir, "chrooted-cvs")): + print pname, ": Project ", project, "does not exist." + sys.exit(common.ex_unknown) + +#if no usernames were provided, exit with an error +if users == "": + print pname, ": Missing user names." + usage + sys.exit(common.ex_syntax) + +#open the new password file so that we can append new users to it +newpasswd = file(os.path.join(projectdir, "chrooted-cvs/etc/passwd"), "a") + +#loop over all of the users provided +for user in users: + found = 0 + #loop over each line from the password database + for line in pwd.getpwall(): + #if the first field matches the username, print out the proper + #things to the password file + if line[0] == user: + found = 1 + if nologin: + user = "%s:x:%s:%s:%s:/tmp:/bin/false" % (line[0], line[2], + line[3], line[4]) + newpasswd.write(user) + else: + user = "%s:x:%s:%s:%s:%s:%s/bin/smrsh" % (line[0], line[2], + line[3], line[4], + projectdir) + #if we didn't find the user, print out a warning message + if not found: + print pname, ": There is no user named ", user + +#we're done, exit successfully! +sys.exit(common.ex_success) diff -urN cvs.orig/sbin/make-module.py cvs/sbin/make-module.py --- cvs.orig/sbin/make-module.py 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/make-module.py 2003-02-27 19:51:10.000000000 -0800 @@ -0,0 +1,173 @@ +#!/usr/bin/env python +#****************************************************************************** +#FILE: make-module.py +#LANGUAGE: python +#SYSTEM: UNIX +#DESCRIPTION +# This scripts creates modules into a project. +#USAGE +# make-user PROJECT-NAME MODULE... +#AUTHORS +# Gregory M Leblanc +# Pascal J. Bourguignon +#MODIFICATIONS +# 2003/02/07 Re-written in Python +# 2000/05/31 Creation. +#BUGS +#LEGAL +# Copyright (c) Gregory Leblanc 2003 +# Copyright Pascal J. Bourguignon 2001 - 2001 +# +# This script is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; see the file COPYING.LIB. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#****************************************************************************** +import sys +import os +import pwd +import grp +import re + +#add in common functions from common.py +import common + +#put the program name into pname +pname = sys.argv[0] + +#set cvsuid to the UID of the cvsadmu from common.py +# +#loop through all of the lines in /etc/passwd +for line in pwd.getpwall(): + #compare the first value in field to the valud of cvsadmu + if line[0] == common.cvsadmu: + #if it matches, set the cvsuid to the third field from that line + cvsuid = line[2] + #then quit + break + +#set uid to the current UID +uid = os.getuid() +#set cvsadmuid to the UID of the admin user from common +cvsadmuid = pwd.getpwnam(common.cvsadmu)[2] +#if we're not root +if not uid == 0: + #and we're not the cvs user + if not uid == cvsadmuid: + #display an error message + print pname, "You must be either root or ", common.cvsadmu, " to run this script." + #then exit + sys.exit(common.ex_privilege) + +#define the usage function +def usage(): + #print out a program usage statement + print pname, " usage:" + print " ", pname, "PROJECT-NAME MODULE-NAME..." + +#set project and modules to an empty string, and told to false +project = '' +modules = [] +told = 0 + +#delete the program name from sys.argv +del sys.argv[0] + +#loop over all of the command line arguments +for arg in sys.argv: + #if the current argument is asking for help + if re.search("-h$", arg) or re.search("--help$", arg): + #call the usage function + usage() + sys.exit(common.ex_success) + #if the argument starts with a - and is anything other than the above + elif re.search("^-", arg): + #tell the user that they've used an option we don't know about + print pname, ": Invalid option", argument + #if the argument contains something other than a letter of any case, a + #number, or one of _ + . = + elif re.search("^[^-A-Za-z0-9_+.=]{1,32}$", arg): + #tell the user that the current argument is an invalid module name + print pname, ": Bad module name", argument, ". Skipped." + #if we haven't already told the user, then + if not told: + #tell them what we expect for a module name + print pname, ": The module name should consist of letters, digits, \n or +-=_. only." + #and toggle that we've already told them + told = 'true' + #if the argument is anything else + else: + #and if project isn't set yet + if project == "": + #set the project to the current argument + project = arg + else: + #otherwise, append the current argument to the modules variable + modules.append(arg) + +if project == "": + usage() + sys.exit(common.ex_syntax) + +#create the project dir variable from the base location, project name, +#and 'chrootedcvs' +projectdir = os.path.join(common.base, project) + +#check to see if the project directory doesn't exist +if not os.path.exists(projectdir): + #if not, print an error and quit + print pname, ': Project ', project, ' does not exist.' + sys.exit(common.ex_unknown) + + +#search for the project name in /etc/group +# +if not common.group_exists(project): + #if it doesn't match, print an error and exit + print pname, ': Project ', project, ' does not exist.' + sys.exit(common.ex_unknown) + +#if there aren't any module names +if modules == '': + #print an error, usage statement, then exit + print pname, ': Missing module names.' + usage() + sys.exit(common.ex_syntax) + + +#create modules +# +#loop over the modules in modules, +for module in modules: + #set moduledir to the module directory + moduledir = os.path.join(projectdir, "chrooted-cvs/cvs", module) + #check to see if the module directory already exists + if os.path.exists(moduledir): + #if so, print that the module already exists + print pname, ': There is already a module named ', module, '. Ignored.' + else: + #print out a status message + print pname, ': Creating module named ', module + #create the directory + os.makedirs(moduledir,2775) + #change the group on the moduledir so that it matches the project + user = os.stat(moduledir)[4] + os.chown(moduledir,user,grp.getgrnam(project)[2]) + #if we're root + if uid == 0: + #change the ownership to the cvs admin user + os.chown(moduledir,pwd.getpwnam(common.cvsadmu)[2], + grp.getgrnam(project)[2]) + +#quit! We've done it! +sys.exit(common.ex_success) diff -urN cvs.orig/sbin/make-project.py cvs/sbin/make-project.py --- cvs.orig/sbin/make-project.py 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/make-project.py 2003-02-27 22:32:24.000000000 -0800 @@ -0,0 +1,495 @@ +#!/usr/bin/env python +#****************************************************************************** +#FILE: make-project +#LANGUAGE: python +#SYSTEM: UNIX +#USER-INTERFACE: None +#DESCRIPTION +# This script both initialize the setup for CVS repository accessed +# thru ssh in a chrooted environment, and initialize new CVS projects +# in this setup. +#USAGE +# make-project [-a|--allow-anonymous] PROJECT-NAME +#AUTHORS +# Pascal J. Bourguignon +# Gregory M. Leblanc +#MODIFICATIONS +# 2003/02/27 Converted to Python +# 2000/05/31 Creation. +#BUGS +# We should separate the setting of the base cvs environment +# from the creation of a new project. (it makes the script hideously more +# complex than is necessary, or sane to follow) +# +# The Python version of this script was developed on Red Hat Linux 8.x +# and hopefully -should- run on any platform with python 2.2. If you find +# any gross non-portabilities, please let me know so that I can fix them. +# +# Sorry, this script is being developed on Linux-SuSE-71. +# It will most probably need some adjusting to run flawlessly +# on other systems. Please contribute your changes. +# +#LEGAL +# Copyright (c) Gregory M. Leblanc 2003 +# Copyright Pascal J. Bourguignon 2001 +# +# This script is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; see the file COPYING.LIB. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#****************************************************************************** +import posix +import sys +import os +import atexit +import re +import shutil +import tempfile +import grp +import pwd +import commands + +#put the program name into pname +pname = os.path.basename(sys.argv[0]) +del sys.argv[0] + +#set first_time to false +first_time = 0 + +#add in common functions from common.py +import common + +#Get a good temporary file name +tmpfilename = tempfile.mktemp() +#open that file for writing +tmp = file(tmpfilename, 'w') + +#create the 'cleanup' function +def cleanup(): + #delete the temporary file + tmp.close() + #delete our tempfile + os.remove(tmpfilename) + +#set up the cleanup function so that it's run at program exit +atexit.register(cleanup) + +#create the usage function to print usage info +def usage(): + print pname, " usage:" + print " ", pname, "[-a|--allow-anonymous] PROJECT-NAME" + print "By default, the access rights are set up to allow read-write access" + print "only to the developers of the project." + print "The option --allow anonymous (-a) sets access rights to allow" + print "anonymous read access in addition." + + +#create the yes_no function, which prompts you to create (or not) create +#the project +def yes_no(project): + #set rep to a known value + rep = 'what' + #test for rep to be either y or n + while not rep == 'y' and not rep == 'n': + #print out a prompt and read the response into rep + rep = raw_input(pname, ": ", project, "(Y|n) ") + #if the response is one of y, Y, Yes, YES, or yes + if rep == 'y' or rep == 'Y' or rep == 'Yes' or rep == 'YES' or rep == 'yes': + #set rep to y + rep = 'y' + #otherwise, if rep is n, N, No, NO, or no + elif rep == 'n' or rep == 'N' or rep == 'No' or rep == 'NO' or rep == 'no': + #set rep to n + rep = 'n' + #if they typed anything else, boggle + else: + print pname, ': What?' + #print the value of rep + return rep + + +#argument parsing +# +#loop through the command line arguments (except the program name) +for i in range(0, len(sys.argv)): + #if it's -a or --allmatches + if re.search('-a$', sys.argv[i]) or re.search('--allow-anonymous$', + sys.argv[i]): + #set the umask + mask = '022' + #delete this argument + del sys.argv[i] + #if it's -h or --help + elif re.search('-h$', sys.argv[i]) or re.search('--help$', sys.argv[i]): + #print a usage message + usage() + #exit with success + sys.exit(common.ex_success) + #if it starts with - + elif re.search('^-',sys.argv[i]): + #then it's an invalid option + print pname, ": inalid option", arg + #print a usage message + usage() + #then exit + sys.exit(common.ex_syntax) + else: + #set the umask + mask = '027' + +#if project is blank +if len(sys.argv) < 1: + #print a specific error + print pname, ": Missing project name." + #print a usage message + usage() + #exit + sys.exit(common.ex_syntax) +else: + #set the project to the first argument + project = sys.argv[0] + #remove the first argument + del sys.argv[0] + +#see if the project name is valid. Any upper or lower case letter, number +#and _ are allowed, up to 32 characters long +if not re.search('^\w{1,32}$', project): + #if it's not, print some instructions then exit + print pname, ": Bad project name", project + print pname, ": I would prefer that the project name consist of" + print " one to 32 characters, upper or lower case letters, numbers, or _ only" + sys.exit(common.ex_badparam) + +#set projectdir to the proper directory for this project +projectdir = os.path.join(common.base, project) + +#check to see if the project directory exists +if os.path.exists(projectdir): + print pname, ": There is already a file named ", projectdir + print " Please choose another name." + sys.exit(common.ex_badparam) + +#if there are any arguments left, then they've provided too many! +if not len(sys.argv) == 0: + print pname, ": too many arguments." + usage() + sys.exit(common.ex_badparam) + +#if we're not root, exit since we can't work +if not posix.getuid() == 0: + print pname, ": You must be root in order to run ", pname + sys.exit(common.ex_privilege) + + +#if the project group already exists +if common.group_exists(project) == 'true': + print pname, ": A group named ", project, " already exists." + #ask if we should keep going anyway + if yes_no(project) == 'no': + print pname, ": Aborting." + sys.exit(common.ex_aborted) +#if the project group doesn't exist create it +else: + print pname, ": Creating the group ", project + os.system("groupadd %s" % project) + +#if the group for CVS developers (cvsdevg) doesn't exist, create it +if common.group_exists(common.cvsdevg) == "false": + print pname, ": Creating the group ", common.cvsdevg + os.system("groupadd %s" % common.cvsdevg) + +#put the GID for the develoeprs group into devgid +devgid = grp.getgrnam(common.cvsdevg)[2] + +#if the group for CVS administrators (cvsadmg) doesn't exist, create it +if common.group_exists(common.cvsadmg) == "false": + print pname, ": Creating the group ", common.cvsadmg + os.system("groupadd %s" % common.cvsadmg) + +#put the GID for the administrators group into admgid +admgid = grp.getgrnam(common.cvsadmg)[2] + +#if the CVS adminstrator user doesn't exist, create it, and set +#the first_time variable to true (1) +if common.user_exists(common.cvsadmu) == "false": + print pname, ": Creating the user ", common.cvsadmu + command = "useradd -g %s -d %s -c 'CVS administrator' -m %s" % ( + common.cvsadmg, common.base, common.cvsadmu) + os.system(command) + first_time = 1 + +#put the UID for the admistrative user into admuid +admuid = pwd.getpwnam(common.cvsadmu)[3] + +#set the umask to mask per above +os.umask(mask) + +#if the home directories path doesn't exist, create it, and set the permissions +if not os.path.exists(os.path.join(common.base, "home")): + print pname, ": Creating the ", os.path.join(common.base, "home"), " directory..." + os.makedirs(os.path.join(common.base, "home")) + os.chown(os.path.join(common.base, "home"),0,0) + +#create the directory heirarchy for the project, and set permissions +# +print pname, ": Creating the ", projectdir, " directory heirarchy..." + +#set chrootdir to the projectdir plus chrooted-cvs +chrootdir = os.path.join(projectdir, "chrooted-cvs") + +#create the cvs directory owned by root with 0755 permissions +common.makedirs_plus(chrootdir, 0, 0, 0755) + +#create the bin and smrsh.bin directories, change ownerships, and set +#permissions +common.makedirs_plus(os.path.join(chrootdir, "bin"), admuid, admgid, 0755) +common.makedirs_plus(os.path.join(chrootdir, "smrsh.bin"), admuid, admgid, 0755) + +#create dev, etc, and tmp directories +common.makedirs_plus(os.path.join(chrootdir, "dev"), 0, 0, 0755) +common.makedirs_plus(os.path.join(chrootdir, "etc"), 0, 0, 0755) +common.makedirs_plus(os.path.join(chrootdir, "tmp"), 0, 0, 1777) +#set the permissions on the tmp directory again, because 1777 in python +#isn't 1777 +command = "chmod 1777 %s" % (os.path.join(chrootdir, "tmp")) +os.system(command) + +#function to copy the CVS binaries and libraries in to place +def copy_cvs_bins(): + #make a temporary variable that contains the path part of ldd cvs + tmp_libs = [] + #loop over the lines in the output from 'ldd cvs' + for i in commands.getoutput("ldd `which cvs`").splitlines(): + #append that to the tmp_libs list + tmp_libs.append(i.split(" => ", 1)[1]) + #these two lines strip out the (0x...) crap. How? I dunno. When you + #figure it out, send me a patch with real docs here. + r = re.compile(r' \(0x.*\)') + libs = map (lambda a: r.sub ('', a), tmp_libs) + #add the path to the CVS binary to the libs list + libs.append(commands.getoutput('which cvs')) + + #create an empty dirs list + dirs = [] + #loop over the items in libs and create a 'dirs' list which contains a + #list of the directories that contain shared libraries for CVS + for item in libs: + #append just the path part of each item in libs to dirs + dirs.append(os.path.dirname(item)) + #remove any duplicates from dirs + dirs = common.uniq(dirs) + #loop over the items in dirs, and create all of those directories. + for item in dirs: + #create the directories in chrootdir + dirtomake = os.path.join(chrootdir, item.lstrip('/')) + common.makedirs_plus(dirtomake, admuid, admgid, 0755) + #loop over the items in libs again (this is probably wasteful, but I'm + #a lousy programmer, and don't know a better way) + for item in libs: + #copy from each entry in libs (it's on the root filesystem, no + #need to keep another copy around in the chroot, which is how it was + #done in the bash version of this script) to the project + #specific chroot + shutil.copyfile(item, os.path.join(chrootdir, item.lstrip('/'))) + +#call the copy_cvs_bins function that we just created +copy_cvs_bins() + +#find out the device numbers for /dev/null +# +#set rdev to the rdev number returned via stat +rdev = os.stat("/dev/null").st_rdev +#major number is rdev divided by 256 +major = rdev / 256 +#minor number is the remainder of rdev divided by 256 +minor = rdev % 256 +#set devnull to the correct path for /dev/null +devnull = os.path.join(chrootdir,"dev/null") +#create the command to create /dev/null +command = "mknod %s c %s %s" % (devnull, major, minor) +#actually create /dev/null +os.system(command) +#set permissions so that everyone can use it +os.chmod(devnull, 666) + +#set the cvsdir to 'cvs' within the chroot +cvsdir = os.path.join(chrootdir, "cvs") + +#create the cvs dir +common.makedirs_plus(cvsdir, admuid, admgid, 0770) +#create the cvs-locks dir. We need this so that we can set up ACLs +common.makedirs_plus(os.path.join(chrootdir, "cvs-locks"), admuid, devgid, 0775) + +#set etcgroup to the "fake" /etc/group within our chroot, and open it so +#that we can 'a'ppend to it. +etcgroup = file(os.path.join(chrootdir, "etc/group"),'a') +#loop over this list of groups +for group in ['root', common.cvsadmg, common.cvsdevg, project]: + #put all the group info into data + data = grp.getgrnam(group) + #reformat the group data into something suitable to write to disk + realdata = ':'.join(data[:2] + (','.join(data[3]),)) + #write it out to disk + etcgroup.write(realdata) + +#make sure root owns /etc/group +os.chown(os.path.join(chrootdir, "etc/group"), 0, 0) +#make sure that the permissions are sane +os.chmod(os.path.join(chrootdir, "etc/group"), 0644) + +#figure out the path to the copy-password.py script +copy_passwd = os.path.join(common.pdir, "copy-passwd.py") +#create the command to copy the root and cvs adminstrator from the system +#password file +command = "%s %s --no-login root %s" % (copy_passwd, project, common.cvsadmu) +#do the actual copies +os.system(command) + +#set 'srcdir' to the path where the smrsh source code lives (sbin is how I set +#things up in common. This is one hack to strip something off, instead one +#hack in several programs to tack something on. +srcdir = common.pdir.rstrip("sbin") +#'make' the binaries, and install them +command = "make -C %s/src BASE=%s PROJECT=%s install" % (srcdir, + common.base, + project) +os.system(command) + +#Time to create the CVS Repository +# +print pname, ": Creating the CVS Repository..." + +#set up the cvs init command +command = "%s -d %s init" % (os.path.join(chrootdir, + commands.getoutput('which cvs')), + cvsdir) +#actuall run cvs init +os.system(command) +#set up and run the command to create the val-tags file. +command = "touch %s" % (os.path.join(cvsdir, "CVSROOT/val-tags")) +os.system(command) + +#get the ownerships on the cvsdir correct, recursively (python's built-in +#doesn't have any recursion available +command = "chown -R %s.%s %s" % (admuid, admgid, cvsdir) +os.system(command) +#chmod the cvsdir so that it's useful, and the CVSROOT dir too +os.chmod(cvsdir, 0775) +os.chmod(os.path.join(cvsdir, "CVSROOT"), 0775) + +#set the permissions on the contents of the CVSROOT dir (again, python +#built-in sucks +command = "chmod 0444 %s/CVSROOT/*" % (cvsdir) +os.system(command) + +#change the ownerships on Emptydir +os.chmod(os.path.join(cvsdir, "CVSROOT/Emptydir"), 0775) + +#change ownerships on CVSROOT/history +os.chown(os.path.join(cvsdir, "CVSROOT/history"), admuid, devgid) +#permissions too. +os.chmod(os.path.join(cvsdir, "CVSROOT/history"), 0664) + +#err, that's probably totally unnecessary, because CVSROOT/history is a +#really really poor idea. I should make sure that I'm turning it off in +#this script, instead of making sure that it works. FIXME + +#set up and run the command to fix the ownerships on val-tags. I forget why +#I'm not using the builtin here +command = "chown %s.%s %s" % (admuid, common.cvsdevg, + os.path.join(cvsdir, "CVSROOT/val-tags")) +os.system(command) +#set the permissions on the val-tags file. +os.chmod(os.path.join(cvsdir, "CVSROOT/val-tags"), 0664) + +#set up the lock directory. Lots of fun +print pname, ": Setting up the Lock Directory..." +#change to the tmp directory. +os.chdir(os.path.join(chrootdir, "tmp")) +#don't drop permissions, because I couldn't make this work +#os.seteuid(admuid) + +#set up and run the command to check out the config file +command = "%s -d %s checkout CVSROOT/config" % ( + os.path.join(chrootdir, commands.getoutput('which cvs')), cvsdir) +os.system(command) +#open the config file, and set the LockDir to /cvs-locks (which will be +#effective within the chroot) +configfile = file(os.path.join(chrootdir, "tmp/CVSROOT/config"), + "w") +configfile.write('LockDir=/cvs-locks') + +#set up and run the command to commit our changes to CVS +command = "%s -d %s commit -m 'Set LockDir for chrooted environment.' CVSROOT/config " % (os.path.join(chrootdir, commands.getoutput('which cvs')), cvsdir) +os.system(command) + +#don't reclaim privs, because we never dropped them +#os.seteuid(0) + +#make sure we're in the chroot dir +os.chdir(os.path.join(chrootdir, "tmp")) +#delete our cvs checkout +shutil.rmtree('CVSROOT') + +#copy the 'chroot.README file to the project directory +shutil.copy(os.path.join(common.pdir, "chroot.README"), + os.path.join(projectdir, "README")) + +#check to see if this is the first project that's been created +if first_time = 1: + #set up some magic stuff to run this "help" script through 'more' + pager = os.popen('more', "w") + print >> pager, """ + +Administration +============== + + + The CVS Administrator can create new users (both unix and cvs) with: + + ${BASE}/sbin/make-user PROJECT USER... + + + He can add an existing unix user to a cvs project with: + + ${BASE}/sbin/copy-passwd PROJECT USER... + + + Only the CVS Administrator can create modules, with: + + ${BASE}/sbin/make-module PROJECT MODULE... + + +Sshd Configuration +================== + + Remember to set the option: + + UseLogin yes + + in sshd_config, otherwise sshd won't use the shell specified in + /etc/passwd. + + +Ssh User Setting +================ + + To let the user use cvs thru ssh without givin their password at + each invocation of cvs, just ask them for their identity.pub or + id_dsa.pub file and put it in their home directory in either + .ssh/authorized_keys or .ssh/authorized_keys2. + +""" + +#exit, we're done! +sys.exit(common.ex_success) diff -urN cvs.orig/sbin/make-user.py cvs/sbin/make-user.py --- cvs.orig/sbin/make-user.py 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/make-user.py 2003-02-27 23:01:58.000000000 -0800 @@ -0,0 +1,245 @@ +#!/usr/bin/env python +#****************************************************************************** +#FILE: make-user.py +#LANGUAGE: python +#SYSTEM: UNIX +#USER-INTERFACE: None +#DESCRIPTION +# This scripts creates new unix and cvs users. +#USAGE +# make-user PROJECT-NAME USER... +#AUTHORS +# Gregory M. Leblanc +# Pascal J. Bourguignon +#MODIFICATIONS +# 2003/02/27 Ported to Python +# 2000/05/31 Creation. +#BUGS +#LEGAL +# Copyright (c) Gregory M. Leblanc 2003 +# Copyright Pascal J. Bourguignon 2001 +# +# This script is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This script is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this library; see the file COPYING.LIB. +# If not, write to the Free Software Foundation, +# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#****************************************************************************** +import sys +import os +import shutil +import re +import atexit +import tempfile + +#set the program name +pname = os.path.basename(sys.argv[0]) +#delete the program name from the list of arguments +del sys.argv[0] + +#import our common variables and functions +import common + +#Create a function to print out the usage statement +# +def usage(): + #print out a program usage statement + print pname, "usage:\n" + print " ", pname, " [-h|--help] PROJECT-NAME \n" + print " [-r|--read-only|-w|--read-write] USER-NAME ..." + print "Default is --read-write." + + +#if we don't have two arguments +if len(sys.argv) < 2: + #print a usage message + usage() + #exit with a syntax error + sys.exit(common.ex_syntax) + +#if we're not root +if not os.getuid() == 0: + #print out a message saying that we need to be root + print pname, ": You must be root to run ", pname, "to be able to create the users." + #quit with an error about insufficient privileges + sys.exit(common.ex_privilege) + +#set the default access mode to read-write. Insecure, should be changed? +access = "rw" +#set users to and empty dictionary (so that usernames are coupled with their +#ability to write or read from CVS +users = {} +#default to not having told the end user what sorts of usernames we expect +told = 0 +#set project to an empty string +project = "" + + +#loop over the arguments to the program. +for arg in sys.argv: + #if it's -h or --help + if re.search("-h$", arg) or re.search("^--help$", arg): + #print the usage message + usage() + #exit successfully + sys.exit(common.ex_success) + #if it's -r or --read-only + elif re.search("^-r$", arg) or re.search("^--read-only$", arg): + #set access mode to read-only + access = "ro" + #if it's -w or --read-write + elif re.search("^-w$", arg) or re.search("--read-write$", arg): + #set access to read-write + access = "rw" + #if it starts with - but isn't one of the above + elif re.search("^-", arg): + #tell them we don't know about that option + print pname, ": Invalid option ", arg + #print a usage message + usage() + #and quit with a syntax error + sys.exit(common.ex_syntax) + #if they provided a kosher name for this argument + elif re.search("^[a-zA-Z0-9]{1,32}$", arg): + #if we don't have a project + if project == "": + #set one + project = arg + #otherwise we're dealing with a username + else: + #if the user already exists + if common.user_exists(arg): + #print an error about that user already existing. Perhaps + #a note about the copy-passwd.py script is in order + #in case they want to add a user to another project. + print pname, ": There is already a user named ", arg, ". Ignored." + #if the user doesn't exist + else: + #add this user to our list of users, and set their access mode + users[arg] = access + #if we didn't match anything above + else: + #and the project is unset + if project == "": + #set the project name to the first argument + project = arg + #otherwise, they provided a bad user name + else: + #print out a message of what sorts of usernames we understand + print pname, ": Bad user name ", arg, ". Skipped." + print " I would prefer that the username consist of two to\nthirty-two letters or numbers only.\n(If you don't like this, just edit me!)" + #note that we've already told the user what kinds of usernames + #we're expecting + told = 1 + +#set the projectdir properly +projectdir = os.path.join(common.base, project) + +#if the project's directory doesn't exist +if not os.path.exists(os.path.join(projectdir, "chrooted-cvs")): + #tell the user that it's missing + print pname, ": Project ", project, " does not exist!" + #exit saying that the project is unknown + sys.exit(common.ex_unknown) + +#if the group for the project doesn't exist +if not common.group_exists(project): + #print out an error message, and wonder if the user ran make-project + print pname, ": The group for ", project, " does not exist." + print " Did you run make-project ", project, "'?" + #exit saying that the project was unknown + sys.exit(common.ex_unknown) + +#if the group for CVS developers doesn't exist +if not common.group_exists(common.cvsdevg): + #print out an error about the developer group missing, and ask the user + #if they ran make project. + print pname, ": The group for CVS developers (", common.cvsdevg, ") does not exist." + print " Did you run 'make-project ", project, "'?" + #exit with an error about their install being bad. + sys.exit(common.ex_install) + +#create the cleanup function +def cleanup(): + #remove the 'skel' directory + shutil.rmtree(skel) + +#if they didn't provide any users +if users == {}: + #whine + print pname, ": Missing user names." + #exit with a syntax error + sys.exit(common.ex_syntax) +else: + #set skel to a useful temp file name + skel = tempfile.mktemp() + #create that directory, and a .ssh within it + os.makedirs(os.path.join(skel, ".ssh")) + #set the permissions to 700, so that ssh works + os.chmod(os.path.join(skel, ".ssh"), 0700) + #register our exit function + atexit.register(cleanup) + + #create an empty list for 'added users' + added_users = [] + + #loop through the users and access modes in our 'users' dictionary. + for user, access in users.items(): + #if it's a read-write user + if access == "rw": + #print out that it's a read-write user, and who it is + print pname, ": Adding read-write user ", user + #set their home directory + homedir = os.path.join(common.base, "home", user) + #set their shell to bin/smrsh + shell = os.path.join(common.base, project, "bin", "smrsh") + #set a comment that they're a developer on the project + comment = "'CVS Developer on project %s'" % (project) + #smear all that into the useradd command + command = "useradd -g %s -G %s -d %s -s %s -c %s -m -k %s %s" % ( + project, common.cvsdevg, homedir, shell, comment, skel, user) + #run the useradd command + os.system(command) + #add this user to the list of added_users + added_users.append(user) + #otherwise, it's a read-only user. + else: + #print that we're adding a read-only user, and who it is + print pname, ": Adding read-only user ", user + #set their home directory + homedir = os.path.join(common.base, "home", user) + #set their shell to bin/smrsh + shell = os.path.join(common.base, project, "bin", "smrsh") + #set a comment that they're a Visitor on the project + comment = "CVS Visitor on project %s" % (project) + #smear all that into the useradd command + command = "useradd -g %s -d %s -s %s -c %s -m -k %s %s" % ( + project, common.cvsdevg, homedir, shell, comment, skel, user) + #run the useradd command + os.system(command) + #add this user to the list of added_users + added_users.append = user + +#find out where the copy-password.py script lives +copy_passwd = os.path.join(common.pdir, "copy-passwd.py") +#copy all the entries over to the chroot +os.system(copy_passwd, added_users) + +#print out a message reminding folks to set up something so that +#users can actually log in +print pname, ": Don't forget to set passwords for the new users:" +print " ", added_users +print "\n" +print " Or better still, have the users send you their ssh key\n and put it into their ~/.ssh/authorized_keys file" + +#exit successfully! +sys.exit(common.ex_success) diff -urN cvs.orig/sbin/TODO.python cvs/sbin/TODO.python --- cvs.orig/sbin/TODO.python 1969-12-31 16:00:00.000000000 -0800 +++ cvs/sbin/TODO.python 2003-03-01 23:32:08.000000000 -0800 @@ -0,0 +1,13 @@ +The scripts here are my "First Go" at converting all of this this to +python, and thus there are a number of places for improvement. + +Use getopt for argument parsing + +Programs should have only one exit point. Same for procedures + +Split 'make-project' into two programs, one that is actually +'make-project', and the other 'initiate-server' or something like +that. (still working on the name) + +Finish documentation for functions. + diff -urN cvs.orig/src/Makefile cvs/src/Makefile --- cvs.orig/src/Makefile 2001-06-04 18:11:29.000000000 -0700 +++ cvs/src/Makefile 2003-03-01 23:22:55.000000000 -0800 @@ -41,16 +41,15 @@ gcc -static -o smrsh smrsh.o strl.o run-cvs:check_vars run-cvs.c - cc -static -O2 \ + gcc -static -O2 \ -DBASE=\"$(BASE)/$(PROJECT)/chrooted-cvs\" \ - -D$(SYSTEM)=1 \ run-cvs.c -o run-cvs install:check_vars all - mv smrsh $(BASE)/$(PROJECT)/bin/ - chown 0:0 $(BASE)/$(PROJECT)/bin/smrsh + mv smrsh $(BASE)/$(PROJECT)/chrooted-cvs/bin/ + chown 0:0 $(BASE)/$(PROJECT)/chrooted-cvs/bin/smrsh mv run-cvs $(BASE)/$(PROJECT)/smrsh.bin/cvs chown 0:0 $(BASE)/$(PROJECT)/smrsh.bin/cvs chmod 4755 $(BASE)/$(PROJECT)/smrsh.bin/cvs