1#compdef swanctl
2
3# Completion for strongSwan's swanctl. See also ipsec, which is deprecated but
4# still supported by strongSwan, and also used by Openswan/Libreswan.
5#
6# Note that in most cases elevated privileges are required to connect to the
7# VICI socket, so the gain-privileges style may be necessary to complete SA
8# names and the like: zstyle ':completion:*:swanctl/*' gain-privileges yes
9#
10# Other notes:
11# - One of swanctl's selling points is that it can provide 'raw' structured
12#   responses for scripting, etc. In practice, though, the output formatted for
13#   humans seems easier to 'parse' from the shell than the plist-like raw output
14# - @todo We don't complete authority names, pool names, peer IPs, etc.
15
16# Complete connection names, SA names, or SA unique IDs. The distinctions
17# between concepts like 'connections' and 'SAs' are very blurry here, partially
18# for convenience and partially due to author confusion
19# --child => complete only child/CHILD_SA names/IDs
20# --ids   => complete unique SA IDs rather than connection/SA names
21# --ike   => complete only connection/IKE_SA names/IDs
22(( $+functions[_swanctl_connections] )) ||
23_swanctl_connections() {
24  local i which
25  local -a expl tmp matches
26  local -A opts
27
28  zparseopts -D -E -A opts - -child -ids -ike
29
30  tmp=( ${(@M)${(f)"$(
31    _call_program -p swanctl-sas $words[1] -l
32  )"}:#[^:]##: \#<->*} )
33  (( $+opts[--ids] )) || tmp+=( ${(@M)${(f)"$(
34    _call_program -p swanctl-conns $words[1] -L
35  )"}:#[^:]##: ([A-Z][A-Za-z0-9]#|),*} )
36
37  for i in $tmp; do
38    if (( $+opts[--child] )) && [[ $i != [[:space:]]* ]]; then
39      continue
40    elif (( $+opts[--ike] )) && [[ $i == [[:space:]]* ]]; then
41      continue
42    fi
43
44    # <name>: #<unique id>, ...
45    i=${i//[#:,]/ }
46
47    if (( $+opts[--ids] )); then
48      matches+=( "${i[(w)2]}:${i[(w)1]} #${i[(w)2]}" )
49    else
50      matches+=( ${i[(w)1]} )
51    fi
52  done
53
54  if (( $+opts[--ids] )); then
55    matches=( ${(onu)matches} )
56    if (( $+opts[--child] )); then
57      which=CHILD_
58    elif (( $+opts[--ike] )); then
59      which=IKE_
60    fi
61    _describe -x2Vt sa-ids "${which}SA unique ID" matches
62  else
63    if (( $+opts[--child] )); then
64      which='child '
65    elif (( $+opts[--ike] )); then
66      which='IKE '
67    fi
68    _wanted -x connections expl "${which}connection/SA name" compadd - $matches
69  fi
70}
71
72_swanctl() {
73  # Although swanctl will correctly parse multiple short options in the first
74  # word, as in `swanctl -lh`, it won't actually *do* anything with the
75  # subsequent options -- so we'll require that they be separated. Also, --help
76  # doesn't take any further options, so just stop if we've got that
77  if (( CURRENT == 2 )) || [[ $words[2] == (-h*|--help) ]]; then
78    _arguments : \
79      '(-)'{-a,--load-pools}'[(re)load pool configuration]' \
80      '(-)'{-A,--list-pools}'[display loaded pool configurations]' \
81      '(-)'{-b,--load-authorities}'[(re)load authority configuration]' \
82      '(-)'{-B,--list-authorities}'[display loaded authority configurations]' \
83      '(-)'{-c,--load-conns}'[(re)load connection configuration]' \
84      '(-)'{-C,--counters}'[display or reset IKE event counters]' \
85      '(-)'{-d,--redirect}'[redirect IKE_SA]' \
86      '(-)'{-f,--flush-certs}'[flush cached certificates]' \
87      '(-)'{-g,--list-algs}'[display loaded algorithms]' \
88      '(-)'{-h,--help}'[display help information]' \
89      '(-)'{-i,--initiate}'[initiate a connection]' \
90      '(-)'{-l,--list-sas}'[display currently active IKE_SAs]' \
91      '(-)'{-L,--list-conns}'[display loaded configurations]' \
92      '(-)'{-m,--monitor-sa}'[monitor for IKE_SA and CHILD_SA changes]' \
93      '(-)'{-p,--install}'[install trap or shunt policy]' \
94      '(-)'{-P,--list-pols}'[display currently installed policies]' \
95      '(-)'{-q,--load-all}'[load credentials, authorities, pools, and connections]' \
96      '(-)'{-r,--reload-settings}'[reload daemon strongswan.conf]' \
97      '(-)'{-R,--rekey}'[rekey SA]' \
98      '(-)'{-s,--load-creds}'[(re)load credentials]' \
99      '(-)'{-S,--stats}'[display daemon statistics]' \
100      '(-)'{-t,--terminate}'[terminate connection]' \
101      '(-)'{-T,--log}'[trace logging output]' \
102      '(-)'{-u,--uninstall}'[uninstall trap or shunt policy]' \
103      '(-)'{-v,--version}'[display version information]' \
104      '(-)'{-x,--list-certs}'[display stored certificates]'
105    return
106  fi
107
108  local ret=1 cmd
109  local -a args cert_flags cert_types
110
111  cert_flags=( aa any ca none ocsp )
112  cert_types=( ocsp_response pubkey x509 x509_ac x509_crl )
113
114  if [[ $words[2] == -[^-]* ]]; then
115    cmd=${(M)words[2]#??}
116  else
117    cmd=$words[2]
118  fi
119  words=( $words[1] "${(@)words[3,-1]}" )
120  (( CURRENT-- ))
121
122  # Technically, only -v, -u, and -+ are truly global command options. However,
123  # in practice, all commands also support -h, -P, and -r
124  args=(
125    '(: * -)'{-h,--help}'[display help information]'
126    '(-P -r --pretty --raw)'{-P,--pretty}'[dump raw response message in pretty print]'
127    '(-P -r --pretty --raw)'{-r,--raw}'[dump raw response message]'
128    # https://wiki.strongswan.org/projects/strongswan/wiki/LoggerConfiguration
129    # https://github.com/strongswan/strongswan/blob/master/src/libstrongswan/utils/debug.h
130    '(-v --debug)'{-v+,--debug=}'[specify debug level]:debug level [1]:((
131      -1\:"absolutely silent (SILENT)"
132      0\:"basic auditing (AUDIT)"
133      1\:"generic control flow with errors (CTRL)"
134      2\:"detailed control flow (DIAG)"
135      3\:"raw binary blobs (RAW)"
136      4\:"sensitive data (PRIVATE)"
137    ))'
138    '(-u --uri)'{-u+,--uri=}'[specify service URI to connect to]:VICI service URI:_urls'
139    '(-+ --options)'{'-\++',--options=}'[read command-line options from specified file]:options file:_files'
140  )
141
142  case $cmd in
143    -A|--list-pools) args+=(
144      '(-n --name)'{-n+,--name=}'[filter by specified pool name]:pool name'
145      '(-l --leases)'{-l,--leases}'[display leases of each pool]'
146    ) ;;
147    -B|--list-authorities) args+=(
148      '(-n --name)'{-n+,--name=}'[filter by specified authority name]:authority name'
149    ) ;;
150    -C|--counters) args+=(
151      '(-a -n --all --name)'{-a,--all}'[display/reset counters for all tracked connections]'
152      '(-a -n --all --name)'{-n+,--name=}'[specify connection name]: :_swanctl_connections --ike'
153      '(-r --reset)'{-r,--reset}'[reset counters]'
154    ) ;;
155    -d|--redirect) args+=(
156      '(-d --peer-id)'{-d+,--peer-id=}'[redirect by IKE_SA matching specified peer identity]:peer identity'
157      '(-g --gateway)'{-g+,--gateway=}'[redirect to specified gateway]:target gateway'
158      '(-i --ike)'{-i+,--ike=}'[redirect by specified IKE_SA name]: :_swanctl_connections --ike'
159      '(-I --ike-id)'{-I+,--ike-id=}'[redirect by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
160      '(-p --peer-ip)'{-p+,--peer-ip=}'[redirect by IKE_SA matching specified peer IP]:peer IP address'
161    ) ;;
162    -f|--flush-certs) args+=(
163      '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:(
164        ${(j< >)${(@q-)cert_types}}
165      )"
166    ) ;;
167    -i|--initiate) args+=(
168      '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child'
169      '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike"
170      '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)'
171    ) ;;
172    -l|--list-sas) args+=(
173      '(-i --ike)'{-i+,--ike=}'[filter by specified IKE_SA name]: :_swanctl_connections --ike'
174      '(-I --ike-id)'{-I+,--ike-id=}'[filter by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
175      '(-n --noblock)'{-n,--noblock}'[do not wait for IKE_SAs in use]'
176    ) ;;
177    -p|-u|--install|--uninstall) args+=(
178      '(-c --child)'{-c+,--child=}'[specify CHILD_SA name]: :_swanctl_connections --child'
179      '(-i --ike)'{-i+,--ike=}"[specify CHILD_SA's connection name]: :_swanctl_connections --ike"
180    ) ;;
181    -P|--list-pols) args+=(
182      '(-c --child)'{-c+,--child=}'[filter by specified CHILD_SA name]: :_swanctl_connections --child'
183      '(-d --drop)'{-d,--drop}'[list drop policies]'
184      '(-p --pass)'{-p,--pass}'[list bypass policies]'
185      '(-t --trap)'{-t,--trap}'[list trap policies]'
186    ) ;;
187    -q|-s|--load-all|--load-creds) args+=(
188      '(-c --clear)'{-c,--clear}'[clear previously loaded credentials]'
189      '(-n --noprompt)'{-n,--noprompt}'[do not prompt for passwords]'
190    ) ;;
191    -R|--rekey) args+=(
192      '(-c --child)'{-c+,--child=}'[rekey by specified CHILD_SA name]: :_swanctl_connections --child'
193      '(-C --child-id)'{-C+,--child-id=}'[rekey by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child'
194      '(-i --ike)'{-i+,--ike=}'[rekey by specified IKE_SA name]: :_swanctl_connections --ike'
195      '(-I --ike-id)'{-I+,--ike-id=}'[rekey by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
196    ) ;;
197    -t|--terminate) args+=(
198      '(-t --timeout)'{-t+,--timeout=}'[specify timeout before detaching]:timeout (seconds)'
199      '(-c --child)'{-c+,--child=}'[terminate by specified CHILD_SA name]: :_swanctl_connections --child'
200      '(-C --child-id)'{-C+,--child-id=}'[terminate by specified CHILD_SA unique ID]: :_swanctl_connections --ids --child'
201      '(-i --ike)'{-i+,--ike=}'[terminate by specified IKE_SA name]: :_swanctl_connections --ike'
202      '(-I --ike-id)'{-I+,--ike-id=}'[terminate by specified IKE_SA unique ID]: :_swanctl_connections --ids --ike'
203    ) ;;
204    -v|--version) args+=(
205      '(-d --daemon)'{-d,--daemon}'[query daemon version]'
206    ) ;;
207    -x|--list-certs) args+=(
208      '(-f --flag)'{-f+,--flag=}"[filter by specified X.509 certificate flag]:certificate flag:(
209        ${(j< >)${(@q-)cert_flags}}
210      )"
211      '(-p --pem)'{-p,--pem}'[display PEM encoding of certificate]'
212      '(-s --subject)'{-s+,--subject=}'[filter by specified certificate subject]:certificate subject'
213      '(-S --short)'{-S,--short}'[omit some certificate details]'
214      '(-t --type)'{-t+,--type=}"[filter by specified certificate type]:certificate type:(
215        ${(j< >)${(@q-)cert_types}}
216      )"
217      '(-u --utc)'{-u,--utc}'[use UTC for time fields]'
218    ) ;;
219  esac
220
221  _arguments -s -S : $args && ret=0
222  return ret
223}
224
225_swanctl "$@"
226