2015-07-15 13:15:18 +00:00
# encoding: utf-8
# copyright: 2015, Vulcano Security GmbH
2015-10-06 16:55:44 +00:00
# author: Christoph Hartmann
# author: Dominik Richter
2015-07-15 13:15:18 +00:00
# license: All rights reserved
2015-07-15 13:15:53 +00:00
# The file format consists of
# - username
# - password
# - userid
# - groupid
# - user id info
# - home directory
# - command
2015-10-04 15:59:13 +00:00
require 'utils/parser'
2016-03-08 18:06:55 +00:00
module Inspec::Resources
2016-03-30 23:51:43 +00:00
class Passwd < Inspec . resource ( 1 ) # rubocop:disable Metrics/ClassLength
2016-03-08 18:06:55 +00:00
name 'passwd'
desc 'Use the passwd InSpec audit resource to test the contents of /etc/passwd, which contains the following information for users that may log into the system and/or as users that own running processes.'
example "
describe passwd do
its ( 'users' ) { should_not include 'forbidden_user' }
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
describe passwd . uids ( 0 ) do
its ( 'users' ) { should cmp 'root' }
its ( 'count' ) { should eq 1 }
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
describe passwd . shells ( / nologin / ) do
# find all users with a nologin shell
its ( 'users' ) { should_not include 'my_login_user' }
end
"
include PasswdParser
attr_reader :uid
attr_reader :params
attr_reader :content
attr_reader :lines
def initialize ( path = nil , opts = nil )
opts || = { }
@path = path || '/etc/passwd'
@content = opts [ :content ] || inspec . file ( @path ) . content
@lines = @content . to_s . split ( " \n " )
@filters = opts [ :filters ] || ''
@params = parse_passwd ( @content )
2016-02-17 11:35:46 +00:00
end
2015-08-28 19:27:35 +00:00
2016-03-08 18:06:55 +00:00
def filter ( hm = { } )
return self if hm . nil? || hm . empty?
res = @params
filters = ''
hm . each do | attr , condition |
2016-03-30 23:51:43 +00:00
res , filters = filter_attribute ( attr , condition , res , filters )
2016-02-17 11:35:46 +00:00
end
2016-03-08 18:06:55 +00:00
content = res . map { | x | x . values . join ( ':' ) } . join ( " \n " )
Passwd . new ( @path , content : content , filters : @filters + filters )
2016-02-17 11:35:46 +00:00
end
2015-07-15 13:15:53 +00:00
2016-03-08 18:06:55 +00:00
def usernames
warn '[DEPRECATION] `passwd.usernames` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
users
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def username
warn '[DEPRECATION] `passwd.user` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
users [ 0 ]
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def uid ( x )
warn '[DEPRECATION] `passwd.uid(arg)` is deprecated. Please use `passwd.uids(arg)` instead. It will be removed in version 1.0.0.'
uids ( x )
end
2015-07-15 13:15:53 +00:00
2016-03-08 18:06:55 +00:00
def users ( name = nil )
name . nil? ? map_data ( 'user' ) : filter ( user : name )
end
2015-07-15 13:15:53 +00:00
2016-03-08 18:06:55 +00:00
def passwords ( password = nil )
password . nil? ? map_data ( 'password' ) : filter ( password : password )
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def uids ( uid = nil )
uid . nil? ? map_data ( 'uid' ) : filter ( uid : uid )
end
2015-07-14 22:47:17 +00:00
2016-03-08 18:06:55 +00:00
def gids ( gid = nil )
gid . nil? ? map_data ( 'gid' ) : filter ( gid : gid )
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def homes ( home = nil )
home . nil? ? map_data ( 'home' ) : filter ( home : home )
end
2015-07-14 22:47:17 +00:00
2016-03-08 18:06:55 +00:00
def shells ( shell = nil )
shell . nil? ? map_data ( 'shell' ) : filter ( shell : shell )
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def to_s
f = @filters . empty? ? '' : ' with' + @filters
" /etc/passwd #{ f } "
end
2016-02-17 11:35:46 +00:00
2016-03-08 18:06:55 +00:00
def count
@params . length
end
2015-10-12 11:01:58 +00:00
2016-03-08 18:06:55 +00:00
private
2015-08-28 19:27:35 +00:00
2016-03-08 18:06:55 +00:00
def map_data ( id )
@params . map { | x | x [ id ] }
end
2016-03-30 23:51:43 +00:00
def filter_res_line ( item , matcher , condition , positive )
# TODO: REWORK ALL OF THESE, please don't depend on them except for simple equality!
case matcher
when '<'
item . to_i < condition
when '<='
item . to_i < = condition
when '>'
item . to_i > condition
when '>='
item . to_i > = condition
else
condition = condition . to_s if condition . is_a? Integer
case item
when condition
positive
else
! positive
end
end
end
def filter_attribute ( attr , condition , res , filters )
matcher = '=='
positive = true
if condition . is_a? ( Hash ) && condition . length == 1
matcher = condition . keys [ 0 ] . to_s
condition = condition . values [ 0 ]
end
positive = false if matcher == '!='
a = res . find_all do | line |
filter_res_line ( line [ attr . to_s ] , matcher , condition , positive )
end
b = filters + " #{ attr } #{ matcher } #{ condition . inspect } "
[ a , b ]
end
2015-10-04 15:49:00 +00:00
end
2015-07-26 10:30:12 +00:00
end