2015-07-15 13:16:10 +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:16:10 +00:00
# license: All rights reserved
# The file format consists of
# - group name
2015-10-06 11:56:29 +00:00
# - password - group's encrypted password
# - gid - group's decimal ID
# - member list - group members, comma seperated list
2015-09-05 20:26:21 +00:00
#
# Usage:
# describe etc_group do
# its('gids') { should_not contain_duplicates }
# its('groups') { should include 'my_user' }
# its('users') { should include 'my_user' }
# end
#
# describe etc_group.where(name: 'my_group') do
# its('users') { should include 'my_user' }
# end
2015-07-15 13:16:10 +00:00
2015-10-06 12:00:19 +00:00
require 'utils/convert'
2015-10-06 11:56:29 +00:00
require 'utils/parser'
2015-10-26 03:04:18 +00:00
class EtcGroup < Inspec . resource ( 1 )
2015-10-06 12:00:19 +00:00
include Converter
2015-12-31 00:01:11 +00:00
include CommentParser
2015-10-06 11:56:29 +00:00
2015-08-28 19:52:59 +00:00
name 'etc_group'
2015-11-27 13:02:38 +00:00
desc 'Use the etc_group InSpec audit resource to test groups that are defined on Linux and UNIX platforms. The /etc/group file stores details about each group---group name, password, group identifier, along with a comma-separate list of users that belong to the group.'
example "
describe etc_group do
its ( 'gids' ) { should_not contain_duplicates }
its ( 'groups' ) { should include 'my_user' }
its ( 'users' ) { should include 'my_user' }
end
"
2015-07-15 13:16:10 +00:00
2015-08-28 19:52:59 +00:00
attr_accessor :gid , :entries
def initialize ( path = nil )
@path = path || '/etc/group'
2015-10-06 11:56:29 +00:00
@entries = parse_group ( @path )
2015-10-06 11:57:22 +00:00
# skip resource if it is not supported on current OS
return skip_resource 'The `etc_group` resource is not supported on your OS.' \
2016-01-28 13:21:52 +00:00
unless inspec . os . unix?
2015-07-26 10:30:12 +00:00
end
2015-07-15 13:16:10 +00:00
2015-09-05 20:26:21 +00:00
def groups ( filter = nil )
entries = filter || @entries
2015-10-07 10:33:33 +00:00
entries . map { | x | x [ 'name' ] } if ! entries . nil?
2015-07-26 10:30:12 +00:00
end
2015-07-15 13:16:10 +00:00
2015-09-05 20:26:21 +00:00
def gids ( filter = nil )
entries = filter || @entries
2015-10-07 10:33:33 +00:00
entries . map { | x | x [ 'gid' ] } if ! entries . nil?
2015-07-26 10:30:12 +00:00
end
2015-07-15 13:16:10 +00:00
2015-09-05 20:26:21 +00:00
def users ( filter = nil )
entries = filter || @entries
return nil if entries . nil?
# filter the user entry
res = entries . map { | x |
2015-10-07 10:33:33 +00:00
x [ 'members' ] . split ( ',' ) if ! x . nil? && ! x [ 'members' ] . nil?
2015-09-05 20:26:21 +00:00
} . flatten
# filter nil elements
res . reject { | x | x . nil? || x . empty? }
2015-07-26 10:30:12 +00:00
end
2015-07-15 13:16:10 +00:00
2015-09-03 18:43:58 +00:00
def where ( conditions = { } )
2015-08-03 01:42:05 +00:00
return if conditions . empty?
fields = {
2015-10-07 10:33:33 +00:00
name : 'name' ,
group_name : 'name' ,
password : 'password' ,
gid : 'gid' ,
group_id : 'gid' ,
users : 'members' ,
members : 'members' ,
2015-08-03 01:42:05 +00:00
}
res = entries
2015-10-07 10:33:33 +00:00
2015-09-04 07:59:30 +00:00
conditions . each do | k , v |
2015-08-03 01:49:05 +00:00
idx = fields [ k . to_sym ]
2015-08-03 01:42:05 +00:00
next if idx . nil?
2015-09-05 20:26:21 +00:00
res = res . select { | x | x [ idx ] == v . to_s }
2015-08-03 01:42:05 +00:00
end
2015-10-07 10:33:33 +00:00
2015-09-05 20:26:21 +00:00
EtcGroupView . new ( self , res )
2015-07-15 13:16:10 +00:00
end
2015-08-03 01:42:05 +00:00
2015-10-12 11:01:58 +00:00
def to_s
'/etc/group'
end
2015-08-28 19:52:59 +00:00
private
2015-08-03 00:40:08 +00:00
2015-10-06 11:56:29 +00:00
def parse_group ( path )
2015-10-26 03:04:18 +00:00
@content = inspec . file ( path ) . content
2015-10-26 15:11:28 +00:00
if @content . nil?
skip_resource " Can't access group file in #{ path } "
return [ ]
end
2015-10-06 11:56:29 +00:00
# iterate over each line and filter comments
@content . split ( " \n " ) . each_with_object ( [ ] ) do | line , lines |
grp_info = parse_group_line ( line )
lines . push ( grp_info ) if ! grp_info . nil? && grp_info . size > 0
2015-08-28 19:52:59 +00:00
end
2015-08-03 00:40:08 +00:00
end
2015-10-06 11:56:29 +00:00
def parse_group_line ( line )
opts = {
comment_char : '#' ,
standalone_comments : false ,
}
line , _idx_nl = parse_comment_line ( line , opts )
2015-10-07 10:33:33 +00:00
x = line . split ( ':' )
# abort if we have an empty or comment line
return nil if x . size == 0
# map data
{
'name' = > x . at ( 0 ) , # Name of the group.
'password' = > x . at ( 1 ) , # Group's encrypted password.
'gid' = > convert_to_i ( x . at ( 2 ) ) , # The group's decimal ID.
'members' = > x . at ( 3 ) , # Group members.
}
2015-10-06 11:56:29 +00:00
end
2015-08-28 19:52:59 +00:00
end
2015-09-05 20:26:21 +00:00
# object that hold a specifc view on etc group
class EtcGroupView
def initialize ( parent , filter )
@parent = parent
@filter = filter
end
2015-10-07 09:28:00 +00:00
# returns the group object
2015-10-07 10:33:33 +00:00
def entries
2015-10-07 09:28:00 +00:00
@filter
end
# only returns group name
2015-10-07 10:33:33 +00:00
def groups
2015-09-09 16:37:16 +00:00
@parent . groups ( @filter )
end
2015-09-05 20:26:21 +00:00
2015-10-07 09:28:00 +00:00
# only return gids
2015-09-09 16:37:16 +00:00
def gids
@parent . gids ( @filter )
end
2015-09-05 20:26:21 +00:00
2015-10-07 09:28:00 +00:00
# only returns users
2015-09-09 16:37:16 +00:00
def users
@parent . users ( @filter )
end
2015-09-05 20:26:21 +00:00
end