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 LGPLv2+, 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_com
sep_eq
sep_col
sep_dquote
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_user
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
emptyMap empty lines
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
del_negateDelete an even number of ‘!’
negate_nodeNegation of boolean values 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
paramater_listA list of comma-separated parameters for defaults
defaultsA Defaults entry
USER SPECIFICATION
runas_specA runas specification for spec, using alias_list
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 LGPLv2+, 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 = del /[ \t]*\n/ "\n"

indent

let indent = del /[ \t]*/ ""

Separators

sep_spc

let sep_spc = del /[ \t]+/ " "

sep_cont

let sep_cont = del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "

sep_cont_opt

let sep_cont_opt = del /([ \t]*|[ \t]*\\\\\n[ \t]*)/ " "

sep_com

let sep_com = sep_cont_opt . Util.del_str "," . sep_cont_opt

sep_eq

let sep_eq = sep_cont_opt . Util.del_str "=" . sep_cont_opt

sep_col

let sep_col = sep_cont_opt . Util.del_str ":" . sep_cont_opt

sep_dquote

let sep_dquote = Util.del_str "\""

Stores

sto_to_com_cmnd

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

sto_to_com_cmnd does not begin or end with a space

There could be a \ in the middle of a command

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_user

let sto_to_com_user = store (
   /[^,=:#() \t\n]+/ /(User|Runas|Host|Cmnd)_Alias|Defaults.*/
)

sto_to_com_col

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

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]
)/ in [ label "#comment" . del /[ \t]*#[ \t]*/ "# " . sto_to_eol . eol ]

Map comments in “#comment” nodes

empty

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

Map empty lines

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) = alias_field kw sto . ( sep_com . alias_field kw sto )*

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 )* . 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' '>' Runas_List

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.

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"

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"

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.

paramater_list

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 . eol ]

A Defaults entry

Definition

Default_Entry ::= Default_Type Parameter_List

USER SPECIFICATION

runas_spec

let runas_spec = Util.del_str "(" . alias_list "runas_user" sto_to_com . Util.del_str ")" . sep_cont_opt

A runas specification for spec, using alias_list

Definition

Runas_Spec ::= '(' 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 = cmnd_spec . (sep_com cmnd_spec)*

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 . 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 . spec_list . ( sep_col . spec_list )* . 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 | alias | defaults spec)*

The sudoers lens, any amount of

filter

let eol = del /[ \t]*\n/ "\n"
let indent = del /[ \t]*/ ""
let sep_spc = del /[ \t]+/ " "
let sep_cont = del /([ \t]+|[ \t]*\\\\\n[ \t]*)/ " "
let sep_cont_opt = del /([ \t]*|[ \t]*\\\\\n[ \t]*)/ " "
let sep_com = sep_cont_opt . Util.del_str "," . sep_cont_opt
let sep_eq = sep_cont_opt . Util.del_str "=" . sep_cont_opt
let sep_col = sep_cont_opt . Util.del_str ":" . sep_cont_opt
let sep_dquote = Util.del_str "\""
let sto_to_com_cmnd = store /(
   [^,=:#() \t\n\\\\][^,=:#()\n]*[^,=:#() \t\n\\\\]
)|[^,=:#() \t\n\\\\]/
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_user = store (
   /[^,=:#() \t\n]+/ /(User|Runas|Host|Cmnd)_Alias|Defaults.*/
)
let sto_to_com_col = store /[^",=#() \t\n\\\\]+/ (* " relax emacs *)
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]
)/ in [ label "#comment" . del /[ \t]*#[ \t]*/ "# " . sto_to_eol . eol ]
Map comments in “#comment” nodes
let empty = [ del /[ \t]*#?[ \t]*\n/ "\n" ]
Map empty lines
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) = alias_field kw sto . ( sep_com . alias_field kw sto )*
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 )* . 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 defaults = [ indent . key "Defaults" . default_type? . sep_cont . parameter_list . eol ]
A Defaults entry
let del_negate = del /(!!)*/ ""
Delete an even number of ‘!’
let negate_node = [ del "!" "!" . label "negate" ]
Negation of boolean values 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"
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"
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 runas_spec = Util.del_str "(" . alias_list "runas_user" sto_to_com . Util.del_str ")" . sep_cont_opt
A runas specification for spec, using alias_list
let spec = [ label "spec" . indent . alias_list "user" sto_to_com_user . sep_cont . spec_list . ( sep_col . spec_list )* . 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 = cmnd_spec . (sep_com cmnd_spec)*
A list of comma-separated cmnd_specs
let spec_list = [ label "host_group" . alias_list "host" sto_to_com . sep_eq . cmnd_spec_list ]
Group of hosts with cmnd_spec_list
let lns = (empty | comment | alias | defaults spec)*
The sudoers lens, any amount of
Close