Parses /etc/sudoers
Author: Raphael Pinson rap@gmai l.com hink
Sudoers | Parses /etc/sudoers |
Reference | This lens tries to keep as close as possible to `man sudoers` where possible. |
License | This file is licensed under the LGPL v2+, like the rest of Augeas. |
Lens Usage | Sample usage of this lens in augtool |
Configuration files | This lens applies to /etc/sudoers. |
USEFUL PRIMITIVES | |
Generic primitives | |
eol | |
indent | |
Separators | |
sep_spc | |
sep_cont | |
sep_cont_opt | |
sep_cont_opt_build | |
sep_com | |
sep_eq | |
sep_col | |
sep_dquote | |
Negation expressions | |
del_negate | Delete an even number of ‘!’ |
negate_node | Negation of boolean values for defaults. |
negate_or_value | A del_negate, followed by either a negated key, or a key/value pair |
Stores | |
sto_to_com_cmnd | sto_to_com_cmnd does not begin or end with a space |
sto_to_com | There could be a \ in the middle of a command |
sto_to_com_host | |
sto_to_com_user | Escaped spaces and NIS domains and allowed |
to_com_chars | |
to_com_dquot | |
sto_to_com_dquot | |
sto_to_com_col | |
sto_to_eq | |
sto_to_spc | |
sto_to_spc_no_dquote | |
sto_integer | |
Comments and empty lines | |
comment | Map comments in “#comment” nodes |
comment_eol | Requires a space before the # |
comment_or_eol | A comment_eol or eol |
empty | Map empty lines |
includedir | |
ALIASES | |
alias_field | Generic alias field to gather all Alias definitions |
alias_list | List of alias_fields, separated by commas |
alias_name | Name of an alias_entry_single |
alias_entry_single | Single alias_entry, named using alias_name and listing alias_list |
alias_entry | Alias entry, a list of comma-separated alias_entry_single fields |
user_alias | User_Alias, see alias_field |
runas_alias | Run_Alias, see alias_field |
host_alias | Host_Alias, see alias_field |
cmnd_alias | Cmnd_Alias, see alias_field |
alias | Every kind of Alias entry, see user_alias, runas_alias, host_alias and cmnd_alias |
DEFAULTS | |
default_type | Type definition for defaults |
parameter_flag | A flag parameter for defaults |
parameter_integer | An integer parameter for defaults |
parameter_string | A string parameter for defaults |
parameter_lists | A single list parameter for defaults |
parameter | A single parameter for defaults |
parameter_list | A list of comma-separated parameters for defaults |
defaults | A Defaults entry |
USER SPECIFICATION | |
runas_spec | A runas specification for spec, using alias_list for listing users and/or groups used to run a command |
tag_spec | Tag specification for spec |
cmnd_spec | Command specification for spec, with optional runas_spec and any amount of tag_specs |
cmnd_spec_list | A list of comma-separated cmnd_specs |
spec_list | Group of hosts with cmnd_spec_list |
spec | A user specification, listing colon-separated spec_lists |
LENS & FILTER | |
lns | The sudoers lens, any amount of |
filter |
This lens tries to keep as close as possible to `man sudoers` where possible.
For example, recursive definitions such as
Cmnd_Spec_List ::= Cmnd_Spec | Cmnd_Spec ',' Cmnd_Spec_List
are replaced by
let cmnd_spec_list = cmnd_spec . ( sep_com . cmnd_spec )*
since Augeas cannot deal with recursive definitions. The definitions from `man sudoers` are put as commentaries for reference throughout the file. More information can be found in the manual.
Sample usage of this lens in augtool
set /files/etc/sudoers/Defaults[1]/type "@LOCALNET"
match /files/etc/sudoers/spec/user "%admin"
rm /files/etc/sudoers/spec[3]
This lens applies to /etc/sudoers. See filter.
let negate_node = [ del "!" "!" . label "negate" ]
Negation of boolean values for defaults. Accept one optional ‘!’ and produce a ‘negate’ node if there is one.
let negate_or_value (key:lens) (value:lens) = [ del_negate . (negate_node . key | key . value) ]
A del_negate, followed by either a negated key, or a key/value pair
let comment_or_eol = comment_eol | (del /([ \t]+#\n|[ \t]*\n)/ "\n")
A comment_eol or eol
let alias_field (kw:string) (sto:lens) = [ label kw . sto ]
Generic alias field to gather all Alias definitions
User_Alias ::= NAME '=' User_List Runas_Alias ::= NAME '=' Runas_List Host_Alias ::= NAME '=' Host_List Cmnd_Alias ::= NAME '=' Cmnd_List
kw:string | the label string |
sto:lens | the store lens |
let alias_list (kw:string) (sto:lens) = Build.opt_list (alias_field kw sto) sep_com
List of alias_fields, separated by commas
let alias_name = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]
Name of an alias_entry_single
NAME ::= [A-Z]([A-Z][0-9]_)*
let alias_entry_single (field:string) (sto:lens) = [ label "alias" . alias_name . sep_eq . alias_list field sto ]
Single alias_entry, named using alias_name and listing alias_list
Alias_Type NAME = item1, item2, ...
field:string | the field name, passed to alias_list |
sto:lens | the store lens, passed to alias_list |
let alias_entry (kw:string) (field:string) (sto:lens) = [ indent . key kw . sep_cont . alias_entry_single field sto . ( sep_col . alias_entry_single field sto )* . comment_or_eol ]
Alias entry, a list of comma-separated alias_entry_single fields
Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
kw:string | the alias keyword string |
field:string | the field name, passed to alias_entry_single |
sto:lens | the store lens, passed to alias_entry_single |
let user_alias = alias_entry "User_Alias" "user" sto_to_com
User_Alias, see alias_field
let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com
Run_Alias, see alias_field
let host_alias = alias_entry "Host_Alias" "host" sto_to_com
Host_Alias, see alias_field
let cmnd_alias = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd
Cmnd_Alias, see alias_field
let alias = user_alias | runas_alias | host_alias | cmnd_alias
Every kind of Alias entry, see user_alias, runas_alias, host_alias and cmnd_alias
Alias ::= 'User_Alias' User_Alias (':' User_Alias)* | 'Runas_Alias' Runas_Alias (':' Runas_Alias)* | 'Host_Alias' Host_Alias (':' Host_Alias)* | 'Cmnd_Alias' Cmnd_Alias (':' Cmnd_Alias)*
let default_type = let value = store /[@:!>][^ \t\n\\]+/ in [ label "type" . value ]
Type definition for defaults
Default_Type ::= 'Defaults' | 'Defaults' '@' Host_List | 'Defaults' ':' User_List | 'Defaults' '!' Cmnd_List | 'Defaults' '>' Runas_List
let parameter_flag_kw = "always_set_home" | "authenticate" | "env_editor" | "env_reset" | "fqdn" | "ignore_dot" | "ignore_local_sudoers" | "insults" | "log_host" | "log_year" | "long_otp_prompt" | "mail_always" | "mail_badpass" | "mail_no_host" | "mail_no_perms" | "mail_no_user" | "noexec" | "path_info" | "passprompt_override" | "preserve_groups" | "requiretty" | "root_sudo" | "rootpw" | "runaspw" | "set_home" | "set_logname" | "setenv" | "shell_noargs" | "stay_setuid" | "targetpw" | "tty_tickets" | "visiblepw" | "closefrom_override" | "closefrom_override" | "compress_io" | "fast_glob" | "log_input" | "log_output" | "pwfeedback" | "umask_override" | "use_pty" | "match_group_by_gid" | "always_query_group_plugin"
A flag parameter for defaults
Flags are implicitly boolean and can be turned off via the ‘!’ operator. Some integer, string and list parameters may also be used in a boolean context to disable them.
let parameter_integer_nobool_kw = "passwd_tries"
An integer parameter for defaults
let parameter_string_nobool_kw = "badpass_message" | "editor" | "mailsub" | "noexec_file" | "passprompt" | "runas_default" | "syslog_badpri" | "syslog_goodpri" | "timestampdir" | "timestampowner" | "secure_path"
A string parameter for defaults
An odd number of ‘!’ operators negate the value of the item; an even number just cancel each other out.
let parameter_lists_kw = "env_check" | "env_delete" | "env_keep"
A single list parameter for defaults
All lists can be used in a boolean context The argument may be a double-quoted, space-separated list or a single value without double-quotes. The list can be replaced, added to, deleted from, or disabled by using the =, +=, -=, and ! operators respectively. An odd number of ‘!’ operators negate the value of the item; an even number just cancel each other out.
let parameter = parameter_flag | parameter_integer | parameter_string | parameter_lists
A single parameter for defaults
Parameter ::= Parameter '=' Value | Parameter '+=' Value | Parameter '-=' Value | '!'* Parameter
Parameters may be flags, integer values, strings, or lists.
let parameter_list = parameter . ( sep_com . parameter )*
A list of comma-separated parameters for defaults
Parameter_List ::= Parameter | Parameter ',' Parameter_List
let runas_spec_user = alias_list "runas_user" sto_to_com
A runas specification for spec, using alias_list for listing users and/or groups used to run a command
Runas_Spec ::= '(' Runas_List ')' | '(:' Runas_List ')' | '(' Runas_List ':' Runas_List ')'
let tag_spec = [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]
Tag specification for spec
Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' | 'SETENV:' | 'NOSETENV:')
let cmnd_spec = [ label "command" . runas_spec? . tag_spec* . sto_to_com_cmnd ]
Command specification for spec, with optional runas_spec and any amount of tag_specs
Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd
let cmnd_spec_list = Build.opt_list cmnd_spec sep_com
A list of comma-separated cmnd_specs
Cmnd_Spec_List ::= Cmnd_Spec | Cmnd_Spec ',' Cmnd_Spec_List
let spec_list = [ label "host_group" . alias_list "host" sto_to_com_host . sep_eq . cmnd_spec_list ]
Group of hosts with cmnd_spec_list
let spec = [ label "spec" . indent . alias_list "user" sto_to_com_user . sep_cont . Build.opt_list spec_list sep_col . comment_or_eol ]
A user specification, listing colon-separated spec_lists
User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \ (':' Host_List '=' Cmnd_Spec_List)*
let eol = Util.eol
let indent = Util.indent
let sep_spc = Sep.space
let sep_cont = Sep.cl_or_space
let sep_cont_opt = Sep.cl_or_opt_space
let sep_cont_opt_build (sep:string) = del (Rx.cl_or_opt_space . sep . Rx.cl_or_opt_space) (" " . sep . " ")
let sep_com = sep_cont_opt_build ","
let sep_eq = sep_cont_opt_build "="
let sep_col = sep_cont_opt_build ":"
let sep_dquote = Util.del_str "\""
Delete an even number of ‘!’
let del_negate = del /(!!)*/ ""
Negation of boolean values for defaults.
let negate_node = [ del "!" "!" . label "negate" ]
A Defaults entry
let defaults = [ indent . key "Defaults" . default_type? . sep_cont . parameter_list . comment_or_eol ]
A del_negate, followed by either a negated key, or a key/value pair
let negate_or_value (key:lens) (value:lens) = [ del_negate . (negate_node . key | key . value) ]
sto_to_com_cmnd does not begin or end with a space
let sto_to_com_cmnd = del_negate . negate_node? . ( let alias = Rx.word - /(NO)?(PASSWD|EXEC|SETENV)/ in let non_alias = /[\/a-z]([^,:#()\n\\]|\\\\[=:,\\])*[^,=:#() \t\n\\]|[^,=:#() \t\n\\]/ in store (alias | non_alias))
There could be a \ in the middle of a command
let sto_to_com = store /([^,=:#() \t\n\\][^,=:#()\n]*[^,=:#() \t\n\\])|[^,=:#() \t\n\\]/
let sto_to_com_host = store /[^,=:#() \t\n\\]+/
Escaped spaces and NIS domains and allowed
let sto_to_com_user = let nis_re = /([A-Z]([-A-Z0-9]|(\\\\[ \t]))*+\\\\\\\\)/ in let user_re = /[%+@a-z]([-A-Za-z0-9._+]|(\\\\[ \t]))*/ in let alias_re = /[A-Z_]+/ in store ((nis_re? . user_re) | alias_re)
let to_com_chars = /[^",=#() \t\n\\]+/ (* " relax emacs *)
let to_com_dquot = /"[^",=#()\n\\]+"/ (* " relax emacs *)
let sto_to_com_dquot = store (to_com_chars|to_com_dquot)
let sto_to_com_col = store to_com_chars
let sto_to_eq = store /[^,=:#() \t\n\\]+/
let sto_to_spc = store /[^", \t\n\\]+|"[^", \t\n\\]+"/
let sto_to_spc_no_dquote = store /[^",# \t\n\\]+/ (* " relax emacs *)
let sto_integer = store /[0-9]+/
Map comments in “#comment” nodes
let comment = let sto_to_eol = store (/([^ \t\n].*[^ \t\n]|[^ \t\n])/ - /include(dir)?.*/) in [ label "#comment" . del /[ \t]*#[ \t]*/ "# " . sto_to_eol . eol ]
Requires a space before the #
let comment_eol = Util.comment_generic /[ \t]+#[ \t]*/ " # "
A comment_eol or eol
let comment_or_eol = comment_eol | (del /([ \t]+#\n|[ \t]*\n)/ "\n")
Map empty lines
let empty = [ del /[ \t]*#?[ \t]*\n/ "\n" ]
let includedir = [ key /#include(dir)?/ . Sep.space . store Rx.fspath . eol ]
Generic alias field to gather all Alias definitions
let alias_field (kw:string) (sto:lens) = [ label kw . sto ]
List of alias_fields, separated by commas
let alias_list (kw:string) (sto:lens) = Build.opt_list (alias_field kw sto) sep_com
Name of an alias_entry_single
let alias_name = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]
Single alias_entry, named using alias_name and listing alias_list
let alias_entry_single (field:string) (sto:lens) = [ label "alias" . alias_name . sep_eq . alias_list field sto ]
Alias entry, a list of comma-separated alias_entry_single fields
let alias_entry (kw:string) (field:string) (sto:lens) = [ indent . key kw . sep_cont . alias_entry_single field sto . ( sep_col . alias_entry_single field sto )* . comment_or_eol ]
User_Alias, see alias_field
let user_alias = alias_entry "User_Alias" "user" sto_to_com
Run_Alias, see alias_field
let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com
Host_Alias, see alias_field
let host_alias = alias_entry "Host_Alias" "host" sto_to_com
Cmnd_Alias, see alias_field
let cmnd_alias = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd
Every kind of Alias entry, see user_alias, runas_alias, host_alias and cmnd_alias
let alias = user_alias | runas_alias | host_alias | cmnd_alias
Type definition for defaults
let default_type = let value = store /[@:!>][^ \t\n\\]+/ in [ label "type" . value ]
A flag parameter for defaults
let parameter_flag_kw = "always_set_home" | "authenticate" | "env_editor" | "env_reset" | "fqdn" | "ignore_dot" | "ignore_local_sudoers" | "insults" | "log_host" | "log_year" | "long_otp_prompt" | "mail_always" | "mail_badpass" | "mail_no_host" | "mail_no_perms" | "mail_no_user" | "noexec" | "path_info" | "passprompt_override" | "preserve_groups" | "requiretty" | "root_sudo" | "rootpw" | "runaspw" | "set_home" | "set_logname" | "setenv" | "shell_noargs" | "stay_setuid" | "targetpw" | "tty_tickets" | "visiblepw" | "closefrom_override" | "closefrom_override" | "compress_io" | "fast_glob" | "log_input" | "log_output" | "pwfeedback" | "umask_override" | "use_pty" | "match_group_by_gid" | "always_query_group_plugin"
An integer parameter for defaults
let parameter_integer_nobool_kw = "passwd_tries"
A string parameter for defaults
let parameter_string_nobool_kw = "badpass_message" | "editor" | "mailsub" | "noexec_file" | "passprompt" | "runas_default" | "syslog_badpri" | "syslog_goodpri" | "timestampdir" | "timestampowner" | "secure_path"
A single list parameter for defaults
let parameter_lists_kw = "env_check" | "env_delete" | "env_keep"
A single parameter for defaults
let parameter = parameter_flag | parameter_integer | parameter_string | parameter_lists
A list of comma-separated parameters for defaults
let parameter_list = parameter . ( sep_com . parameter )*
A runas specification for spec, using alias_list for listing users and/or groups used to run a command
let runas_spec_user = alias_list "runas_user" sto_to_com
A user specification, listing colon-separated spec_lists
let spec = [ label "spec" . indent . alias_list "user" sto_to_com_user . sep_cont . Build.opt_list spec_list sep_col . comment_or_eol ]
Tag specification for spec
let tag_spec = [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]
Command specification for spec, with optional runas_spec and any amount of tag_specs
let cmnd_spec = [ label "command" . runas_spec? . tag_spec* . sto_to_com_cmnd ]
A list of comma-separated cmnd_specs
let cmnd_spec_list = Build.opt_list cmnd_spec sep_com
Group of hosts with cmnd_spec_list
let spec_list = [ label "host_group" . alias_list "host" sto_to_com_host . sep_eq . cmnd_spec_list ]
The sudoers lens, any amount of
let lns = ( empty | comment | includedir | alias | defaults | spec )*