about summary refs log tree commit diff
path: root/Completion/Unix/Command/_swanctl
blob: ba2f5402dc79750ce7dda83eecf07a8f135c7c8b (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
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
#compdef swanctl

# Completion for strongSwan's swanctl. See also ipsec, which is deprecated but
# still supported by strongSwan, and also used by Openswan/Libreswan.
#
# Note that in most cases elevated privileges are required to connect to the
# VICI socket, so the gain-privileges style may be necessary to complete SA
# names and the like: zstyle ':completion:*:swanctl/*' gain-privileges yes
#
# Other notes:
# - One of swanctl's selling points is that it can provide 'raw' structured
#   responses for scripting, etc. In practice, though, the output formatted for
#   humans seems easier to 'parse' from the shell than the plist-like raw output
# - @todo We don't complete authority names, pool names, peer IPs, etc.

# Complete connection names, SA names, or SA unique IDs. The distinctions
# between concepts like 'connections' and 'SAs' are very blurry here, partially
# for convenience and partially due to author confusion
# --child => complete only child/CHILD_SA names/IDs
# --ids   => complete unique SA IDs rather than connection/SA names
# --ike   => complete only connection/IKE_SA names/IDs
(( $+functions[_swanctl_connections] )) ||
_swanctl_connections() {
  local i which
  local -a expl tmp matches
  local -A opts

  zparseopts -D -E -A opts - -child -ids -ike

  tmp=( ${(@M)${(f)"$(
    _call_program -p swanctl-sas $words[1] -l
  )"}:#[^:]##: \#<->*} )
  (( $+opts[--ids] )) || tmp+=( ${(@M)${(f)"$(
    _call_program -p swanctl-conns $words[1] -L
  )"}:#[^:]##: ([A-Z][A-Za-z0-9]#|),*} )

  for i in $tmp; do
    if (( $+opts[--child] )) && [[ $i != [[:space:]]* ]]; then
      continue
    elif (( $+opts[--ike] )) && [[ $i == [[:space:]]* ]]; then
      continue
    fi

    # <name>: #<unique id>, ...
    i=${i//[#:,]/ }

    if (( $+opts[--ids] )); then
      matches+=( "${i[(w)2]}:${i[(w)1]} #${i[(w)2]}" )
    else
      matches+=( ${i[(w)1]} )
    fi
  done

  if (( $+opts[--ids] )); then
    matches=( ${(onu)matches} )
    if (( $+opts[--child] )); then
      which=CHILD_
    elif (( $+opts[--ike] )); then
      which=IKE_
    fi
    _describe -x2Vt sa-ids "${which}SA unique ID" matches
  else
    if (( $+opts[--child] )); then
      which='child '
    elif (( $+opts[--ike] )); then
      which='IKE '
    fi
    _wanted -x connections expl "${which}connection/SA name" compadd - $matches
  fi
}

_swanctl() {
  # Although swanctl will correctly parse multiple short options in the first
  # word, as in `swanctl -lh`, it won't actually *do* anything with the
  # subsequent options -- so we'll require that they be separated. Also, --help
  # doesn't take any further options, so just stop if we've got that
  if (( CURRENT == 2 )) || [[ $words[2] == (-h*|--help) ]]; then
    _arguments : \
      '(-)'{-a,--load-pools}'[(re)load pool configuration]' \
      '(-)'{-A,--list-pools}'[display loaded pool configurations]' \
      '(-)'{-b,--load-authorities}'[(re)load authority configuration]' \
      '(-)'{-B,--list-authorities}'[display loaded authority configurations]' \
      '(-)'{-c,--load-conns}'[(re)load connection configuration]' \
      '(-)'{-C,--counters}'[display or reset IKE event counters]' \
      '(-)'{-d,--redirect}'[redirect IKE_SA]' \
      '(-)'{-f,--flush-certs}'[flush cached certificates]' \
      '(-)'{-g,--list-algs}'[display loaded algorithms]' \
      '(-)'{-h,--help}'[display help information]' \
      '(-)'{-i,--initiate}'[initiate a connection]' \
      '(-)'{-l,--list-sas}'[display currently active IKE_SAs]' \
      '(-)'{-L,--list-conns}'[display loaded configurations]' \
      '(-)'{-m,--monitor-sa}'[monitor for IKE_SA and CHILD_SA changes]' \
      '(-)'{-p,--install}'[install trap or shunt policy]' \
      '(-)'{-P,--list-pols}'[display currently installed policies]' \
      '(-)'{-q,--load-all}'[load credentials, authorities, pools, and connections]' \
      '(-)'{-r,--reload-settings}'[reload daemon strongswan.conf]' \
      '(-)'{-R,--rekey}'[rekey SA]' \
      '(-)'{-s,--load-creds}'[(re)load credentials]' \
      '(-)'{-S,--stats}'[display daemon statistics]' \
      '(-)'{-t,--terminate}'[terminate connection]' \
      '(-)'{-T,--log}'[trace logging output]' \
      '(-)'{-u,--uninstall}'[uninstall trap or shunt policy]' \
      '(-)'{-v,--version}'[display version information]' \
      '(-)'{-x,--list-certs}'[display stored certificates]'
    return
  fi

  local ret=1 cmd
  local -a args cert_flags cert_types

  cert_flags=( aa any ca none ocsp )
  cert_types=( ocsp_response pubkey x509 x509_ac x509_crl )

  if [[ $words[2] == -[^-]* ]]; then
    cmd=${(M)words[2]#??}
  else
    cmd=$words[2]
  fi
  words=( $words[1] "${(@)words[3,-1]}" )
  (( CURRENT-- ))

  # Technically, only -v, -u, and -+ are truly global command options. However,
  # in practice, all commands also support -h, -P, and -r
  args=(
    '(: * -)'{-h,--help}'[display help information]'
    '(-P -r --pretty --raw)'{-P,--pretty}'[dump raw response message in pretty print]'
    '(-P -r --pretty --raw)'{-r,--raw}'[dump raw response message]'
    # https://wiki.strongswan.org/projects/strongswan/wiki/LoggerConfiguration
    # https://github.com/strongswan/strongswan/blob/master/src/libstrongswan/utils/debug.h
    '(-v --debug)'{-v+,--debug=}'[specify debug level]:debug level [1]:((
      -1\:"absolutely silent (SILENT)"
      0\:"basic auditing (AUDIT)"
      1\:"generic control flow with errors (CTRL)"
      2\:"detailed control flow (DIAG)"
      3\:"raw binary blobs (RAW)"
      4\:"sensitive data (PRIVATE)"
    ))'
    '(-u --uri)'{-u+,--uri=}'[specify service URI to connect to]:VICI service URI:_urls'
    '(-+ --options)'{'-\++',--options=}'[read command-line options from specified file]:options file:_files'
  )

  case $cmd in
    -A|--list-pools) args+=(
      '(-n --name)'{-n+,--name=}'[filter by specified pool name]:pool name'
      '(-l --leases)'{-l,--leases}'[display leases of each pool]'
    ) ;;
    -B|--list-authorities) args+=(
      '(-n --name)'{-n+,--name=}'[filter by specified authority name]:authority name'
    ) ;;
    -C|--counters) args+=(
      '(-a -n --all --name)'{-a,--all}'[display/reset counters for all tracked connections]'
      '(-a -n --all --name)'{-n+,--name=}'[specify connection name]: :_swanctl_connections --ike'
      '(-r --reset)'{-r,--reset}'[reset counters]'
    ) ;;
    -d|--redirect) args+=(
      '(-d --peer-id)'{-d+,--peer-id=}'[redirect by IKE_SA matching specified peer identity]:peer identity'
      '(-g --gateway)'{-g+,--gateway=}'[redirect to specified gateway]:target gateway'
      '(-i --ike)'{-i+,--ike=}'[redirect by specified IKE_SA name]: :_swanctl_connections --ike'
      '(-I --ike-id)'{-I+,--ike-id=}'[redirect by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
      '(-p --peer-ip)'{-p+,--peer-ip=}'[redirect by IKE_SA matching specified peer IP]:peer IP address'
    ) ;;
    -f|--flush-certs) args+=(
      '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:(
        ${(j< >)${(@q-)cert_types}}
      )"
    ) ;;
    -i|--initiate) args+=(
      '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child'
      '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike"
      '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)'
    ) ;;
    -l|--list-sas) args+=(
      '(-i --ike)'{-i+,--ike=}'[filter by specified IKE_SA name]: :_swanctl_connections --ike'
      '(-I --ike-id)'{-I+,--ike-id=}'[filter by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
      '(-n --noblock)'{-n,--noblock}'[do not wait for IKE_SAs in use]'
    ) ;;
    -p|-u|--install|--uninstall) args+=(
      '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child'
      '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike"
    ) ;;
    -P|--list-pols) args+=(
      '(-c --child)'{-c+,--child=}'[filter by specified CHILD_SA name]: :_swanctl_connections --child'
      '(-d --drop)'{-d,--drop}'[list drop policies]'
      '(-p --pass)'{-p,--pass}'[list bypass policies]'
      '(-t --trap)'{-t,--trap}'[list trap policies]'
    ) ;;
    -q|-s|--load-all|--load-creds) args+=(
      '(-c --clear)'{-c,--clear}'[clear previously loaded credentials]'
      '(-n --noprompt)'{-n,--noprompt}'[do not prompt for passwords]'
    ) ;;
    -R|--rekey) args+=(
      '(-c --child)'{-c+,--child=}'[rekey by specified CHILD_SA name]: :_swanctl_connections --child'
      '(-C --child-id)'{-C+,--child-id=}'[rekey by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child'
      '(-i --ike)'{-i+,--ike=}'[rekey by specified IKE_SA name]: :_swanctl_connections --ike'
      '(-I --ike-id)'{-I+,--ike-id=}'[rekey by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
    ) ;;
    -t|--terminate) args+=(
      '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)'
      '(-c --child)'{-c+,--child=}'[terminate by specified CHILD_SA name]: :_swanctl_connections --child'
      '(-C --child-id)'{-C+,--child-id=}'[terminate by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child'
      '(-i --ike)'{-i+,--ike=}'[terminate by specified IKE_SA name]: :_swanctl_connections --ike'
      '(-I --ike-id)'{-I+,--ike-id=}'[terminate by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
    ) ;;
    -v|--version) args+=(
      '(-d --daemon)'{-d,--daemon}'[query daemon version]'
    ) ;;
    -x|--list-certs) args+=(
      '(-f --flag)'{-f+,--flag=}"[filter by specified X.509 certificate flag]:certificate flag:(
        ${(j< >)${(@q-)cert_flags}}
      )"
      '(-p --pem)'{-p,--pem}'[display PEM encoding of certificate]'
      '(-s --subject)'{-s+,--subject=}'[filter by specified certificate subject]:certificate subject'
      '(-S --short)'{-S,--short}'[omit some certificate details]'
      '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:(
        ${(j< >)${(@q-)cert_types}}
      )"
      '(-u --utc)'{-u,--utc}'[use UTC for time fields]'
    ) ;;
  esac

  _arguments -s -S : $args && ret=0
  return ret
}

_swanctl "$@"