about summary refs log tree commit diff
path: root/Completion/compaudit
blob: 528334dc0c96ae3471cf582d41d1d6900a3e2c85 (plain) (blame)
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
# So that this file can also be read with `.' or `source' ...
compaudit() {                           # Define and then call

# Audit the fpath to assure that it contains all the directories needed by
# the completion system, and that those directories are at least unlikely
# to contain dangerous files.  This is far from perfect, as the modes or
# ownership of files or directories might change between the time of the
# audit and the time the function is executed.

# This function is designed to be called from compinit, which assumes that
# it is in the same directory, i.e., it can be autoloaded from the initial
# fpath as compinit was.  Most local parameter names in this function must
# therefore be the same as those used in compinit.

emulate -L zsh
setopt extendedglob

[[ -x /usr/bin/getent ]] || getent() { cat /etc/$1 }

# The positional parameters are the directories to check, else fpath.
if (( $# )); then
  local _compdir=''
elif (( $#fpath == 0 )); then
  print 'compaudit: No directories in $fpath, cannot continue' 1>&2
  return 1
else
  set -- $fpath
fi

# _i_check is defined by compinit; used here as a test for whether this
# function is running standalone or was called by compinit.  If called
# by compinit, we use parameters that are defined in compinit's scope,
# otherwise we make them local here.
(( $+_i_check )) || {
  local _i_q _i_line _i_file _i_fail=verbose
  local -a _i_files _i_addfiles _i_wdirs _i_wfiles
  local -a -U +h fpath
}

fpath=( $* )

# _compdir may be defined by the user; see the compinit documentation.
# If it isn't defined, we want it to point somewhere sensible, but the
# user is allowed to set it to empty to bypass the check below.
(( $+_compdir )) || {
  local _compdir=${fpath[(r)*/$ZSH_VERSION/*]}
  [[ -z $_compdir ]] && _compdir=$fpath[1]
  ### [[ -d $_compdir/../Base ]] && _compdir=${_compdir:h}
}

_i_wdirs=()
_i_wfiles=()

_i_files=( ${^~fpath:/.}/^([^_]*|*~|*.zwc)(N) )
if [[ -n $_compdir ]]; then
  if [[ $#_i_files -lt 20 || $_compdir = */Base || -d $_compdir/Base ]]; then
    # Too few files: we need some more directories, or we need to check
    # that all directories (not just Base) are present.
    _i_addfiles=()
    if [[ -d $_compdir/Base/Core ]]; then
      # Add all the Completion subdirectories (CVS-layout)
      _i_addfiles=(${_compdir}/*/*(/))
    elif [[ -d $_compdir/Base ]]; then
      # Likewise (installation-layout)
      _i_addfiles=(${_compdir}/*(/))
    fi
    for _i_line in {1..$#i_addfiles}; do
      _i_file=${_i_addfiles[$_i_line]}
      [[ -d $_i_file && -z ${fpath[(r)$_i_file]} ]] ||
        _i_addfiles[$_i_line]=
    done
    fpath=($fpath $_i_addfiles)
    _i_files=( ${^~fpath:/.}/^([^_]*|*~|*.zwc)(N) )
  fi
fi

[[ $_i_fail == use ]] && return 0

# RedHat Linux "per-user groups" check.  This is tricky, because it's very
# difficult to tell whether the sysadmin has put someone else into your
# "private" group (e.g., via the default group field in /etc/passwd, or
# by NFS group sharing with an untrustworthy machine).  So we must assume
# that this has not happened, and pick the best group.

local GROUP GROUPMEM _i_pw _i_gid _i_ulwdirs
while IFS=: read GROUP _i_pw _i_gid GROUPMEM; do
  if (( UID == EUID )); then
    [[ $GROUP == $LOGNAME ]] && break
  else
    (( _i_gid == EGID )) && break       # Somewhat arbitrary
  fi
done <<(getent group)

# We search for:
# - world/group-writable directories in fpath not owned by root and the user
# - parent-directories of directories in fpath that are world/group-writable
#   and not owned by root and the user (that would allow someone to put a
#   digest file for one of the directories into the parent directory)
# - digest files for one of the directories in fpath not owned by root and
#   the user
# - and for files in directories from fpath not owned by root and the user
#   (including zwc files)

if [[ $GROUP == $LOGNAME && ( -z $GROUPMEM || $GROUPMEM == $LOGNAME ) ]]; then
  _i_wdirs=( ${^fpath}(N-f:g+w:^g:${GROUP}:,-f:o+w:,-^u0u${EUID})
             ${^fpath:h}(N-f:g+w:^g:${GROUP}:,-f:o+w:,-^u0u${EUID}) )
else
  _i_wdirs=( ${^fpath}(N-f:g+w:,-f:o+w:,-^u0u${EUID})
             ${^fpath:h}(N-f:g+w:,-f:o+w:,-^u0u${EUID}) )
fi

if [[ -f /etc/debian_version ]]
then
_i_ulwdirs=( ${(M)_i_wdirs:#/usr/local/*} )
_i_wdirs=( ${_i_wdirs:#/usr/local/*} ${^_i_ulwdir}(Nf:g+ws:^g:staff:,f:o+w:,^u0) )
fi

_i_wdirs=( $_i_wdirs ${^fpath}.zwc^([^_]*|*~)(N-^u0u${EUID}) )
_i_wfiles=( ${^fpath}/^([^_]*|*~)(N-^u0u${EUID}) )

case "${#_i_wdirs}:${#_i_wfiles}" in
(0:0) _i_q= ;;
(0:*) _i_q=files ;;
(*:0) _i_q=directories ;;
(*:*) _i_q='directories and files' ;;
esac

if [[ -n "$_i_q" ]]; then
  [[ $_i_fail == verbose ]] && {
    print There are insecure ${_i_q}: 1>&2
    print -l - $_i_wdirs $_i_wfiles
  }
  return 1
fi
return 0

}                                       # Define and then call

compaudit "$@"