remove lshell

There is security related issue with lshell, and it is not
maintained now. So remove it from our system to avoid
security issue.

To remove lshell:
1. Package sudo-config is created for wrs.sudo configure file
following the refactor process.
2. ldapusersetup in ldapscripts is modified to use bash only.
lshell support is removed.

ldapusersetup related patches are merged into 1 for easy
maintenance.

Test has been done:
Build and deploy test is done, also unit tests for ldap are
executed with pass, except lshell related test.

Closes-Bug: 1795451

Change-Id: Ia5de1bc94d22eb6c9bea6d9a96e92564ad848b19
Signed-off-by: slin14 <shuicheng.lin@intel.com>
This commit is contained in:
slin14 2018-10-16 19:43:58 +08:00
parent d7d8b3cf06
commit 6a6ea416e1
23 changed files with 297 additions and 971 deletions

View File

@ -1,25 +0,0 @@
From 30a087a13a78b77537a969db2a30b531246b0bd7 Mon Sep 17 00:00:00 2001
From: Don Penney <don.penney@windriver.com>
Date: Mon, 26 Sep 2016 17:39:58 -0400
Subject: [PATCH] Update package versioning for TIS format
---
SPECS/lshell.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/SPECS/lshell.spec b/SPECS/lshell.spec
index 0fd4d17..e5f1317 100644
--- a/SPECS/lshell.spec
+++ b/SPECS/lshell.spec
@@ -2,7 +2,7 @@
Name: lshell
Version: 0.9.16
-Release: 6%{?dist}
+Release: 5.el7%{?_tis_dist}.%{tis_patch_ver}
Summary: A Python-based limited shell
License: GPLv3+
--
1.8.3.1

View File

@ -1,3 +0,0 @@
spec-include-TiS-changes.patch
spec-update-lshell-conf-allowed-list.patch
0001-Update-package-versioning-for-TIS-format.patch

View File

@ -1,87 +0,0 @@
lshell.spec: to include Titanium Cloud changes
To include the Titanium Cloud specific changes from:
stx/stx-integ/base/lshell
diff -u b/SPECS/lshell.spec b/SPECS/lshell.spec
--- b/SPECS/lshell.spec
+++ b/SPECS/lshell.spec
@@ -1,3 +1,5 @@
+%define WRSROOT_P cBglipPpsKwBQ
+
Name: lshell
Version: 0.9.16
Release: 5%{?dist}
@@ -6,6 +8,15 @@
License: GPLv3+
URL: https://github.com/ghantoos/lshell
Source0: http://downloads.sourceforge.net/%{name}/%{name}-%{version}.tar.gz
+Source1: cgcs_cli
+Source2: lshell.conf
+Source3: wrs.sudo
+Source4: lshell_env_setup
+Patch1: lshell_cgcs.patch
+Patch2: lshell-source-support.patch
+Patch3: lshell-prompt-change-support.patch
+Patch4: lshell-newline-escape-character-support.patch
+Patch5: lshell-shell-escape-check.patch
BuildArch: noarch
BuildRequires: python2-devel
@@ -20,6 +31,11 @@
%setup -q
#Fix permission
chmod -x CHANGES
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+%patch4 -p1
+%patch5 -p1
%build
%{__python2} setup.py build
@@ -27,10 +43,25 @@
%install
%{__python2} setup.py install -O1 --skip-build --root=%{buildroot}
# Doc files at the wrong place
-rm %{buildroot}%{_defaultdocdir}/lshell/{CHANGES,COPYING,README}
+rm -f %{buildroot}%{_defaultdocdir}/lshell/{CHANGES,COPYING,README}
+mkdir -p ${RPM_BUILD_ROOT}/usr/local/bin
+install -m 755 ${RPM_SOURCE_DIR}/cgcs_cli ${RPM_BUILD_ROOT}/usr/local/bin/cgcs_cli
+install -m 755 ${RPM_SOURCE_DIR}/lshell_env_setup ${RPM_BUILD_ROOT}/usr/local/bin/lshell_env_setup
+install -d ${RPM_BUILD_ROOT}/etc
+install -m 644 ${RPM_SOURCE_DIR}/lshell.conf ${RPM_BUILD_ROOT}/etc/lshell.conf
+install -d ${RPM_BUILD_ROOT}/etc/sudoers.d
+cp ${RPM_SOURCE_DIR}/wrs.sudo wrs.sudo
+echo 'Defaults passprompt="Password: "' >> wrs.sudo
+install -m 440 wrs.sudo ${RPM_BUILD_ROOT}/etc/sudoers.d/wrs
%pre
getent group lshell >/dev/null || groupadd -r lshell
+getent group wrs >/dev/null || groupadd -r wrs
+getent group wrs_protected >/dev/null || groupadd -f -g 345 wrs_protected
+getent passwd wrsroot > /dev/null || \
+useradd -m -g wrs -G root,wrs_protected \
+ -d /home/wrsroot -p %{WRSROOT_P} \
+ -s /bin/sh wrsroot 2> /dev/null || :
%post
grep -q '^%{_bindir}/%{name}$' %{_sysconfdir}/shells || \
@@ -42,13 +73,13 @@
fi
%files
-%doc CHANGES COPYING README
-%{_mandir}/man*/*.*
%{_bindir}/%{name}
%config(noreplace) %{_sysconfdir}/%{name}.conf
-%config(noreplace) %{_sysconfdir}/logrotate.d/%{name}
+%config(noreplace) %{_sysconfdir}/sudoers.d/wrs
%{python_sitelib}/lshell/
%{python_sitelib}/%{name}*.egg-info
+/usr/local/bin/cgcs_cli
+/usr/local/bin/lshell_env_setup
%changelog
* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 0.9.16-5

View File

@ -1,15 +0,0 @@
---
lshell.spec | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/SPECS/lshell.spec
+++ b/SPECS/lshell.spec
@@ -2,7 +2,7 @@
Name: lshell
Version: 0.9.16
-Release: 5%{?dist}
+Release: 6%{?dist}
Summary: A Python-based limited shell
License: GPLv3+

View File

@ -1 +0,0 @@
mirror:Source/lshell-0.9.16-5.el7.src.rpm

View File

@ -1,2 +0,0 @@
#!/bin/sh
/usr/bin/lshell

View File

@ -1,53 +0,0 @@
---
lshell/shellcmd.py | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
--- a/lshell/shellcmd.py
+++ b/lshell/shellcmd.py
@@ -74,6 +74,7 @@ class ShellCmd(cmd.Cmd, object):
self.promptbase = getuser()
self.prompt = '%s:~$ ' % self.promptbase
+ self.prompt2 = '> ' # PS2 prompt
self.intro = self.conf['intro']
@@ -670,6 +671,12 @@ class ShellCmd(cmd.Cmd, object):
self.stdout.write("%s\n" % self.intro)
if self.conf['login_script']:
self.loginCmdParse(self.conf['login_script'])
+
+ # for long commands, a user may escape the new line
+ # by giving a bash like '\' character at the end of
+ # the line. cmdloop() needs to recognize that and
+ # create an appended line before sending it to onecmd()
+ partial_line = ""
stop = None
while not stop:
if self.cmdqueue:
@@ -691,7 +698,24 @@ class ShellCmd(cmd.Cmd, object):
line = 'EOF'
else:
line = line[:-1] # chop \n
- line = self.precmd(line)
+
+ if len(line) > 1 and line.startswith('\\'):
+ # implying previous partial line
+ line = line[:1].replace('\\', '', 1)
+ if partial_line:
+ line = partial_line + line
+ if line.endswith('\\'):
+ # continuation character. First partial line.
+ # We shall expect the command to continue in
+ # a new line. Change to bash like PS2 prompt to
+ # indicate this continuation to the user
+ partial_line = line.strip('\\')
+ self.prompt = self.prompt2 # switching to PS2
+ continue
+ partial_line = ""
+
+ self.updateprompt(os.getcwd())
+ line = self.precmd(line)
stop = self.onecmd(line)
stop = self.postcmd(stop, line)
self.postloop()

View File

@ -1,139 +0,0 @@
---
lshell/shellcmd.py | 77 ++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 71 insertions(+), 6 deletions(-)
--- a/lshell/shellcmd.py
+++ b/lshell/shellcmd.py
@@ -28,6 +28,7 @@ import readline
import glob
import subprocess
+from time import gmtime, strftime
from utils import get_aliases
@@ -50,6 +51,9 @@ class ShellCmd(cmd.Cmd, object):
else:
self.stderr = stderr
+ # create a devnull device
+ self.devnull = open(os.devnull, 'w')
+
self.args = args
self.conf = userconf
self.log = self.conf['logpath']
@@ -145,13 +149,63 @@ class ShellCmd(cmd.Cmd, object):
self.g_cmd, self.g_arg, self.g_line = ['', '', '']
return object.__getattribute__(self, attr)
+ def check_prompt(self, var, value):
+ """ check if user is attempting to
+ modify shell prompt and if so then
+ update the prompt
+ """
+ if 'PS' in var:
+ if 'PS1' in var:
+ # update prompt
+ self.promptbase = self.setprompt(
+ {'prompt' : value.strip('\n').strip('\r')})
+ self.updateprompt(os.getcwd())
+ else:
+ self.log.critical("*** forbidden %s prompt change requested. "
+ "Only PS1 changes permissible" % var)
+
+
def setprompt(self, conf):
""" set prompt used by the shell
"""
if conf.has_key('prompt'):
promptbase = conf['prompt']
- promptbase = promptbase.replace('%u', getuser())
- promptbase = promptbase.replace('%h', os.uname()[1].split('.')[0])
+ # Recognize shell name control command
+ promptbase = re.sub(r'\\s', 'lshell',
+ promptbase)
+ # Recognize username control command
+ promptbase = re.sub(r'\\u|%u', getuser(),
+ promptbase)
+ # Recognize hostname control command
+ promptbase = re.sub(r'\\h|%h', os.uname()[1].split('.')[0],
+ promptbase)
+ # Recognize full hostname control command
+ promptbase = re.sub(r'\\H', os.uname()[1],
+ promptbase)
+ # Recognize time control commands
+ promptbase = re.sub(r'\\t', strftime("%H:%M:%S", gmtime()),
+ promptbase)
+ promptbase = re.sub(r'\\T', strftime("%I:%M:%S", gmtime()),
+ promptbase)
+ promptbase = re.sub(r'\\A', strftime("%H:%M", gmtime()),
+ promptbase)
+ promptbase = re.sub(r'\\@', strftime("%I:%M:%S%p", gmtime()),
+ promptbase)
+ promptbase = re.sub(r'\\d', strftime("%a %b %d", gmtime()),
+ promptbase)
+ ########################################################
+ # The following control commands are not supported: #
+ # v - the shell version #
+ # V - the shell release version #
+ # w - Complete path of current working directory #
+ # W - the basename of the current working directory #
+ # ! - the history number of this command #
+ # # - the command number of this command #
+ # $? - status of the last command #
+ # $() - any command executions #
+ ########################################################
+ promptbase = re.sub(r'\\v|\\V|\\w|\\W|\\!|\\#|\\\$\?|\\\$\(.*\)|\\\$', '',
+ promptbase)
else:
promptbase = getuser()
@@ -199,7 +253,7 @@ class ShellCmd(cmd.Cmd, object):
def export(self):
""" export environment variables """
# if command contains at least 1 space
- if self.g_line.count(' '):
+ if self.g_line.count(' '):
env = self.g_line.split(" ", 1)[1]
# if it conatins the equal sign, consider only the first one
if env.count('='):
@@ -216,6 +270,10 @@ class ShellCmd(cmd.Cmd, object):
cin, cout = os.popen2('`which echo` %s' % value)
value = cout.readlines()[0]
+ # check if new exported environment
+ # is a prompt change command
+ self.check_prompt(var, value)
+
os.environ.update({var: value.rstrip()})
def source(self):
@@ -485,11 +543,14 @@ class ShellCmd(cmd.Cmd, object):
p = subprocess.Popen( "`which echo` %s" % item,
shell=True,
stdin=subprocess.PIPE,
- stdout=subprocess.PIPE )
+ stdout=subprocess.PIPE,
+ stderr = self.devnull )
(cin, cout) = (p.stdin, p.stdout)
except ImportError:
- cin, cout = os.popen2('`which echo` %s' % item)
- item = cout.readlines()[0].split(' ')[0].strip()
+ cin, cout = os.popen2('`which echo` %s 2>/dev/null' % item)
+ shellresponse = cout.readlines()
+ if shellresponse:
+ item = shellresponse[0].split(' ')[0].strip()
item = os.path.expandvars(item)
tomatch = os.path.realpath(item)
if os.path.isdir(tomatch) and tomatch[-1] != '/': tomatch += '/'
@@ -559,6 +620,10 @@ class ShellCmd(cmd.Cmd, object):
if len(env) is not 2:
continue
newenv.update(dict([env]))
+ # check if the new environment includes
+ # any Shell prompt change commands
+ self.check_prompt(env[0], env[1])
+
os.environ.update(newenv)
def loginCmdParse(self, script):

View File

@ -1,121 +0,0 @@
---
lshell/shellcmd.py | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 77 insertions(+), 3 deletions(-)
--- a/lshell/shellcmd.py
+++ b/lshell/shellcmd.py
@@ -30,7 +30,7 @@ import subprocess
from time import gmtime, strftime
from utils import get_aliases
-
+from distutils.spawn import find_executable
class ShellCmd(cmd.Cmd, object):
""" Main lshell CLI class
@@ -337,6 +337,44 @@ class ShellCmd(cmd.Cmd, object):
# strip all spaces/tabs
line = " ".join(line.split())
+ # Expand all variables
+ line = os.path.expandvars(line)
+
+ # *** AWK HOOK *** #
+ # Before we begin, check if user is trying
+ # to pass an awk script to the awk interpreter
+ # and disallow that option.
+ #
+ # Also disallow inline vars in awk since an attacker
+ # may use that to scramble a forbidden cmd
+ # such as the following shell escape:
+ # (awk -v X=ba -v Y=ash 'BEGIN { system("/bin/"X Y) }'
+ #
+ # In an ideal world we should parse the awk script
+ # and inline vars for forbidden paths and commands
+ # but that will require some gnarly regexes (esp for
+ # the inline vars). Deferring this as TODO
+ if re.match(r'\s*awk.*-f\s*[\w/~]+', line):
+ return self.warn_count('awk script option', oline, strict, ssh)
+ if re.match(r'\s*awk.*-v\s*\w+=', line):
+ return self.warn_count('awk inline variable option', oline, strict, ssh)
+
+
+ # process all quoted text seperately
+ # This logic is kept crudely simple on purpose.
+ # At most we might match the same stanza twice
+ # (for e.g. "'a'", 'a') but the converse would
+ # require detecting single quotation stanzas
+ # nested within double quotes and vice versa
+ relist = re.findall(r'[^=]\"(.+)\"',line)
+ relist2 = re.findall(r'[^=]\'(.+)\'',line)
+ relist = relist + relist2
+ for item in relist:
+ if self.check_secure(item, strict = strict):
+ return 1
+ if self.check_path(item, strict = strict):
+ return 1
+
# ignore quoted text
line = re.sub(r'\"(.+?)\"', '', line)
line = re.sub(r'\'(.+?)\'', '', line)
@@ -438,7 +476,8 @@ class ShellCmd(cmd.Cmd, object):
new_cmd_line = 'export ' + oline
self.g_line = new_cmd_line
self.check_secure(new_cmd_line, strict = strict)
- else:
+ # filter out macros, text or constructs that got picked up as commands
+ elif command.islower() and find_executable(command):
return self.warn_count('command', oline, strict, ssh, command)
return 0
@@ -499,6 +538,7 @@ class ShellCmd(cmd.Cmd, object):
%(self.conf['warning_counter']))
self.stderr.write('This incident has been reported.\n')
+
def check_path(self, line, completion=None, ssh=None, strict=None):
""" Check if a path is entered in the line. If so, it checks if user \
are allowed to see this path. If user is not allowed, it calls \
@@ -594,7 +634,41 @@ class ShellCmd(cmd.Cmd, object):
detect the new environment and then use that to update the \
environ of the lshell process.
"""
- pipe = subprocess.Popen("%s; env -0" % script,
+ try:
+ script_path = os.path.expanduser(script.\
+ strip("source").split()[0])
+ script_path = os.path.expandvars(script_path)
+ with open (script_path) as fd:
+ content = fd.readlines()
+ content = [line.strip('\n') for line in content]
+
+ # Although rare in a normal cases, an attacker
+ # may attempt to bypass line validation by
+ # scrambling commands via line continuations
+ partial_line = ""
+ for i,line in enumerate(content):
+ if line.startswith('#'):
+ continue
+ if len(line) > 1 and line.startswith('\\'):
+ # implying previous partial line
+ content[i] = line[:1].replace('\\', '', 1)
+ if partial_line:
+ content[i] = partial_line + line
+ if line.endswith('\\'):
+ # continuation character. First partial line.
+ # We shall expect the command to continue in
+ # a new line.
+ partial_line = content[i].strip('\\')
+ continue
+ partial_line = ""
+ if self.check_secure(content[i]):
+ return
+ if self.check_path(content[i]):
+ return
+ except:
+ pass
+
+ pipe = subprocess.Popen("%s; env -0" % script,
bufsize=1,
stdout=subprocess.PIPE,
shell=True)

View File

@ -1,106 +0,0 @@
---
lshell/shellcmd.py | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 64 insertions(+), 1 deletion(-)
--- a/lshell/shellcmd.py
+++ b/lshell/shellcmd.py
@@ -26,6 +26,7 @@ import re
import signal
import readline
import glob
+import subprocess
from utils import get_aliases
@@ -128,6 +129,9 @@ class ShellCmd(cmd.Cmd, object):
# builtin export function
elif self.g_cmd == 'export':
self.export()
+ # builtin source function
+ elif self.g_cmd == 'source':
+ self.source()
# case 'cd' is in an alias e.g. {'toto':'cd /var/tmp'}
elif self.g_line[0:2] == 'cd':
self.g_cmd = self.g_line.split()[0]
@@ -214,6 +218,14 @@ class ShellCmd(cmd.Cmd, object):
os.environ.update({var: value.rstrip()})
+ def source(self):
+ """ implementation of the "source" command
+ """
+ # ensure if command contains at least 1 space
+ if self.g_line.count(' '):
+ source_script = self.g_line
+ self.sourceShell(source_script)
+
def cd(self):
""" implementation of the "cd" command
"""
@@ -515,6 +527,57 @@ class ShellCmd(cmd.Cmd, object):
else:
self.prompt = '%s:%s$ ' % (self.promptbase, path)
+ def sourceShell(self, script):
+ """Source the shell script and call env when done in order to \
+ detect the new environment and then use that to update the \
+ environ of the lshell process.
+ """
+ pipe = subprocess.Popen("%s; env -0" % script,
+ bufsize=1,
+ stdout=subprocess.PIPE,
+ shell=True)
+
+ iterator = iter(pipe.stdout.readline, b'')
+ outputlist = list(iterator)
+ output = ''
+ for i, line in enumerate(outputlist):
+ if i == (len(outputlist) -1):
+ output = line
+ else:
+ sys.stdout.write(line)
+
+ # output may pick up some echos at the end of script and merge
+ # with the first line in env. Test for this and echo those to stdout
+ envList = output.split('\0')
+ firstenv = re.findall('^\S+=\S+$', envList[0], re.MULTILINE)
+ if firstenv:
+ print envList[0].strip(firstenv[0])
+ envList[0] = firstenv[0]
+ newenv = {}
+ for line in envList:
+ env = line.split("=", 1)
+ if len(env) is not 2:
+ continue
+ newenv.update(dict([env]))
+ os.environ.update(newenv)
+
+ def loginCmdParse(self, script):
+ """Parse the login command specified in login_script. \
+ If login_script or a sub script sources a bash config \
+ then call shell_source()
+ """
+ # if multiple commands are chained together, execute
+ # them individually. We will not support conditional
+ # chaining (&& or ||) since that would required the
+ # additional complexity of checking the retcode of
+ # the previous command
+ cmds = script.split(";")
+ for cmd in cmds:
+ if "source" in cmd:
+ self.sourceShell(cmd)
+ else:
+ os.system(cmd)
+
def cmdloop(self, intro=None):
"""Repeatedly issue a prompt, accept input, parse an initial prefix \
off the received input, and dispatch to action methods, passing them \
@@ -541,7 +604,7 @@ class ShellCmd(cmd.Cmd, object):
if self.intro and isinstance(self.intro, str):
self.stdout.write("%s\n" % self.intro)
if self.conf['login_script']:
- os.system(self.conf['login_script'])
+ self.loginCmdParse(self.conf['login_script'])
stop = None
while not stop:
if self.cmdqueue:

View File

@ -1,94 +0,0 @@
# lshell.py configuration file
#
# $Id: lshell.conf,v 1.27 2010-10-18 19:05:17 ghantoos Exp $
[global]
## log directory (default /var/log/lshell/ )
logpath : /var/log/lshell/
## set log level to 0, 1, 2, 3 or 4 (0: no logs, 1: least verbose,
## 4: log all commands)
loglevel : 2
## configure log file name (default is %u i.e. username.log)
#logfilename : %y%m%d-%u
#logfilename : syslog
## in case you are using syslog, you can choose your logname
#syslogname : myapp
[default]
## a list of the allowed commands or 'all' to allow all commands in user's PATH
allowed : ['source','vim','awk','cut','grep','cat','env','export', 'read', 'pwd','ls','echo','cd','ll','less','cp','scp','sftp','mv','rm','nova','system','neutron','cinder','glance','ceilometer','heat','keystone','passwd','openstack']
## a list of forbidden character or commands -- deny vim, as it allows to escape lshell
#forbidden : [';', '&', '|','`','>','<', '$(', '${']
forbidden : [';', '&', '>','<', '$(']
## a list of allowed command to use with sudo(8)
#sudo_commands : ['ls', 'more']
## number of warnings when user enters a forbidden value before getting
## exited from lshell, set to -1 to disable.
warning_counter : 2
## command aliases list (similar to bashs alias directive)
aliases : {'ll':'ls -l', 'vim':'rvim'}
## introduction text to print (when entering lshell)
#intro : "== My personal intro ==\nWelcome to lshell\nType '?' or 'help' to get the list of allowed commands"
## configure your promt using %u or %h (default: username)
prompt : "%u@%h"
## set sort prompt current directory update (default: 0)
#prompt_short : 0
## a value in seconds for the session timer
timer : 900
## list of path to restrict the user "geographicaly"
#path : ['/home/bla/','/etc']
## set the home folder of your user. If not specified the home_path is set to
## the $HOME environment variable
#home_path : '/home/bla/'
## update the environment variable $PATH of the user
#env_path : ':/usr/local/bin:/usr/sbin'
## a list of path; all executable files inside these path will be allowed
#allowed_cmd_path: ['/home/bla/bin','/home/bla/stuff/libexec']
## add environment variables
#env_vars : {'foo':1, 'bar':'helloworld'}
env_vars : {'OPENRC_TEMPLATE':'/etc/nova/ldap_openrc_template'}
## allow or forbid the use of scp (set to 1 or 0)
#scp : 1
## forbid scp upload
#scp_upload : 0
## forbid scp download
#scp_download : 0
## allow of forbid the use of sftp (set to 1 or 0)
#sftp : 1
## list of command allowed to execute over ssh (e.g. rsync, rdiff-backup, etc.)
#overssh : ['ls', 'rsync']
## logging strictness. If set to 1, any unknown command is considered as
## forbidden, and user's warning counter is decreased. If set to 0, command is
## considered as unknown, and user is only warned (i.e. *** unknown synthax)
strict : 0
## force files sent through scp to a specific directory
#scpforce : '/home/bla/uploads/'
## history file maximum size
history_size : 100
## set history file name (default is /home/%u/.lhistory)
#history_file : "/home/%u/.lshell_history"
## define the script to run at user login
login_script : "source /usr/local/bin/lshell_env_setup --mute; install -m 0500 /usr/local/bin/lshell_env_setup ~/"

View File

@ -1,54 +0,0 @@
Index: lshell-0.9.16/setup.py
===================================================================
--- lshell-0.9.16.orig/setup.py
+++ lshell-0.9.16/setup.py
@@ -40,10 +40,7 @@ choose a list of allowed commands for ev
scripts = ['bin/lshell'],
package_dir = {'lshell':'lshell'},
packages = ['lshell'],
- data_files = [('/etc', ['etc/lshell.conf']),
- ('/etc/logrotate.d', ['etc/logrotate.d/lshell']),
- ('share/doc/lshell',['README', 'COPYING', 'CHANGES']),
- ('share/man/man1/', ['man/lshell.1']) ],
+ data_files = [],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console'
Index: lshell-0.9.16/lshell/shellcmd.py
===================================================================
--- lshell-0.9.16.orig/lshell/shellcmd.py
+++ lshell-0.9.16/lshell/shellcmd.py
@@ -199,7 +199,7 @@ class ShellCmd(cmd.Cmd, object):
env = self.g_line.split(" ", 1)[1]
# if it conatins the equal sign, consider only the first one
if env.count('='):
- var, value = env.split(' ')[0].split('=')[0:2]
+ var, value = env.split('=', 1)
# expand values, if variable is surcharged by other variables
try:
import subprocess
@@ -212,7 +212,7 @@ class ShellCmd(cmd.Cmd, object):
cin, cout = os.popen2('`which echo` %s' % value)
value = cout.readlines()[0]
- os.environ.update({var: value})
+ os.environ.update({var: value.rstrip()})
def cd(self):
""" implementation of the "cd" command
@@ -361,7 +361,14 @@ class ShellCmd(cmd.Cmd, object):
# for all other commands check in allowed list
if command not in self.conf['allowed'] and command:
- return self.warn_count('command', oline, strict, ssh, command)
+ export_pattern = re.compile('^[a-zA-Z0-9\-\_]*=')
+ if export_pattern.match(oline):
+ self.g_cmd = 'export'
+ new_cmd_line = 'export ' + oline
+ self.g_line = new_cmd_line
+ self.check_secure(new_cmd_line, strict = strict)
+ else:
+ return self.warn_count('command', oline, strict, ssh, command)
return 0
def warn_count(self, messagetype, line=None, strict=None, ssh=None, command=None):

View File

@ -1,100 +0,0 @@
#!/bin/bash
MAX_OPENRC_LEN=100
read -p "Pre-store Keystone user credentials for this session? (y/N): " confirm
confirm=${confirm,,}
if [ "$confirm" == "y" ] || [ "$confirm" = "yes" ]; then
if [ -z "$OPENRC_TEMPLATE" ] || [ ! -f `echo $OPENRC_TEMPLATE` ]; then
read -p "env[OPENRC_TEMPLATE] not set.
Hints will not be available for certain options. Continue anyways? (Y/n): " confirm
confirm=${confirm,,}
([ "$confirm" == "n" ] || [ "$confirm" == "no" ]) && exit 0
else
# Check if we are to run Muted
[ "$1" = "--mute" ] && MUTE=1
# Load default values for System URL, Region and Keystone URL
defEnv=( $(cat $OPENRC_TEMPLATE) )
defEnvLen=${#defEnv[@]}
[ "$defEnvLen" -gt "$MAX_OPENRC_LEN" ] && \
defEnvLen="$MAX_OPENRC_LEN"
for (( i=0; i<$defEnvLen; i++));
do
if [[ ${defEnv[$i]} =~ OS_AUTH_URL=(.*)$ ]]; then
def_os_auth_url=${BASH_REMATCH[1]}
elif [[ ${defEnv[$i]} =~ OS_REGION_NAME=(.*)$ ]]; then
def_os_region_name=${BASH_REMATCH[1]}
elif [[ ${defEnv[$i]} =~ OS_PROJECT_NAME=(.*)$ ]]; then
def_os_project_name=${BASH_REMATCH[1]}
elif [[ ${defEnv[$i]} =~ OS_USER_DOMAIN_NAME=(.*)$ ]]; then
def_os_user_domain_name=${BASH_REMATCH[1]}
elif [[ ${defEnv[$i]} =~ OS_PROJECT_DOMAIN_NAME=(.*)$ ]]; then
def_os_project_domain_name=${BASH_REMATCH[1]}
fi
done
fi
read -p "Enter Keystone username [$USER]: " os_user
[ -z "$os_user" ] && os_user="$USER"
read -p "Enter Keystone user domain name: " os_user_domain_name
[ -z "$os_user_domain_name" ] && os_user_domain_name="$def_os_user_domain_name"
read -p "Enter Project name: " os_project_name
[ -z "$os_project_name" ] && os_project_name="$def_os_project_name"
read -p "Enter Project domain name: " os_project_domain_name
[ -z "$os_project_domain_name" ] && os_project_domain_name="$def_os_project_domain_name"
read -s -p "Enter Keystone password: " os_pass
[ -z "$os_pass" ] && \
echo -n "Invalid password entry. Aborting!" && exit 1
# if we are not in mute mode then ask for these
# from user as input
if [ -z "$MUTE" ]; then
if [ -z "$def_os_region_name" ]; then
read -p "\n\nEnter Keystone Region Name: " os_region
else
read -p "Enter Keystone Region Name [$def_os_region_name]: " os_region
[ -z "$os_region" ] && os_region="$def_os_region_name"
fi
if [ -z "$def_os_auth_url" ]; then
read -p "Enter Keystone Authentication URL: " os_auth_url
else
read -p "Enter Keystone Authentication URL [$def_os_auth_url]: " os_auth_url
[ -z "$os_auth_url" ] && os_auth_url="$def_os_auth_url"
fi
else
# In MUTE mode
os_region="$def_os_region_name"
echo ""; echo ""
echo "Using default Openstack Region Name: $os_region"
os_auth_url="$def_os_auth_url"
echo "Using default Openstack Authentication URL: $os_auth_url"
echo "To set these to non-default, run \"source ~/$(basename $BASH_SOURCE)\" in your shell"
fi
# set user environment which will be valid for
# the duration of this session
# Since lshell is running for internal clients
# move OS ENDPOINT TYPE to internalURL
export OS_ENDPOINT_TYPE="internalURL"
export CINDER_ENDPOINT_TYPE="internalURL"
export OS_INTERFACE="internal"
export OS_USERNAME="$os_user"
export OS_PASSWORD="$os_pass"
export OS_PROJECT_NAME="$os_project_name"
export OS_USER_DOMAIN_NAME="$os_user_domain_name"
export OS_PROJECT_DOMAIN_NAME="$os_project_domain_name"
export OS_AUTH_URL="$os_auth_url"
export OS_IDENTITY_API_VERSION=3
export OS_REGION_NAME="$os_region"
# modify PS1 prompt
newprompt="[\u@\h \W($os_user)]\$ "
export PS1="$newprompt"
echo ""
echo "Keystone credentials preloaded!"
fi

View File

@ -169,9 +169,6 @@ initscripts
# setup
setup
# lshell
lshell
# nss-pam-ldapd
nss-pam-ldapd
@ -232,6 +229,9 @@ novnc
# sudo
sudo
# config files
sudo-config
# net-snmp
net-snmp-utils
net-snmp-libs

View File

@ -2,7 +2,6 @@ tools/vm-topology
base/initscripts
base/util-linux
base/setup
base/lshell
utilities/namespace-utils
ldap/nss-pam-ldapd
base/centos-release
@ -103,6 +102,7 @@ logging/logmgmt
filesystem/filesystem-scripts
utilities/branding
config-files/io-scheduler
config-files/sudo-config
tools/collector
grub/grubby
utilities/platform-util

View File

@ -1,2 +1,2 @@
COPY_LIST="files/*"
TIS_PATCH_VER=4
TIS_PATCH_VER=0

View File

@ -0,0 +1,32 @@
Summary: StarlingX Sudo Configuration File
Name: sudo-config
Version: 1.0
Release: %{tis_patch_ver}%{?_tis_dist}
License: Apache-2.0
Group: base
Packager: StarlingX
URL: unknown
Source0: wrs.sudo
Source1: LICENSE
%define WRSROOT_P cBglipPpsKwBQ
%description
StarlingX sudo configuration file
%install
install -d %{buildroot}/%{_sysconfdir}/sudoers.d
install -m 440 %{SOURCE0} %{buildroot}/%{_sysconfdir}/sudoers.d/wrs
%pre
getent group wrs >/dev/null || groupadd -r wrs
getent group wrs_protected >/dev/null || groupadd -f -g 345 wrs_protected
getent passwd wrsroot > /dev/null || \
useradd -m -g wrs -G root,wrs_protected \
-d /home/wrsroot -p %{WRSROOT_P} \
-s /bin/sh wrsroot 2> /dev/null || :
%files
%license ../SOURCES/LICENSE
%config(noreplace) %{_sysconfdir}/sudoers.d/wrs

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -9,3 +9,4 @@ wrsroot ALL=(root) NOPASSWD: /usr/bin/config_management
wrsroot ALL=(root) NOPASSWD: /usr/local/sbin/collect
Defaults lecture=never, secure_path=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
Defaults passprompt="Password: "

View File

@ -19,9 +19,7 @@ Patch0: sudo-support.patch
Patch1: sudo-delete-support.patch
Patch2: log_timestamp.patch
Patch3: ldap-user-setup-support.patch
Patch4: ldap-user-setup-support-input-validation.patch
Patch5: ldap-user-setup-noninteractive-mode-fix.patch
Patch6: allow-anonymous-bind-for-ldap-search.patch
Patch4: allow-anonymous-bind-for-ldap-search.patch
%define debug_package %{nil}
@ -31,7 +29,6 @@ Patch6: allow-anonymous-bind-for-ldap-search.patch
%description
Shell scripts that allow to manage POSIX accounts (users, groups, machines) in an LDAP directory.
%prep
%setup -q
%patch0 -p1
@ -39,20 +36,16 @@ Shell scripts that allow to manage POSIX accounts (users, groups, machines) in a
%patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%patch6 -p1
%build
%install
make install DESTDIR=%{buildroot}
rm -Rf %{buildroot}/usr/local/man
rm -f %{buildroot}/usr/local/sbin/*machine*
rm -f %{buildroot}/usr/local/etc/ldapscripts/ldapaddmachine.template.sample
install -d ldroot}}/usr/local/etc/
install -d %{buildroot}/usr/local/etc/
install -m 644 %{SOURCE1} %{buildroot}/usr/local/etc/ldapscripts/ldapscripts.conf
install -m 644 %{SOURCE2} %{buildroot}/usr/local/etc/ldapscripts/ldapadduser.template.cgcs
install -m 644 %{SOURCE3} %{buildroot}/usr/local/etc/ldapscripts/ldapaddgroup.template.cgcs

View File

@ -1,15 +0,0 @@
---
sbin/ldapusersetup | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
--- a/sbin/ldapusersetup
+++ b/sbin/ldapusersetup
@@ -105,7 +105,7 @@ LdapAddLoginShell () {
;;
esac
else
- shellopn=${$2,,}
+ shellopn=${2,,}
case $shellopn in
"bash") _SHELL="/bin/sh";;
"lshell") _SHELL="$_DEFAULTLSHELL";;

View File

@ -1,87 +0,0 @@
---
sbin/ldapusersetup | 45 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 34 insertions(+), 11 deletions(-)
--- a/sbin/ldapusersetup
+++ b/sbin/ldapusersetup
@@ -44,6 +44,29 @@ _SHELL=""
### Helper functions ###
+# Gets input from user and validates it.
+# Will only return if input meets validation
+# criteria otherwise will just sit there.
+#
+# Input : input string ($1), valid output options ($2)
+# Output: the validated input
+# Note : the validation list must be an array
+LdapUserInput () {
+declare -a optionAry=("${!2}")
+while true; do
+ read -p "$1" _output
+ # convert to lower case
+ _output2=${_output,,}
+ # check if output is a valid option
+ if [[ "${optionAry[@]}" =~ "$_output2" ]]; then
+ break
+ else
+ echo "Invalid input \"$_output\". Allowed options: ${optionAry[@]}" >&2
+ fi
+done
+ echo "$_output2"
+}
+
# Delete an ldap user if it exists
# and exit with error
# Input : username ($1), exit msg ($2)
@@ -67,10 +90,12 @@ LdapAddUser() {
LdapAddLoginShell () {
if [ -z "$2" ]; then
# Ask the user for the login shell
- echo "Select Login Shell option # [2]:
+ shellInput="Select Login Shell option # [2]:
1) Bash
-2) Lshell"
- read opn
+2) Lshell
+"
+ options=( 1, 2 )
+ opn=`LdapUserInput "$shellInput" options[@]`
case $opn in
1) _SHELL="/bin/sh";;
2) _SHELL="$_DEFAULTLSHELL";;
@@ -139,7 +164,6 @@ LdapUpdateShadowWarning () {
echo "Updating password expiry to $_newWarning days"
}
-
# Since this setup script is meant to be a
# wrapper on top of existing ldap scripts,
# it share invoke those... we could have achieved
@@ -170,10 +194,9 @@ if [ "$#" -eq 0 ]; then
# prompt for sudo permissions
if [ "$_SHELL" != "$_DEFAULTLSHELL" ]; then
# Should sudo be activated for this user
- echo -n "Add $_username to sudoer list? (yes/NO): "
- read CONFIRM
- CONFIRM=${CONFIRM,,}
-
+ shellInput="Add $_username to sudoer list? (yes/NO): "
+ options=( "yes", "no" )
+ CONFIRM=`LdapUserInput "$shellInput" options[@]`
if is_yes $CONFIRM
then
LdapAddSudo "$_username"
@@ -181,9 +204,9 @@ if [ "$#" -eq 0 ]; then
fi
# Add to secondary user group
- echo -n "Add $_username to secondary user group? (yes/NO): "
- read CONFIRM
- CONFIRM=${CONFIRM,,}
+ shellInput="Add $_username to secondary user group? (yes/NO): "
+ options=( "yes", "no" )
+ CONFIRM=`LdapUserInput "$shellInput" options[@]`
if is_yes $CONFIRM
then
echo -n "Secondary group to add user to? [$_DEFAULTGRP2]: "

View File

@ -1,12 +1,17 @@
---
Makefile | 5
man/man1/ldapusersetup.1 | 61 ++++++++++
sbin/ldapusersetup | 263 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 327 insertions(+), 2 deletions(-)
Makefile | 5 +-
man/man1/ldapusersetup.1 | 60 +++++++++++
sbin/ldapusersetup | 254 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 317 insertions(+), 2 deletions(-)
create mode 100644 man/man1/ldapusersetup.1
create mode 100644 sbin/ldapusersetup
diff --git a/sbin/ldapusersetup b/sbin/ldapusersetup
new file mode 100644
index 0000000..27d12dc
--- /dev/null
+++ b/sbin/ldapusersetup
@@ -0,0 +1,263 @@
@@ -0,0 +1,254 @@
+#!/bin/sh
+
+# ldapusersetup : interactive setup for adding users to LDAP
@ -33,7 +38,6 @@
+ echo "Usage : $0 [-u <username | uid> <field> <value>]
+where accepted field(s) are as follows:
+--sudo : whether to add this user to sudoer list
+--shell <\"bash\"|\"lshell\"> : choose the shell for this user (default is lshell)
+--secondgroup <grp> : the secondary group to add this user to
+--passmax <value> : the shadowMax value for this user
+--passwarning <value> : the shadowWarning value for this user"
@ -46,13 +50,36 @@
+
+# runtime defaults
+_DEFAULTGRP2="wrs_protected"
+_DEFAULTLSHELL="/usr/local/bin/cgcs_cli"
+_BASHSHELL="/bin/bash"
+_DEFAULTSHADOWMAX="90"
+_DEFAULTSHADOWWARNING="2"
+_SHELL=""
+
+### Helper functions ###
+
+# Gets input from user and validates it.
+# Will only return if input meets validation
+# criteria otherwise will just sit there.
+#
+# Input : input string ($1), valid output options ($2)
+# Output: the validated input
+# Note : the validation list must be an array
+LdapUserInput () {
+declare -a optionAry=("${!2}")
+while true; do
+ read -p "$1" _output
+ # convert to lower case
+ _output2=${_output,,}
+ # check if output is a valid option
+ if [[ "${optionAry[@]}" =~ "$_output2" ]]; then
+ break
+ else
+ echo "Invalid input \"$_output\". Allowed options: ${optionAry[@]}" >&2
+ fi
+done
+ echo "$_output2"
+}
+
+# Delete an ldap user if it exists
+# and exit with error
+# Input : username ($1), exit msg ($2)
@ -74,30 +101,8 @@
+# Input : username ($1), shell to set ($2)
+# Output : none
+LdapAddLoginShell () {
+ if [ -z "$2" ]; then
+ # Ask the user for the login shell
+ echo "Select Login Shell option # [2]:
+1) Bash
+2) Lshell"
+ read opn
+ case $opn in
+ 1) _SHELL="/bin/sh";;
+ 2) _SHELL="$_DEFAULTLSHELL";;
+ *)
+ [ ! -z "$opn" ] && echo "Invalid option. Selecting Lshell"
+ _SHELL="$_DEFAULTLSHELL"
+ ;;
+ esac
+ else
+ shellopn=${$2,,}
+ case $shellopn in
+ "bash") _SHELL="/bin/sh";;
+ "lshell") _SHELL="$_DEFAULTLSHELL";;
+ *)
+ echo "Invalid option($2). Selecting Lshell"; _SHELL="$_DEFAULTLSHELL"
+ ;;
+ esac
+ fi
+ # Support bash only now.
+ _SHELL="$_BASHSHELL"
+ # Replace the login shell
+ ldapmodifyuser $1 replace loginShell $_SHELL &> /dev/null
+ [ $? -eq 0 ] || LdapRollback $1 "Critical setup error: cannot set login shell"
@ -148,14 +153,13 @@
+ echo "Updating password expiry to $_newWarning days"
+}
+
+
+# Since this setup script is meant to be a
+# wrapper on top of existing ldap scripts,
+# it share invoke those... we could have achieved
+# loose coupling by not relying on helpers but
+# at the expense of massively redundant code
+# duplication.
+declare -a helper_scripts=("ldapadduser" "ldapaddsudo" "ldapmodifyuser" "ldapaddusertogroup" "$_DEFAULTLSHELL")
+declare -a helper_scripts=("ldapadduser" "ldapaddsudo" "ldapmodifyuser" "ldapaddusertogroup" "$_BASHSHELL")
+
+# Do some quick sanity tests to make sure
+# helper scripts are present
@ -172,27 +176,23 @@
+ read _username
+ LdapAddUser "$_username"
+
+ # Replace the login shell. We will prompt the user for this
+ # Replace the login shell. Only bash is supported now.
+ LdapAddLoginShell "$_username"
+
+ # If login shell is NOT the default limited shell then
+ # prompt for sudo permissions
+ if [ "$_SHELL" != "$_DEFAULTLSHELL" ]; then
+ # Should sudo be activated for this user
+ echo -n "Add $_username to sudoer list? (yes/NO): "
+ read CONFIRM
+ CONFIRM=${CONFIRM,,}
+ # Should sudo be activated for this user
+ echo -n "Add $_username to sudoer list? (yes/NO): "
+ read CONFIRM
+ CONFIRM=${CONFIRM,,}
+
+ if is_yes $CONFIRM
+ then
+ LdapAddSudo "$_username"
+ fi
+ if is_yes $CONFIRM
+ then
+ LdapAddSudo "$_username"
+ fi
+
+ # Add to secondary user group
+ echo -n "Add $_username to secondary user group? (yes/NO): "
+ read CONFIRM
+ CONFIRM=${CONFIRM,,}
+ shellInput="Add $_username to secondary user group? (yes/NO): "
+ options=( "yes", "no" )
+ CONFIRM=`LdapUserInput "$shellInput" options[@]`
+ if is_yes $CONFIRM
+ then
+ echo -n "Secondary group to add user to? [$_DEFAULTGRP2]: "
@ -226,10 +226,6 @@
+ --sudo) # optional
+ _sudo="yes"
+ ;;
+ --shell) # optional
+ _loginshell="$2"
+ shift
+ ;;
+ --passmax) # optional
+ _shadowMax="$2"
+ shift
@ -270,9 +266,11 @@
+ LdapUpdateShadowMax $_username $_shadowMax
+ LdapUpdateShadowWarning $_username $_shadowWarning
+fi
diff --git a/Makefile b/Makefile
index f81c272..6e5b193 100644
--- a/Makefile
+++ b/Makefile
@@ -41,12 +41,13 @@ SBINFILES = ldapdeletemachine ldapmodify
@@ -41,12 +41,13 @@ SBINFILES = ldapdeletemachine ldapmodifygroup ldapsetpasswd lsldap ldapadduser l
ldapdeleteuser ldapsetprimarygroup ldapfinger ldapid ldapgid ldapmodifymachine \
ldaprenamegroup ldapaddgroup ldapaddusertogroup ldapdeleteuserfromgroup \
ldapinit ldapmodifyuser ldaprenamemachine ldapaddmachine ldapdeletegroup \
@ -288,9 +286,12 @@
MAN5FILES = ldapscripts.5
TMPLFILES = ldapaddgroup.template.sample ldapaddmachine.template.sample \
ldapadduser.template.sample
diff --git a/man/man1/ldapusersetup.1 b/man/man1/ldapusersetup.1
new file mode 100644
index 0000000..9b3129b
--- /dev/null
+++ b/man/man1/ldapusersetup.1
@@ -0,0 +1,61 @@
@@ -0,0 +1,60 @@
+.\" Copyright (c) 2015 Wind River Systems, Inc.
+.\"
+.\" This program is free software; you can redistribute it and/or
@ -337,7 +338,6 @@
+The name or uid of the user to modify.
+The following fields are available as long format options:
+--sudo : whether to add this user to sudoer list
+--shell <bash | lshell> : which login shell to use (default is lshell)
+--secondgroup <grp> : the secondary group to add this user to
+--passmax <value> : the shadowMax value for this user
+--passwarning <value> : the shadowWarning value for this user"