/usr/share/openscap/sectool-sce/01_passwd.sh is in libopenscap8 1.2.15-1build1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
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 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 | #!/bin/bash
# ----------------------------------------------------------- #
# Copyright (C) 2008 Red Hat, Inc. #
# Written by Michel Samia <msamia@redhat.com> #
# passwd.sh #
# more info in passwd.dsc #
# ----------------------------------------------------------- #
# TODO:
# are uids and gids in correct range? (find the range in file used in adduser from shadow utils..)
# has filled fullname?
# has valid shell? (/etc/shell)
function check_file_perm () {
if [[ -a "${1}" ]]; then
local -i CPERM=$(stat -c '%a' "${1}")
if (( ${CPERM} != $2 )); then
if (( (8#${CPERM} | 8#${2}) == 8#${2} )); then
if (( ${4} == 1 )); then
echo "Permissions on $(stat -c '%F' "${1}") \"${1}\" are more restrictive than required: ${CPERM} (${6:-uknown}, required persmissions are ${2})"
fi
else
if (( ${4} == 1 )); then
echo "Wrong permissions on $(stat -c '%F' "${1}") \"${1}\": ${CPERM} (${6:-unknown}, required permissions are ${2})"
RET=$XCCDF_RESULT_FAIL
fi
fi
fi
if ! (stat -c '%U:%G' "${1}" | grep -q "${3}"); then
if (( ${4} == 1 )); then
echo "Wrong owner/group on $(stat -c '%F' "${1}"): \"${1}\" (${6:-unknown}, required owner/group is ${3})"
RET=$XCCDF_RESULT_FAIL
fi
fi
else
if (( ${4} == 1 )); then
echo "Missing file or directory: \"${1}\" (${6:-unknown})"
RET=$XCCDF_RESULT_FAIL
fi
fi
}
# gets a value of a constant defined in a c/c++ header file by #define
# usage example:
# getValueFromH '/usr/include/bits/utmp.h' 'UT_NAMESIZE'
# echo $ReturnVal
function getValueFromH {
if ! [[ -r "$1" ]]; then
report 'WARNING' 1234 "Can't read a constant $2, header file $1 not found"
return 0
else
line="$(egrep "^#define $2..*" $1)"
if [[ -n "$line" ]]; then
local -i retval=$(echo "$line" | cut -f2)
return $retval
else
report 'WARNING' 1234 "Can't read a constant $2 from file $1, definition of the constant not found in this file"
return 0
fi
fi
}
# function isValidName
# tests a string whether it is a valid group/user name
# 1 - true
# 0 - false
function isValidName {
# first we need to set LC_ALL to C to get ranges working case-sensitively
oldLC_ALL=${LC_ALL}
LC_ALL="C"
# this constant contains a regex which recognizes, if the string is valid name of user or group
allowedNamesRegex='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]*[a-zA-Z0-9_.$-]?$'
echo $1 | egrep -q "$allowedNamesRegex"
returnValue=$[ 1 - $? ]
LC_ALL=${oldLC_ALL}
return $returnValue
}
# is it an ordinary user?
# takes one param - username
function canLogIn {
if [[ "$(getent passwd "${1}" | cut -d: -f 7)" != "/sbin/nologin" ]]; then
password="$(getent shadow "${1}" | cut -d: -f 2)"
# length of passwd - very short means invalid password and disabled account
if (( ${#password} < 13 )); then
return 1
else
# 0 is used as true in this case
return 0
fi
else
# 1 is used as false in this case
return 1
fi
}
RET=$XCCDF_RESULT_PASS
#constants
passwd=/etc/passwd
group=/etc/group
shadow=/etc/shadow
group_shadow=/etc/gshadow
# --- Error IDs ------- #
UID_MIN_VALUE="`egrep '^UID_MIN' /etc/login.defs | awk '{ print $2 }'`"
UID_MAX_VALUE="`egrep '^UID_MAX' /etc/login.defs | awk '{ print $2 }'`"
GID_MIN_VALUE="`egrep '^GID_MIN' /etc/login.defs | awk '{ print $2 }'`"
GID_MAX_VALUE="`egrep '^GID_MAX' /etc/login.defs | awk '{ print $2 }'`"
#if[[ `whoami` -ne 'root' ]]
if [[ $UID -ne '0' ]]
then
echo "You have to be logged as root to run this test!"
exit $XCCDF_RESULT_ERROR
fi
# permissions on /etc/passwd and /etc/group should be 644, should be owned by root:root
check_file_perm $passwd 644 root:root 1 $E_BAD_PERMISSIONS "User database"
i=0
while read line
do
i=$[i+1]
##### empty line #####
if [[ "$line" == "" ]]
then
echo "$passwd: Line $i is empty"
echo "Please delete this line."
RET=$XCCDF_RESULT_FAIL
continue
fi
##### number of fields #####
nf=`echo "$line" | awk -F: '{printf NF}'`
if [ "$nf" -ne "7" ]
then
echo "$passwd: Line $i has wrong number of fields"
echo "Please see 'man 5 passwd' and correct this line."
RET=$XCCDF_RESULT_FAIL
continue
fi
# now we can parse these fields, we know that all fields exist
username="`echo $line | awk -F: '{print $1}'`"
pass="`echo $line | awk -F: '{print $2}'`"
uid="`echo $line | awk -F: '{print $3}'`"
gid="`echo $line | awk -F: '{print $4}'`"
fullname="`echo $line | awk -F: '{print $5}'`"
homedir="`echo $line | awk -F: '{print $6}'`"
shell="`echo $line | awk -F: '{print $7}'`"
##### line has an empty login field #####
if [[ "$username" == "" ]]
then
echo "$passwd: Line $i: missing username!"
echo "Check this line, fill in first item (username), or delete the whole line."
RET=$XCCDF_RESULT_FAIL
fi
##### disallowed characters #####
isValidName $username
if [ $? -ne 1 ]
then
echo "$passwd: Line $i: User $username contains disallowed characters in his login."
echo "Check this line and rename user's login to contain lowercase letters only."
RET=$XCCDF_RESULT_FAIL
fi
##### too long username #####
getValueFromH '/usr/include/bits/utmp.h' 'UT_NAMESIZE'
MaxLength=$?
length=$(echo "$username" | wc -m)
if [ $length -gt $MaxLength ]
then
echo "$passwd: Line $i: User $username has too long username."
echo "Check this line and rename user's login to be shorter than $MaxLength characters"
RET=$XCCDF_RESULT_FAIL
fi
##### password empty #####
if [[ "$pass" == "" ]]
then
echo "$passwd: Line $i: User $username has no password!"
echo "Please use 'passwd' utility immediately to set his password!"
RET=$XCCDF_RESULT_FAIL
fi
##### password not shadowed (x in field 2) #####
if [[ "$pass" != "x" ]] && [[ "$pass" != "" ]] && [[ "$pass" != "*" ]] && [[ "$pass" != "!" ]]
then
echo "$passwd: Line $i: User ${username}'s password is not shadowed"
echo "Please use pwconv utility for moving passwords from \$passwd to \$shadow"
RET=$XCCDF_RESULT_FAIL
fi
##### is UID a number? #####
if [[ "`echo $uid | grep -e '^-\?[0-9]\+$'`" != "$uid" ]]
then
echo "$passwd: Line $i: User ID of user $username is not a valid number"
echo "Please correct the user ID in $passwd."
RET=$XCCDF_RESULT_FAIL
fi
##### is GID a number? #####
if [[ "`echo $gid | grep -e '^-\?[0-9]\+$'`" != "$gid" ]]
then
echo "$passwd: Line $i: Group ID of user $username is not a valid number"
echo "Please correct the group ID in $passwd."
RET=$XCCDF_RESULT_FAIL
else
##### is UID and GID in the range? #####
if canLogIn "$username" && [[ "$username" != "root" ]]; then
if [ "$uid" -lt "$UID_MIN_VALUE" ] || [ "$uid" -gt "$UID_MAX_VALUE" ]
then
echo "$passwd: Line $i: User $username has UID out of range"
echo "Change UID of this user to be in the range <$UID_MIN_VALUE, $UID_MAX_VALUE> (ordinary users) or less than $UID_MIN_VALUE (daemons)."
RET=$XCCDF_RESULT_FAIL
fi
if [ $gid -lt $GID_MIN_VALUE ] || [ $gid -gt $GID_MAX_VALUE ]
then
echo "$passwd: Line $i: User $username has GID out of range"
echo "Change GID of this user to be in the range <$GID_MIN_VALUE, $GID_MAX_VALUE> (ordinary users) or less than $GID_MIN_VALUE (daemons)."
RET=$XCCDF_RESULT_FAIL
fi
fi
fi
##### someone has uid 0, but his login is not "root"
if [[ "$uid" == "0" ]] && [[ "$username" != "root" ]]
then
echo "$passwd: Line $i: User $username has UID 0, but his login is not 'root'!"
echo "If there are more users with UID 0 in $passwd (you should see them on next lines), delete all but the first. Then set his name to root."
RET=$XCCDF_RESULT_FAIL
fi
##### someone has uid 1, but his login is not "bin"
if [[ "$uid" == "1" ]] && [[ "$username" != "bin" ]]
then
echo "$passwd: Line $i: User $username has UID 1, but his login is not 'bin'!"
echo "If there are more users with UID 1 in $passwd (you should see them on next lines), delete all but the first. Then set his name to 'bin'."
RET=$XCCDF_RESULT_FAIL
fi
##### someone has gid 0, but his login is not "root"
if [[ "$gid" == "0" ]] && [[ "$username" != "root" ]] && [[ "$username" != "halt" ]] && [[ "$username" != "shutdown" ]] && [[ "$username" != "sync" ]] && [[ "$username" != "operator" ]]
then
echo "$passwd: Line $i: User $username has GID 0, but his login is not 'root'!"
echo "If there are more users with GID 0 in $passwd (you should see them on next lines), delete all but the first. Then set his name to root."
RET=$XCCDF_RESULT_FAIL
fi
##### someone has gid 1, but his login is not "bin"
if [[ "$gid" == "1" ]] && [[ "$username" != "bin" ]]
then
echo "$passwd: Line $i: User $username has GID 1, but his login is not 'bin'!"
echo "If there are more users with GID 1 in $passwd (you should see them on next lines), delete all but the first. Then set his name to 'bin'."
RET=$XCCDF_RESULT_FAIL
fi
##### negative uid #####
if [ $uid -lt 0 ]
then
echo "$passwd: Line $i: User $username has a negative user ID"
echo "Set his UID to non-negative and not yet used value in the range <$UID_MIN_VALUE, $UID_MAX_VALUE> (ordinary users) or less than $UID_MIN_VALUE (daemons)."
RET=$XCCDF_RESULT_FAIL
fi
##### negative gid #####
if [ $gid -lt 0 ]
then
echo "$passwd: Line $i: User $username has a negative group ID"
echo "Set his GID to non-negative and not yet used value in the range <$GID_MIN_VALUE, $GID_MAX_VALUE> (ordinary users) or less than $GID_MIN_VAL (daemons)."
RET=$XCCDF_RESULT_FAIL
fi
##### is shell OK? (is in /etc/shells) #####
if [[ "$username" != "halt" ]] && [[ "$username" != "sync" ]] && [[ "$username" != "shutdown" ]] && [[ "$username" != "news" ]]
then
shell_ok="no"
while read s
do
if [[ "$shell" == "$s" ]]
then
shell_ok="yes"
fi
done<<EOF
`cat /etc/shells`
EOF
if [[ "$shell_ok" != "yes" ]]
then
echo "$passwd: Line $i: User $username has strange shell $shell"
echo "Set last field on this line to /bin/bash, or add the shell to /etc/shells"
RET=$XCCDF_RESULT_FAIL
fi
fi
done<<EOF
`cat $passwd`
EOF
##### two users with the same UID #####
while read line
do
# ignore empty lines
if [[ "`echo $line | egrep -c '^[0-9]+:$'`" == "1" ]]
then
continue
fi
line_nr=`echo $line | cut -d: -f1`
# we will compare with previous line, because it is sorted
# But when we read first (non-empty) line, we have to only save his UID
if [[ $first_nonempty != "false" ]]
then
prev_uid=`echo $line | cut -d: -f4`
prev_username=`echo $line | cut -d: -f2`
prev_line=`echo $line | cut -d: -f1`
first_nonempty="false"
continue
fi
uid=`echo $line | cut -d: -f4`
user=`echo $line |cut -d: -f2`
line=`echo $line |cut -d: -f1`
if [[ $uid == $prev_uid ]]
then
echo "User $user (passwd line $line) has the same UID ($uid) as user $prev_username (passwd line $prev_line)"
echo "Please change UID of this user and don't forget to chown his home directory"
RET=$XCCDF_RESULT_FAIL
fi
prev_uid=$uid
prev_line=$line
prev_username=$user
done<<EOF
`cat $passwd | awk '{ printf "%u:%s\n",NR,$0 }' | sort -n -t: -k4`
EOF
# ^^^
# we want to know line numbers of coliding records, so we can't use uniq -d...
# this also works with empty lines in passwd
##### two users with same username #####
while read user
do
if [[ "$user" != "" ]]
then
lines="`grep -n -e "^$user:" $passwd | awk -F: '{ print $1 }'| tr '\n' ','`"
lines="${lines%','}" # delete last coma
echo "Duplicate login '$user' ($passwd lines $lines)"
echo "Please change usernames on these lines to be different or delete duplicate records"
RET=$XCCDF_RESULT_FAIL
fi
done<<EOF
`awk -F: '{ if ($1 != "") print $1 }' $passwd | sort | uniq -d`
EOF
##### has root UID 0? #####
root_uid=`grep '^root:' $passwd |cut -d: -f3`
if [[ $root_uid != "0" ]]
then
echo "User root has UID $root_uid, but should have 0"
echo "Change root's UID to 0"
RET=$XCCDF_RESULT_FAIL
fi
exit $RET
|