#! /usr/bin/env python
# Copyright (C) 2005 Red Hat 
# see file 'COPYING' for use and warranty information
#
# semanage is a tool for managing SELinux configuration files
#
#    This program 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 program 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 program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA     
#                                        02111-1307  USA
#
#  
import commands, sys, os, pwd, string, getopt, pwd
from semanage import *;
class loginRecords:
	def __init__(self):
		self.sh=semanage_handle_create()
		self.semanaged=semanage_is_managed(self.sh)
		if self.semanaged:
			semanage_connect(self.sh)

	def add(self, name, sename, serange):
		(rc,k)=semanage_seuser_key_create(self.sh, name)
		(rc,exists)= semanage_seuser_exists(self.sh, k)
		if exists:
			raise ValueError("SELinux User %s mapping already defined" % name)
		try:
			pwd.getpwname(name)
		except:
			raise ValueError("Linux User %s does not exist" % name)
			
		(rc,u)= semanage_seuser_create(self.sh)
		semanage_seuser_set_name(self.sh, u, name)
		semanage_seuser_set_mlsrange(self.sh, u, serange)
		semanage_seuser_set_sename(self.sh, u, sename)
		semanage_begin_transaction(self.sh)
		semanage_seuser_add(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to add SELinux user mapping")

	def modify(self, name, sename="", serange=""):
		(rc,k)=semanage_seuser_key_create(self.sh, name)
		(rc,u)= semanage_seuser_query(self.sh, k)
		if rc !=0 :
			raise ValueError("SELinux user %s mapping is not defined." % name)
		if sename == "" and serange=="":
			raise ValueError("Requires, seuser or serange")
		if serange != "":
			semanage_seuser_set_mlsrange(self.sh, u, serange)
		if sename != "":
			semanage_seuser_set_sename(self.sh, u, sename)
		semanage_begin_transaction(self.sh)
		semanage_seuser_modify(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to modify SELinux user mapping")

		
	def delete(self, name):
		(rc,k)=semanage_seuser_key_create(self.sh, name)
		(rc,exists)= semanage_seuser_exists(self.sh, k)
		if rc !=0 :
			raise ValueError("SELinux user %s mapping is not defined." % name)
		semanage_begin_transaction(self.sh)
		semanage_seuser_del(self.sh, k)
		if semanage_commit(self.sh) != 0:
			raise ValueError("SELinux User %s mapping not defined" % name)
		
	def list(self):
		print "\n%-25s %-25s %-25s\n" % ("Login Name", "SELinux User", "MLS/MCS Range")
		(status, self.ulist, self.usize) = semanage_seuser_list(self.sh)
		for idx in range(self.usize):
			u=semanage_seuser_by_idx(self.ulist, idx)
			name=semanage_seuser_get_name(u)
			
			print "%-25s %-25s %-25s" % (name, semanage_seuser_get_sename(u), semanage_seuser_get_mlsrange(u))

class seluserRecords:
	def __init__(self):
		roles=[]
		self.sh=semanage_handle_create()
		self.semanaged=semanage_is_managed(self.sh)
		if self.semanaged:
			semanage_connect(self.sh)

	def add(self, name, roles, selevel, serange):
		(rc,k)=semanage_user_key_create(self.sh, name)
		(rc,exists)= semanage_user_exists(self.sh, k)
		if exists:
			raise ValueError("Seuser %s already defined" % name)
		(rc,u)= semanage_user_create(self.sh)
		semanage_user_set_name(self.sh, u, name)
		for r in roles:
			semanage_user_add_role(self.sh, u, r)
		semanage_user_set_mlsrange(self.sh, u, serange)
		semanage_user_set_mlslevel(self.sh, u, selevel)
		(rc,key) = semanage_user_key_extract(self.sh,u)
		semanage_begin_transaction(self.sh)
		semanage_user_add_local(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to add SELinux user")

		self.dict[name]=seluser(name, roles, selevel, serange)
		
	def modify(self, name, roles=[], selevel="", serange=""):
		(rc,k)=semanage_user_key_create(self.sh, name)
		(rc,exists)= semanage_user_exists(self.sh, k)
		if not exists:
			raise ValueError("user %s is not defined" % name)
		(rc,u)= semanage_user_query(self.sh, k)
		if rc !=0 :
			raise ValueError("User %s is not defined." % name)
		if len(roles) == 0  and serange=="" and selevel=="":
			raise ValueError("Requires, roles, level  or range")
		if serange != "":
			semanage_user_set_mlsrange(self.sh, u, serange)
		if selevel != "":
			semanage_user_set_mlslevel(self.sh, u, selevel)
		if len(roles) != 0:
			for r in roles:
				print r
				semanage_user_add_role(self.sh, u, r)
		semanage_begin_transaction(self.sh)
		semanage_user_modify_local(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to modify SELinux user")

		
	def delete(self, name):
		(rc,k)=semanage_user_key_create(self.sh, name)
		(rc,exists)= semanage_user_exists(self.sh, k)
		if not exists:
			raise ValueError("user %s is not defined" % name)
		semanage_begin_transaction(self.sh)
		semanage_user_del_local(self.sh, k)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Login User %s not defined" % name)
		
	def list(self):
		print "\n%-15s %-10s %-20s" % ("", "MLS/", "MLS/")
		print "%-15s %-10s %-15s %-20s\n" % ("SELinux User", "MCS Level", "MCS Range", "SELinux Roles")
		(status, self.ulist, self.usize) = semanage_user_list(self.sh)
		for idx in range(self.usize):
			u=semanage_user_by_idx(self.ulist, idx)
			name=semanage_user_get_name(u)
			(status, rlist, rlist_size) = semanage_user_get_roles(self.sh, u)
			roles=""

			if rlist_size:
				roles+=char_by_idx(rlist, 0)
				for ridx in range (1,rlist_size):
					roles+=" " + char_by_idx(rlist, ridx)
			print "%-15s %-10s %-15s %s" % (semanage_user_get_name(u), semanage_user_get_mlslevel(u), semanage_user_get_mlsrange(u), roles)

class portRecords:
	def __init__(self):
		self.dict={}
		self.sh=semanage_handle_create()
		self.semanaged=semanage_is_managed(self.sh)
		if self.semanaged:
			semanage_connect(self.sh)

	def add(self, name, type):
		(rc,k)=semanage_port_key_create(self.sh, name)
		(rc,exists)= semanage_port_exists(self.sh, k)
		if exists:
			raise ValueError("User %s already defined" % name)
		(rc,u)= semanage_port_create(self.sh)
		semanage_port_set_name(self.sh, u, name)
		semanage_port_set_mlsrange(self.sh, u, serange)
		semanage_port_set_sename(self.sh, u, sename)
		semanage_begin_transaction(self.sh)
		semanage_port_add(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to add port")

	def modify(self, name, type):
		(rc,k)=semanage_port_key_create(self.sh, name)
		(rc,u)= semanage_port_query(self.sh, k)
		if rc !=0 :
			raise ValueError("User %s is not defined." % name)
		if sename == "" and serange=="":
			raise ValueError("Requires, port or serange")
		if serange != "":
			semanage_port_set_mlsrange(self.sh, u, serange)
		if sename != "":
			semanage_port_set_sename(self.sh, u, sename)
		semanage_begin_transaction(self.sh)
		semanage_port_modify(self.sh, k, u)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Failed to add port")
		
	def delete(self, name):
		(rc,k)=semanage_port_key_create(self.sh, name)
		semanage_begin_transaction(self.sh)
		semanage_port_del(self.sh, k)
		if semanage_commit(self.sh) != 0:
			raise ValueError("Port %s not defined" % name)
		
	def list(self):
		(status, self.plist, self.psize) = semanage_port_list(self.sh)
		print "%-25s %s\n" % ("SELinux Port Name", "Port Number")
		for idx in range(self.psize):
			u=semanage_port_by_idx(self.plist, idx)
			name=semanage_port_get_name(u)
			print "%20s %d" % ( name, semanage_port_get_number(u))
			
if __name__ == '__main__':

	def usage(message=""):
		print '\
semanage user [-admsRrh] SELINUX_USER\n\
semanage login [-admsrh] LOGIN_NAME\n\
semanage port [-admth] SELINUX_PORT_NAME\n\
	-a, --add        Add a OBJECT record NAME\n\
	-d, --delete     Delete a OBJECT record NAME\n\
	-h, --help       display this message\n\
	-l, --list       List the OBJECTS\n\
	-m, --modify     Modify a OBJECT record NAME\n\
	-r, --range      MLS/MCS Security Range\n\
	-R, --roles      SELinux Roles (Separate by spaces)\n\
	-s, --seuser     SELinux user name\n\
	-t, --type       SELinux Type for the object\n\
	-v, --verbose    verbose output\n\
'
		print message
		sys.exit(1)
		
	def errorExit(error):
		sys.stderr.write("%s: " % sys.argv[0])
		sys.stderr.write("%s\n" % error)
		sys.stderr.flush()
		sys.exit(1)

	#
	# 
	#
	try:
		objectlist=("login", "user", "port")
		input=sys.stdin
		output=sys.stdout
		serange="s0"
		selevel="s0"
		roles=""
		seuser=""
		type=""
		add=0
		modify=0
		delete=0
		list=0
		if len(sys.argv) < 3:
			usage("Requires 2 or more arguments")
			
		object=sys.argv[1]
		if object not in objectlist:
			usage("%s not defined" % object)
			
		args=sys.argv[2:]
		gopts, cmds = getopt.getopt(args,
					    'adlhms:R:r:t:v',
					    ['add',
					     'delete',
					     'help',
					     'list', 
					     'modify',
					     'seuser=',
					     'range=',
					     'roles=',
					     'type=',
					     'verbose'
					     ])
		for o,a in gopts:
			if o == "-a" or o == "--add":
				if modify or delete:
					usage()
				add=1
				
			if o == "-d"  or o == "--delese":
				if modify or add:
					usage()
				delete=1
			if o == "-h" or o == "--help":
				usage()

			if o == "-m"or o == "--modify":
				if delete or add:
					usage()
				modify=1
				
			if o == "-r" or o == '--range':
				serange=a

			if o == "-R" or o == '--roles':
				roles=a

			if o == "-t" or o == "--type":
				type=a

			if o == "-l" or o == "--list":
				list=1

			if o == "-s" or o == "--seuser":
				seuser=a

			if o == "-v" or o == "--verbose":
				verbose=1

		if object == "login":
			OBJECT=loginRecords()

		if object == "user":
			OBJECT=seluserRecords()

		if object == "port":
			OBJECT=portRecords()
		
		if list:
			OBJECT.list()
			sys.exit(0);
			
		if len(cmds) != 1:
			usage()

		name=cmds[0]

		if add:
			if object == "login":
				OBJECT.add(name, seuser, serange)

			if object == "user":
				rlist=roles.split()
				print rlist
				OBJECT.add(name, rlist, selevel, serange)

			if object == "port":
				OBJECT.add(name, type)

			OBJECT.list()
			sys.exit(0);
			
		if modify:
			if object == "login":
				OBJECT.modify(name, seuser, serange)

			if object == "user":
				rlist=roles.split()
				print rlist
				OBJECT.modify(name, rlist, selevel, serange)

			if object == "port":
				OBJECT.modify(name, type)
				sys.exit(0);
			OBJECT.list()
			sys.exit(0);

		if delete:
			OBJECT.delete(name)
			sys.exit(0);
		usage()
			
	except getopt.error, error:
		errorExit("Options Error " + error.msg)
	except ValueError, error:
		errorExit(error.args[0])
	except IOError, error:
		errorExit(error.args[1])
	except KeyboardInterrupt, error:
		sys.exit(0)
