Sudoers

Parses /etc/sudoers

Author: Raphael Pinson rap.nosp@m.hink@gmai.nosp@m.l.com

Summary
SudoersParses /etc/sudoers
ReferenceThis lens tries to keep as close as possible to `man sudoers` where possible.
LicenseThis file is licensed under the LGPL v2+, like the rest of Augeas.
Lens UsageSample usage of this lens in augtool
Configuration filesThis 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_negateDelete an even number of ‘!’
negate_nodeNegation of boolean values for defaults.
negate_or_valueA del_negate, followed by either a negated key, or a key/value pair
Stores
sto_to_com_cmndsto_to_com_cmnd does not begin or end with a space
sto_to_comThere could be a \ in the middle of a command
sto_to_com_host
sto_to_com_userEscaped 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
commentMap comments in “#comment” nodes
comment_eolRequires a space before the #
comment_or_eolA comment_eol or eol
emptyMap empty lines
includedir
ALIASES
alias_fieldGeneric alias field to gather all Alias definitions
alias_listList of alias_fields, separated by commas
alias_nameName of an alias_entry_single
alias_entry_singleSingle alias_entry, named using alias_name and listing alias_list
alias_entryAlias entry, a list of comma-separated alias_entry_single fields
user_aliasUser_Alias, see alias_field
runas_aliasRun_Alias, see alias_field
host_aliasHost_Alias, see alias_field
cmnd_aliasCmnd_Alias, see alias_field
aliasEvery kind of Alias entry, see user_alias, runas_alias, host_alias and cmnd_alias
DEFAULTS
default_typeType definition for defaults
parameter_flagA flag parameter for defaults
parameter_integerAn integer parameter for defaults
parameter_stringA string parameter for defaults
parameter_listsA single list parameter for defaults
parameterA single parameter for defaults
parameter_listA list of comma-separated parameters for defaults
defaultsA Defaults entry
USER SPECIFICATION
runas_specA runas specification for spec, using alias_list for listing users and/or groups used to run a command
tag_specTag specification for spec
cmnd_specCommand specification for spec, with optional runas_spec and any amount of tag_specs
cmnd_spec_listA list of comma-separated cmnd_specs
spec_listGroup of hosts with cmnd_spec_list
specA user specification, listing colon-separated spec_lists
LENS & FILTER
lnsThe sudoers lens, any amount of
filter

Reference

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.

License

This file is licensed under the LGPL v2+, like the rest of Augeas.

Lens Usage

Sample usage of this lens in augtool

  • Set first Defaults to apply to the “LOCALNET” network alias
set /files/etc/sudoers/Defaults[1]/type "@LOCALNET"
  • List all user specifications applying explicitely to the “admin” Unix group
match /files/etc/sudoers/spec/user "%admin"
  • Remove the full 3rd user specification
rm /files/etc/sudoers/spec[3]

Configuration files

This lens applies to /etc/sudoers.  See filter.

USEFUL PRIMITIVES

Generic primitives

eol

let eol = Util.eol

indent

let indent = Util.indent

Separators

sep_spc

let sep_spc = Sep.space

sep_cont

let sep_cont = Sep.cl_or_space

sep_cont_opt

let sep_cont_opt = Sep.cl_or_opt_space

sep_cont_opt_build

let sep_cont_opt_build (sep:string) = del (Rx.cl_or_opt_space . sep . Rx.cl_or_opt_space) (" " . sep . " ")

sep_com

let sep_com = sep_cont_opt_build ","

sep_eq

let sep_eq = sep_cont_opt_build "="

sep_col

let sep_col = sep_cont_opt_build ":"

sep_dquote

let sep_dquote = Util.del_str "\""

Negation expressions

del_negate

let del_negate = del /(!!)*/ ""

Delete an even number of ‘!’ signs

negate_node

let negate_node = [ del "!" "!" . label "negate" ]

Negation of boolean values for defaults.  Accept one optional ‘!’ and produce a ‘negate’ node if there is one.

negate_or_value

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

Stores

sto_to_com_cmnd

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))

sto_to_com_cmnd does not begin or end with a space

sto_to_com

let sto_to_com = store /([^,=:#() \t\n\\][^,=:#()\n]*[^,=:#() \t\n\\])|[^,=:#() \t\n\\]/

There could be a \ in the middle of a command

sto_to_com_host

let sto_to_com_host = store /[^,=:#() \t\n\\]+/

sto_to_com_user

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)

Escaped spaces and NIS domains and allowed

to_com_chars

let to_com_chars = /[^",=#() \t\n\\]+/ (* " relax emacs *)

to_com_dquot

let to_com_dquot = /"[^",=#()\n\\]+"/ (* " relax emacs *)

sto_to_com_dquot

let sto_to_com_dquot = store (to_com_chars|to_com_dquot)

sto_to_com_col

let sto_to_com_col = store to_com_chars

sto_to_eq

let sto_to_eq = store /[^,=:#() \t\n\\]+/

sto_to_spc

let sto_to_spc = store /[^", \t\n\\]+|"[^", \t\n\\]+"/

sto_to_spc_no_dquote

let sto_to_spc_no_dquote = store /[^",# \t\n\\]+/ (* " relax emacs *)

sto_integer

let sto_integer = store /[0-9]+/

Comments and empty lines

comment

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 ]

Map comments in “#comment” nodes

comment_eol

let comment_eol = Util.comment_generic /[ \t]+#[ \t]*/ " # "

Requires a space before the #

comment_or_eol

let comment_or_eol = comment_eol | (del /([ \t]+#\n|[ \t]*\n)/ "\n")

A comment_eol or eol

empty

let empty = [ del /[ \t]*#?[ \t]*\n/ "\n" ]

Map empty lines

includedir

let includedir = [ key /#include(dir)?/ . Sep.space . store Rx.fspath . eol ]

ALIASES

alias_field

let alias_field (kw:string) (sto:lens) = [ label kw . sto ]

Generic alias field to gather all Alias definitions

Definition

User_Alias ::= NAME '=' User_List
Runas_Alias ::= NAME '=' Runas_List
Host_Alias ::= NAME '=' Host_List
Cmnd_Alias ::= NAME '=' Cmnd_List

Parameters

kw:stringthe label string
sto:lensthe store lens

alias_list

let alias_list (kw:string) (sto:lens) = Build.opt_list (alias_field kw sto) sep_com

List of alias_fields, separated by commas

alias_name

let alias_name = [ label "name" . store /[A-Z][A-Z0-9_]*/ ]

Name of an alias_entry_single

Definition

NAME ::= [A-Z]([A-Z][0-9]_)*

alias_entry_single

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

Definition

Alias_Type NAME = item1, item2, ...

Parameters

field:stringthe field name, passed to alias_list
sto:lensthe store lens, passed to alias_list

alias_entry

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

Definition

Alias_Type NAME = item1, item2, item3 : NAME = item4, item5

Parameters

kw:stringthe alias keyword string
field:stringthe field name, passed to alias_entry_single
sto:lensthe store lens, passed to alias_entry_single

user_alias

let user_alias = alias_entry "User_Alias" "user" sto_to_com

User_Alias, see alias_field

runas_alias

let runas_alias = alias_entry "Runas_Alias" "runas_user" sto_to_com

Run_Alias, see alias_field

host_alias

let host_alias = alias_entry "Host_Alias" "host" sto_to_com

Host_Alias, see alias_field

cmnd_alias

let cmnd_alias = alias_entry "Cmnd_Alias" "command" sto_to_com_cmnd

Cmnd_Alias, see alias_field

alias

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

Definition

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)*

DEFAULTS

default_type

let default_type = let value = store /[@:!>][^ \t\n\\]+/ in [ label "type" . value ]

Type definition for defaults

Definition

Default_Type ::= 'Defaults' |
                 'Defaults' '@' Host_List |
                 'Defaults' ':' User_List |
                 'Defaults' '!' Cmnd_List |
                 'Defaults' '>' Runas_List

parameter_flag

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"

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.

parameter_integer

let parameter_integer_nobool_kw = "passwd_tries"

An integer parameter for defaults

parameter_string

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.

parameter_lists

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.

parameter

let parameter = parameter_flag | parameter_integer | parameter_string | parameter_lists

A single parameter for defaults

Definition

Parameter ::= Parameter '=' Value |
              Parameter '+=' Value |
              Parameter '-=' Value |
              '!'* Parameter

Parameters may be flags, integer values, strings, or lists.

parameter_list

let parameter_list = parameter . ( sep_com . parameter )*

A list of comma-separated parameters for defaults

Definition

Parameter_List ::= Parameter |
                   Parameter ',' Parameter_List

defaults

let defaults = [ indent . key "Defaults" . default_type? . sep_cont . parameter_list . comment_or_eol ]

A Defaults entry

Definition

Default_Entry ::= Default_Type Parameter_List

USER SPECIFICATION

runas_spec

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

Definition

Runas_Spec ::= '(' Runas_List ')' |
               '(:' Runas_List ')' |
               '(' Runas_List ':' Runas_List ')'

tag_spec

let tag_spec = [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]

Tag specification for spec

Definition

Tag_Spec ::= ('NOPASSWD:' | 'PASSWD:' | 'NOEXEC:' | 'EXEC:' |
             'SETENV:' | 'NOSETENV:')

cmnd_spec

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

Definition

Cmnd_Spec ::= Runas_Spec? Tag_Spec* Cmnd

cmnd_spec_list

let cmnd_spec_list = Build.opt_list cmnd_spec sep_com

A list of comma-separated cmnd_specs

Definition

Cmnd_Spec_List ::= Cmnd_Spec |
                   Cmnd_Spec ',' Cmnd_Spec_List

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

spec

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

Definition

User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
              (':' Host_List '=' Cmnd_Spec_List)*

LENS & FILTER

lns

let lns = ( empty | comment | includedir | alias | defaults | spec )*

The sudoers lens, any amount of

filter

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 "\""
let del_negate = del /(!!)*/ ""
Delete an even number of ‘!’
let negate_node = [ del "!" "!" . label "negate" ]
Negation of boolean values for defaults.
let defaults = [ indent . key "Defaults" . default_type? . sep_cont . parameter_list . comment_or_eol ]
A Defaults entry
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 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))
sto_to_com_cmnd does not begin or end with a space
let sto_to_com = store /([^,=:#() \t\n\\][^,=:#()\n]*[^,=:#() \t\n\\])|[^,=:#() \t\n\\]/
There could be a \ in the middle of a command
let sto_to_com_host = store /[^,=:#() \t\n\\]+/
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)
Escaped spaces and NIS domains and allowed
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]+/
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 ]
Map comments in “#comment” nodes
let comment_eol = Util.comment_generic /[ \t]+#[ \t]*/ " # "
Requires a space before the #
let comment_or_eol = comment_eol | (del /([ \t]+#\n|[ \t]*\n)/ "\n")
A comment_eol or eol
let empty = [ del /[ \t]*#?[ \t]*\n/ "\n" ]
Map empty lines
let includedir = [ key /#include(dir)?/ . Sep.space . store Rx.fspath . eol ]
let alias_field (kw:string) (sto:lens) = [ label kw . sto ]
Generic alias field to gather all Alias definitions
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
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
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
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
let default_type = let value = store /[@:!>][^ \t\n\\]+/ in [ label "type" . value ]
Type definition 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"
A flag parameter for defaults
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
let parameter_lists_kw = "env_check" | "env_delete" | "env_keep"
A single list parameter for defaults
let parameter = parameter_flag | parameter_integer | parameter_string | parameter_lists
A single parameter for defaults
let parameter_list = parameter . ( sep_com . parameter )*
A list of comma-separated parameters for defaults
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
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
let tag_spec = [ label "tag" . store /(NO)?(PASSWD|EXEC|SETENV)/ . sep_col ]
Tag specification for spec
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
let cmnd_spec_list = Build.opt_list cmnd_spec sep_com
A list of comma-separated cmnd_specs
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 lns = ( empty | comment | includedir | alias | defaults | spec )*
The sudoers lens, any amount of
Close