Bump to 1.14.1
[platform/upstream/augeas.git] / lenses / sudoers.aug
1 (*
2 Module: Sudoers
3   Parses /etc/sudoers
4
5 Author: Raphael Pinson <raphink@gmail.com>
6
7 About: Reference
8   This lens tries to keep as close as possible to `man sudoers` where possible.
9
10 For example, recursive definitions such as
11
12      > Cmnd_Spec_List ::= Cmnd_Spec |
13      >                    Cmnd_Spec ',' Cmnd_Spec_List
14
15 are replaced by
16
17   >   let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*
18
19 since Augeas cannot deal with recursive definitions.
20 The definitions from `man sudoers` are put as commentaries for reference
21 throughout the file. More information can be found in the manual.
22
23 About: License
24   This file is licensed under the LGPL v2+, like the rest of Augeas.
25
26
27 About: Lens Usage
28   Sample usage of this lens in augtool
29
30     * Set first Defaults to apply to the "LOCALNET" network alias
31       > set /files/etc/sudoers/Defaults[1]/type "@LOCALNET"
32     * List all user specifications applying explicitly to the "admin" Unix group
33       > match /files/etc/sudoers/spec/user "%admin"
34     * Remove the full 3rd user specification
35       > rm /files/etc/sudoers/spec[3]
36
37 About: Configuration files
38   This lens applies to /etc/sudoers. See <filter>.
39 *)
40
41
42
43 module Sudoers =
44   autoload xfm
45
46 (************************************************************************
47  * Group:                 USEFUL PRIMITIVES
48  *************************************************************************)
49
50 (* Group: Generic primitives *)
51 (* Variable: eol *)
52 let eol       = Util.eol
53
54 (* Variable: indent *)
55 let indent    = Util.indent
56
57
58 (* Group: Separators *)
59
60 (* Variable: sep_spc *)
61 let sep_spc  = Sep.space
62
63 (* Variable: sep_cont *)
64 let sep_cont = Sep.cl_or_space
65
66 (* Variable: sep_cont_opt *)
67 let sep_cont_opt = Sep.cl_or_opt_space
68
69 (* Variable: sep_cont_opt_build *)
70 let sep_cont_opt_build (sep:string) =
71    del (Rx.cl_or_opt_space . sep . Rx.cl_or_opt_space) (" " . sep . " ")
72
73 (* Variable: sep_com *)
74 let sep_com = sep_cont_opt_build ","
75
76 (* Variable: sep_eq *)
77 let sep_eq   = sep_cont_opt_build "="
78
79 (* Variable: sep_col *)
80 let sep_col  = sep_cont_opt_build ":"
81
82 (* Variable: sep_dquote *)
83 let sep_dquote   = Util.del_str "\""
84
85 (* Group: Negation expressions *)
86
87 (************************************************************************
88  * View: del_negate
89  *   Delete an even number of '!' signs
90  *************************************************************************)
91 let del_negate = del /(!!)*/ ""
92
93 (************************************************************************
94  * View: negate_node
95  *   Negation of boolean values for <defaults>. Accept one optional '!'
96  *   and produce a 'negate' node if there is one.
97  *************************************************************************)
98 let negate_node = [ del "!" "!" . label "negate" ]
99
100 (************************************************************************
101  * View: negate_or_value
102  *   A <del_negate>, followed by either a negated key, or a key/value pair
103  *************************************************************************)
104 let negate_or_value (key:lens) (value:lens) =
105   [ del_negate . (negate_node . key | key . value) ]
106
107 (* Group: Stores *)
108
109 (* Variable: sto_to_com_cmnd
110 sto_to_com_cmnd does not begin or end with a space *)
111
112 let sto_to_com_cmnd = del_negate . negate_node? . (
113       let alias = Rx.word - /(NO)?(PASSWD|EXEC|SETENV)/
114      in let non_alias = /[\/a-z]([^,:#()\n\\]|\\\\[=:,\\])*[^,=:#() \t\n\\]|[^,=:#() \t\n\\]/
115    in store (alias | non_alias))
116
117 (* Variable: sto_to_com
118
119 There could be a \ in the middle of a command *)
120 let sto_to_com      = store /([^,=:#() \t\n\\][^,=:#()\n]*[^,=:#() \t\n\\])|[^,=:#() \t\n\\]/
121
122 (* Variable: sto_to_com_host *)
123 let sto_to_com_host = store /[^,=:#() \t\n\\]+/
124
125
126 (* Variable: sto_to_com_user
127 Escaped spaces and NIS domains and allowed*)
128 let sto_to_com_user =
129       let nis_re = /([A-Z]([-A-Z0-9]|(\\\\[ \t]))*+\\\\\\\\)/
130    in let user_re = /[%+@a-z]([-A-Za-z0-9._+]|(\\\\[ \t])|\\\\\\\\[A-Za-z0-9])*/ - /@include(dir)?/
131    in let alias_re = /[A-Z_]+/
132    in store ((nis_re? . user_re) | alias_re)
133
134 (* Variable: to_com_chars *)
135 let to_com_chars        = /[^",=#() \t\n\\]+/ (* " relax emacs *)
136
137 (* Variable: to_com_dquot *)
138 let to_com_dquot        = /"[^",=#()\n\\]+"/ (* " relax emacs *)
139
140 (* Variable: sto_to_com_dquot *)
141 let sto_to_com_dquot    = store (to_com_chars|to_com_dquot)
142
143 (* Variable: sto_to_com_col *)
144 let sto_to_com_col      = store to_com_chars
145
146 (* Variable: sto_to_eq *)
147 let sto_to_eq  = store /[^,=:#() \t\n\\]+/
148
149 (* Variable: sto_to_spc *)
150 let sto_to_spc = store /[^", \t\n\\]+|"[^", \t\n\\]+"/
151
152 (* Variable: sto_to_spc_no_dquote *)
153 let sto_to_spc_no_dquote = store /[^",# \t\n\\]+/ (* " relax emacs *)
154
155 (* Variable: sto_integer *)
156 let sto_integer = store /-?[0-9]+/
157
158
159 (* Group: Comments and empty lines *)
160
161 (* View: comment
162 Map comments in "#comment" nodes *)
163 let comment =
164   let sto_to_eol = store (/([^ \t\n].*[^ \t\n]|[^ \t\n])/ - /include(dir)?.*/) in
165   [ label "#comment" . del /[ \t]*#[ \t]*/ "# " . sto_to_eol . eol ]
166
167 (* View: comment_eol
168 Requires a space before the # *)
169 let comment_eol = Util.comment_generic /[ \t]+#[ \t]*/ " # "
170
171 (* View: comment_or_eol
172 A <comment_eol> or <eol> *)
173 let comment_or_eol = comment_eol | (del /([ \t]+#\n|[ \t]*\n)/ "\n")
174
175 (* View: empty
176 Map empty lines *)
177 let empty   = [ del /[ \t]*#?[ \t]*\n/ "\n" ]
178
179 (* View: includedir *)
180 let includedir =
181   [ key /(#|@)include(dir)?/ . Sep.space . store Rx.fspath . eol ]
182
183
184 (************************************************************************
185  * Group:                                    ALIASES
186  *************************************************************************)
187
188 (************************************************************************
189  * View: alias_field
190  *   Generic alias field to gather all Alias definitions
191  *
192  *   Definition:
193  *     > User_Alias ::= NAME '=' User_List
194  *     > Runas_Alias ::= NAME '=' Runas_List
195  *     > Host_Alias ::= NAME '=' Host_List
196  *     > Cmnd_Alias ::= NAME '=' Cmnd_List
197  *
198  *   Parameters:
199  *     kw:string - the label string
200  *     sto:lens  - the store lens
201  *************************************************************************)
202 let alias_field (kw:string) (sto:lens) = [ label kw . sto ]
203
204 (* View: alias_list
205      List of <alias_fields>, separated by commas *)
206 let alias_list  (kw:string) (sto:lens) =
207   Build.opt_list (alias_field kw sto) sep_com
208
209 (************************************************************************
210  * View: alias_name
211  *   Name of an <alias_entry_single>
212  *
213  *   Definition:
214  *     > NAME ::= [A-Z]([A-Z][0-9]_)*
215  *************************************************************************)
216 let alias_name
217     = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]
218
219 (************************************************************************
220  * View: alias_entry_single
221  *   Single <alias_entry>, named using <alias_name> and listing <alias_list>
222  *
223  *   Definition:
224  *     > Alias_Type NAME = item1, item2, ...
225  *
226  *   Parameters:
227  *     field:string - the field name, passed to <alias_list>
228  *     sto:lens     - the store lens, passed to <alias_list>
229  *************************************************************************)
230 let alias_entry_single (field:string) (sto:lens)
231     = [ label "alias" . alias_name . sep_eq . alias_list field sto ]
232
233 (************************************************************************
234  * View: alias_entry
235  *   Alias entry, a list of comma-separated <alias_entry_single> fields
236  *
237  *   Definition:
238  *     > Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
239  *
240  *   Parameters:
241  *     kw:string    - the alias keyword string
242  *     field:string - the field name, passed to <alias_entry_single>
243  *     sto:lens     - the store lens, passed to <alias_entry_single>
244  *************************************************************************)
245 let alias_entry (kw:string) (field:string) (sto:lens)
246     = [ indent . key kw . sep_cont . alias_entry_single field sto
247           . ( sep_col . alias_entry_single field sto )* . comment_or_eol ]
248
249 (* TODO: go further in user definitions *)
250 (* View: user_alias
251      User_Alias, see <alias_field> *)
252 let user_alias  = alias_entry "User_Alias" "user" sto_to_com
253 (* View: runas_alias
254      Run_Alias, see <alias_field> *)
255 let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com
256 (* View: host_alias
257      Host_Alias, see <alias_field> *)
258 let host_alias  = alias_entry "Host_Alias" "host" sto_to_com
259 (* View: cmnd_alias
260      Cmnd_Alias, see <alias_field> *)
261 let cmnd_alias  = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd
262
263
264 (************************************************************************
265  * View: alias
266  *   Every kind of Alias entry,
267  *     see <user_alias>, <runas_alias>, <host_alias> and <cmnd_alias>
268  *
269  *   Definition:
270  *     > Alias ::= 'User_Alias'  User_Alias (':' User_Alias)* |
271  *     >           'Runas_Alias' Runas_Alias (':' Runas_Alias)* |
272  *     >           'Host_Alias'  Host_Alias (':' Host_Alias)* |
273  *     >           'Cmnd_Alias'  Cmnd_Alias (':' Cmnd_Alias)*
274  *************************************************************************)
275 let alias = user_alias | runas_alias | host_alias | cmnd_alias
276
277 (************************************************************************
278  * Group:                          DEFAULTS
279  *************************************************************************)
280
281
282 (************************************************************************
283  * View: default_type
284  *   Type definition for <defaults>
285  *
286  *   Definition:
287  *     > Default_Type ::= 'Defaults' |
288  *     >                  'Defaults' '@' Host_List |
289  *     >                  'Defaults' ':' User_List |
290  *     >                  'Defaults' '!' Cmnd_List |
291  *     >                  'Defaults' '>' Runas_List
292  *************************************************************************)
293 let default_type     =
294   let value = store /[@:!>][^ \t\n\\]+/ in
295   [ label "type" . value ]
296
297 (************************************************************************
298  * View: parameter_flag
299  *   A flag parameter for <defaults>
300  *
301  *   Flags are implicitly boolean and can be turned off via the '!'  operator.
302  *   Some integer, string and list parameters may also be used in a boolean
303  *     context to disable them.
304  *************************************************************************)
305 let parameter_flag_kw    = "always_set_home" | "authenticate" | "env_editor"
306                          | "env_reset" | "fqdn" | "ignore_dot"
307                          | "ignore_local_sudoers" | "insults" | "log_host"
308                          | "log_year" | "long_otp_prompt" | "mail_always"
309                          | "mail_badpass" | "mail_no_host" | "mail_no_perms"
310                          | "mail_no_user" | "noexec" | "path_info"
311                          | "passprompt_override" | "preserve_groups"
312                          | "requiretty" | "root_sudo" | "rootpw" | "runaspw"
313                          | "set_home" | "set_logname" | "setenv"
314                          | "shell_noargs" | "stay_setuid" | "targetpw"
315                          | "tty_tickets" | "visiblepw" | "closefrom_override"
316                          | "closefrom_override" | "compress_io" | "fast_glob"
317                          | "log_input" | "log_output" | "pwfeedback"
318                          | "umask_override" | "use_pty" | "match_group_by_gid"
319                          | "always_query_group_plugin"
320
321 let parameter_flag       = [ del_negate . negate_node?
322                                . key parameter_flag_kw ]
323
324 (************************************************************************
325  * View: parameter_integer
326  *   An integer parameter for <defaults>
327  *************************************************************************)
328 let parameter_integer_nobool_kw = "passwd_tries"
329
330 let parameter_integer_nobool    = [ key parameter_integer_nobool_kw . sep_eq
331                                       . del /"?/ "" . sto_integer
332                                       . del /"?/ "" ]
333
334
335 let parameter_integer_bool_kw   = "loglinelen" | "passwd_timeout"
336                                 | "timestamp_timeout" | "umask"
337
338 let parameter_integer_bool      =
339   negate_or_value
340     (key parameter_integer_bool_kw)
341     (sep_eq . del /"?/ "" . sto_integer . del /"?/ "")
342
343 let parameter_integer           = parameter_integer_nobool
344                                 | parameter_integer_bool
345
346 (************************************************************************
347  * View: parameter_string
348  *   A string parameter for <defaults>
349  *
350  *   An odd number of '!' operators negate the value of the item;
351  *      an even number just cancel each other out.
352  *************************************************************************)
353 let parameter_string_nobool_kw = "badpass_message" | "editor" | "mailsub"
354                                | "noexec_file" | "passprompt" | "runas_default"
355                                | "syslog_badpri" | "syslog_goodpri"
356                                | "timestampdir" | "timestampowner" | "secure_path"
357
358 let parameter_string_nobool    = [ key parameter_string_nobool_kw . sep_eq
359                                      . sto_to_com_dquot ]
360
361 let parameter_string_bool_kw   = "exempt_group" | "lecture" | "lecture_file"
362                                | "listpw" | "logfile" | "mailerflags"
363                                | "mailerpath" | "mailto" | "mailfrom" 
364                                | "syslog" | "verifypw"
365
366 let parameter_string_bool      =
367   negate_or_value
368     (key parameter_string_bool_kw)
369     (sep_eq . sto_to_com_dquot)
370
371 let parameter_string           = parameter_string_nobool
372                                | parameter_string_bool
373
374 (************************************************************************
375  * View: parameter_lists
376  *   A single list parameter for <defaults>
377  *
378  *   All lists can be used in a boolean context
379  *   The argument may be a double-quoted, space-separated list or a single
380  *      value without double-quotes.
381  *   The list can be replaced, added to, deleted from, or disabled
382  *      by using the =, +=, -=, and ! operators respectively.
383  *   An odd number of '!' operators negate the value of the item;
384  *      an even number just cancel each other out.
385  *************************************************************************)
386 let parameter_lists_kw           = "env_check" | "env_delete" | "env_keep"
387 let parameter_lists_value        = [ label "var" . sto_to_spc_no_dquote ]
388 let parameter_lists_value_dquote = [ label "var"
389                                      . del /"?/ "" . sto_to_spc_no_dquote
390                                      . del /"?/ "" ]
391
392 let parameter_lists_values = parameter_lists_value_dquote
393                            | ( sep_dquote . parameter_lists_value
394                                . ( sep_cont . parameter_lists_value )+
395                                . sep_dquote )
396
397 let parameter_lists_sep    = sep_cont_opt
398                              . ( [ del "+" "+" . label "append" ]
399                                | [ del "-" "-" . label "remove" ] )?
400                              . del "=" "=" . sep_cont_opt
401
402 let parameter_lists        =
403   negate_or_value
404     (key parameter_lists_kw)
405     (parameter_lists_sep . parameter_lists_values)
406
407 (************************************************************************
408  * View: parameter
409  *   A single parameter for <defaults>
410  *
411  *   Definition:
412  *     > Parameter ::= Parameter '=' Value |
413  *     >               Parameter '+=' Value |
414  *     >               Parameter '-=' Value |
415  *     >               '!'* Parameter
416  *
417  *     Parameters may be flags, integer values, strings, or lists.
418  *
419  *************************************************************************)
420 let parameter        = parameter_flag | parameter_integer
421                      | parameter_string | parameter_lists
422
423 (************************************************************************
424  * View: parameter_list
425  *   A list of comma-separated <parameters> for <defaults>
426  *
427  *   Definition:
428  *     > Parameter_List ::= Parameter |
429  *     >                    Parameter ',' Parameter_List
430  *************************************************************************)
431 let parameter_list   = parameter . ( sep_com . parameter )*
432
433 (************************************************************************
434  * View: defaults
435  *   A Defaults entry
436  *
437  *   Definition:
438  *     > Default_Entry ::= Default_Type Parameter_List
439  *************************************************************************)
440 let defaults = [ indent . key "Defaults" . default_type? . sep_cont
441                    . parameter_list . comment_or_eol ]
442
443
444
445 (************************************************************************
446  * Group:                     USER SPECIFICATION
447  *************************************************************************)
448
449 (************************************************************************
450  * View: runas_spec
451  *   A runas specification for <spec>, using <alias_list> for listing
452  *   users and/or groups used to run a command
453  *
454  *   Definition:
455  *     > Runas_Spec ::= '(' Runas_List ')' |
456  *     >                '(:' Runas_List ')' |
457  *     >                '(' Runas_List ':' Runas_List ')'
458  *************************************************************************)
459 let runas_spec_user       = alias_list "runas_user" sto_to_com
460 let runas_spec_group      = Util.del_str ":" . indent
461                             . alias_list "runas_group" sto_to_com
462
463 let runas_spec_usergroup  = runas_spec_user . indent . runas_spec_group
464
465 let runas_spec = Util.del_str "("
466                  . (runas_spec_user
467                     | runas_spec_group
468                     | runas_spec_usergroup )
469                  . Util.del_str ")" . sep_cont_opt
470
471 (************************************************************************
472  * View: tag_spec
473  *   Tag specification for <spec>
474  *
475  *   Definition:
476  *     > Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
477  *     >              'SETENV:' | 'NOSETENV:')
478  *************************************************************************)
479 let tag_spec   =
480   [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]
481
482 (************************************************************************
483  * View: cmnd_spec
484  *   Command specification for <spec>,
485  *     with optional <runas_spec> and any amount of <tag_specs>
486  *
487  *   Definition:
488  *     > Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
489  *************************************************************************)
490 let cmnd_spec  =
491   [ label "command" .  runas_spec? . tag_spec* . sto_to_com_cmnd ]
492
493 (************************************************************************
494  * View: cmnd_spec_list
495  *   A list of comma-separated <cmnd_specs>
496  *
497  *   Definition:
498  *     > Cmnd_Spec_List ::= Cmnd_Spec |
499  *     >                    Cmnd_Spec ',' Cmnd_Spec_List
500  *************************************************************************)
501 let cmnd_spec_list = Build.opt_list cmnd_spec sep_com
502
503
504 (************************************************************************
505  * View: spec_list
506  *   Group of hosts with <cmnd_spec_list>
507  *************************************************************************)
508 let spec_list = [ label "host_group" . alias_list "host" sto_to_com_host
509                     . sep_eq . cmnd_spec_list ]
510
511 (************************************************************************
512  * View: spec
513  *   A user specification, listing colon-separated <spec_lists>
514  *
515  *   Definition:
516  *     > User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
517  *     >               (':' Host_List '=' Cmnd_Spec_List)*
518  *************************************************************************)
519 let spec = [ label "spec" . indent
520                . alias_list "user" sto_to_com_user . sep_cont
521                . Build.opt_list spec_list sep_col
522                . comment_or_eol ]
523
524
525 (************************************************************************
526  * Group:                        LENS & FILTER
527  *************************************************************************)
528
529 (* View: lns
530      The sudoers lens, any amount of
531        * <empty> lines
532        * <comments>
533        * <includedirs>
534        * <aliases>
535        * <defaults>
536        * <specs>
537 *)
538 let lns = ( empty | comment | includedir | alias | defaults | spec  )*
539
540 (* View: filter *)
541 let filter = (incl "/etc/sudoers")
542     . (incl "/usr/local/etc/sudoers")
543     . (incl "/etc/sudoers.d/*")
544     . (incl "/usr/local/etc/sudoers.d/*")
545     . (incl "/opt/csw/etc/sudoers")
546     . (incl "/etc/opt/csw/sudoers")
547     . Util.stdexcl
548
549 let xfm = transform lns filter