This repository has been archived by the owner on Oct 13, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
/
tacplus-restrict
executable file
·259 lines (209 loc) · 7.2 KB
/
tacplus-restrict
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
#! /bin/bash
# Copyright 2016 by Cumulus Networks, Inc.
# This script needs to be run as root or via sudo
# Set up an existing account to be use a restricted shell
# (only rbash is supported), changing the home directory so the
# user can not remove the dot files, and have the dot files
# owned by bin.bin.
# The intent is to set up an account for use by users who
# are only allowed to use commands (and command arguments)
# permitted by a TACACS+ server.
# Set up a ~user/bin directory and PATH to point to that.
# Create a symlink from /usr/sbin/tacplus-auth to each of
# the the requested commands to the bin directory
# Those will be the only commands accessible to the user, and
# only if also permitted by the TACACS+ server
# Use the -i option to initialize the directory
# permissions and dot files. It will only be run
# once, unless the -f option is given
# Use the -a to add command links that the user will be able
# to potentially run (subject to authorization by the TACACS+ server)
# The list of commands is given after all other arguments
# -i and -a can be combined
# The -u username argument is required
# See
# man tacplus-auth
# for additional information.
prog=${0##*/}
# could add rzsh or rksh in future, but keeping it simple for now
shell=/bin/rbash origshell=/bin/bash
tacacs_restrict=.tacacs_restricted # flag file for inited
authprog=/usr/sbin/tacplus-auth # command that does authorization
# set defaults
user= firstarg=
addcmds=0 delcmds=0 doinit=0 dorestore=0 force=0
warn()
{
echo $prog: Warning: "$@" 1>&2
}
usage()
{
echo Usage: $prog '-u user [-f (force)] [-i (init)] [-R (restore)] [(-a|-d) cmd1 cmd2 ...]' 1>&2
exit 1
}
fatal()
{
warn "$@"
exit 2
}
initialize()
{
set -e # exit if any of the next group of commands fail
chsh -s $shell $user || fatal Use tacacs0, tacacs15, etc. for -u arg
chmod 755 $home || $warn Unable to chmod ${user} home directory $home
dots=$(echo .[a-zA-Z]*)
if [ -n "${dots}" -a "${dots}" != ".[a-zA-Z]*" ]; then
# create dir if needed. If this is the 2nd init
# or later with dotfiles, the original backup will
# be saved in the new backup.
mkdir -p DOTback
mv -i .[a-zA-Z]* DOTback/
chown bin.bin DOTback
chmod 750 DOTback
mv DOTback .DOTback
echo Moved old dot files to DOTback: ; (cd .DOTback && ls -a)
fi
for f in .bash_login .profile .bash_profile .bash_logout
do echo '. ~/.bashrc' > $f
done
echo "export PATH=$home/bin" > .bashrc
mkdir -p bin
touch ${tacacs_restrict}
chown -R bin.bin .
chmod -R o-w . # be paranoid
set +e # initialization complete
}
# dorestore the initialization. Return shell to /bin/bash, restore dotfiles, etc.
restore()
{
local l cmd lev=${user#tacacs}
warn Restoring $user '(tacacs priv='$lev')' to normal use,'
able to run all commands.'
warn You have 5 seconds to interrupt to prevent the restore
sleep 5
chsh -s $origshell $user || warn unable to restore shell $origshell for $user
for f in .bash_login .profile .bash_profile .bash_logout
do rm -f $f # remove the files we created in initialize()
done
dots=$(echo .DOTback/.[a-zA-Z]* | sed 's,.DOTback/.DOTback,,')
if [ -z "${dots}" -o "${dots}" = ".DOTback/.[a-zA-Z]*" ]; then
warn No saved files '(.bashrc, etc.)', using /etc/skel
cp -a /etc/skel/.[a-zA-Z0-9]* .
else
mv ${dots} .
fi
[ -e ${tacacs_restrict} ] && rm ${tacacs_restrict}
# Only remove .DOTback if empty, not rm -rf. Sometimes multiple levels
rmdir .DOTback 2>/dev/null
[ -d .DOTback ] && chown -R ${user}.tacacs .DOTback # make user can see files
for cmd in bin/*; do
[ -L "${cmd}" ] && {
l=$(readlink -s "${cmd}")
case "$l" in
$authprog) rm -f "${cmd}" ;;
esac
}
done
rmdir bin 2>/dev/null # remove if empty
}
linkcmds()
{
[ -x $authprog ] || fatal TACACS authorization command $authprog not found
for c in "${firstarg}" "$@"; do
cmd=${c##*/} # make relative in case they gave full name
case $cmd in
sh|dash|bash|csh|tcsh|zsh|ksh|sh.distrib)
echo $prog: Skipping shell $cmd because it bypasses restrictions
continue ;;
esac
ln -s -f $authprog bin/$cmd
done
}
# inverse of linkcmds, but supports '*' to delete all
unlinkcmds()
{
(
[ ! -d bin ] && { warn All commands for $user have already been removed; exit 1; }
cd bin || exit 1
for c in "${firstarg}" "$@"; do
[ -L "${c}" ] || warn "${c}" is not currently an enabled command # but remove anyway
case "${c}" in
'*') warn Removing all commands for $user: $(echo *) ; rm -f -- * ;;
*) rm -f -- "${c}" ;;
esac
done
)
}
listcmds()
{
local lev=${user#tacacs}
if [ ! -d bin ]; then
warn No commands available to $user, tacacs priv=${lev}.
return
fi
(
cd bin || exit 1
p=${user/tacacs}
case "$p" in
[0-9]|1[0-5]) umsg="$user (TACACS+ priv=$p)" ;;
*) umsg=$user ;;
esac
cmds=$(echo *)
case "$cmds" in
'*'|'') warn No commands available to user $umsg ;;
*) echo Commands available to $umsg are:
/bin/ls -C
esac
)
}
# With both arrays and using set -- $(getopt ...), if any of the
# getopts has similar issues, and I want to detect things like
# -a cmd1 cmd2 -d cmd3
# getopts builtin can't do that, but getopt does.
# There is an issue with quoted arguments not remaining quoted
# but that's OK for this use.
# Unfortunately, the quoting itself (getopt uses apostrophe quotes)
# can cause problems. So use getopts, and step through all args.
while getopts a:d:fhiRu: opt
do
case "$opt" in
a) addcmds=1 firstarg="$OPTARG" ;;
d) delcmds=1 firstarg="$OPTARG" ;;
u) user=$OPTARG ;;
i) doinit=1 ;;
f) force=1 ;;
R) dorestore=1;;
h|?) usage ;;
esac
done
shift $(( OPTIND - 1 ))
# catch things like -a b c -d e f g
for a in $@; do
case "$a" in
-d) [ $addcmds -eq 1 ] && usage ;;
-a) [ $delcmds -eq 1 ] && usage ;;
esac
done
# sanity check that we have valid argument sets, and no
# extra arguments
[ $addcmds -eq 1 -a $delcmds -eq 1 ] && usage
[ $addcmds -eq 0 -a $delcmds -eq 0 -a $# -ne 0 ] && usage
[ -z "$user" ] && usage
[ $doinit -eq 1 -a $addcmds -eq 0 -a $delcmds -eq 0 -a $# -ne 0 ] && usage
[ $addcmds -eq 0 -a $doinit -eq 0 -a $delcmds -eq 0 -a $dorestore -eq 0 ] && usage
[ $dorestore -eq 1 -a \( $addcmds -eq 1 -o $delcmds -eq 1 -o $doinit -eq 1 -o $# -ne 0 \) ] && usage
home=$(eval echo ~"${user}")
[ -z "$home" -o "$home" = ~${user} ] &&
fatal Unable to get home directory for $user
cd $home || fatal home dir "${home}" for $user does not exist
[ -e ${tacacs_restrict} -a $doinit -eq 1 -a $force -eq 0 ] &&
fatal initialization requested without '-f', and already initialized
[ \( $addcmds -eq 1 -o $delcmds -eq 1 \) -a ! -e ${tacacs_restrict} ] &&
warn not yet initialized
[ -x $shell ] || fatal shell "${shell}" is not executable
umask 222 # no write perms on any dirs or files
[ $dorestore -eq 1 ] && { restore ; exit 1; }
[ $doinit -eq 1 ] && initialize
[ $addcmds -eq 1 ] && linkcmds "$@"
[ $delcmds -eq 1 ] && unlinkcmds "$@"
listcmds # Always show which commands are avilable now