MAC_DO(4)              FreeBSD Kernel Interfaces Manual              MAC_DO(4)

NAME

     mac_do - policy allowing unprivileged users to change process credentials

SYNOPSIS

     To compile the mac_do policy into your kernel, place the following lines
     in your kernel configuration file:

           options MAC
           options MAC_DO

     Alternately, to load this policy module at boot time, place the following
     line in your kernel configuration file:

           options MAC

     and in loader.conf(5):

           mac_do_load="YES"

DESCRIPTION

     The mac_do policy module allows unprivileged users to change process
     credentials according to rules configured by the administrator.  It
     supports per-jail configuration.

     The mac_do policy module only produces effects on processes spawned from
     specific executables from a configurable whitelist.  By default, this
     whitelist only contains the /usr/bin/mdo executable, please see mdo(1)
     for more details on this program.

     Section CREDENTIALS RULES specifies the format of credentials transition
     rules, and section CONFIGURATION explains how to configure mac_do,
     including rules and authorized executables.

CREDENTIALS RULES

     Rules specify which transitions of process credentials mac_do will allow,
     based on current process credentials and the desired final ones.  They
     are passed by an administrator in the form of a string having the
     specific syntax described below in a top-bottom manner.  They have been
     designed to be able to finely describe the desired target credentials in
     a safe and compact way.

Top-Level List of Rules

     At the top, rules are a possibly empty list of individual rules separated
     by a semi-colon (`;'):
           <rules>  <?>  [<rule> [`;' <rule>]*]
     They form a disjunction, i.e., mac_do authorizes a credentials transition
     as soon as at least one rule in the list matches.

     One rule is composed of a <from> part (also called "match") and a <to>
     part (also called "target"), in this order, separated by a greater-than
     sign (`>'):
           <rule>  <?>  <from> `>' <to>

Rule's Part

     The first part of a rule, <from>, is matched against the credentials of
     the process requesting some credentials transition.  It has the form:
           <from>  <?>  <type> `=' <id>

     <type> must be:
           <type>  <?>  [`uid' | `gid']
     i.e., one of the literal strings `uid' or `gid'.  <id> must be the
     numerical ID of a user or group and is matched against the current
     process real ID of the corresponding type, and on type `gid' additionally
     against the supplementary groups.

Rule's Part

     The second part of a rule, <to>, is a comma-separated (`,') non-empty
     list of target clauses:
           <to>  <?>  <target_clause> [`,' <target_clause>]*
     Target clauses of a given rule also form a disjunction, i.e., the IDs
     they specify are alternatives for the target credentials, except in some
     cases described below.

     The next subsections describe the syntax of target clauses, the defaults
     that apply and the principle of non-redundancy and non-contradiction in
     each rule's <to> part.

Target Clauses

     A target clause in a rule's <to> part must be of one of the following
     forms:
           <target_clause>  <?>  `any'
           <target_clause>  <?>  <flags> <type> `=' <id>
     The first form is a compact way to specify that any target credentials
     are allowed.  The second form is similar to that of <from> clauses, with
     the following extensions:
     o   <id> may also be a literal `*' or `any' or `.'.  `*' and `any' both
         designate any ID for the specified <type>, and are treated
         identically.  `.' designates the process' current IDs for the
         specified <type>, as explained below.
     o   <flags> may contain at most one of the `+', `-' and `!' characters,
         and may be non-empty only when <type> is `gid'.  Additionally, if
         <id> is `*' or `any', only the `+' flag may appear.

     For target clauses of `gid' type, an absence of flag indicates that the
     specified group ID is allowed as the real, effective and/or saved group
     IDs (the "primary" groups).  Conversely, the presence of any allowed flag
     indicates that the specification concerns supplementary groups.  Each
     flag has a specific meaning:
     o   `+' indicates that the group ID is allowed as a supplementary group.
     o   `!' indicates that the group ID is mandatory, i.e., it must be listed
         in the supplementary groups.
     o   `-' indicates that the group ID must not be listed in the
         supplementary groups.
     A specification with `-' is only useful in conjunction with a `+'-tagged
     specification where only one of them has `.' as its <id>.  Target clauses
     having the `!' or `-' flag are "forcing" clauses, and as such do not take
     part in the disjunction of the other target clauses but rather
     unconditionally apply in their rule.

     `.' is a placeholder for IDs that the calling process already has on
     privilege check.  For type `uid', it designates any of the process' real,
     effective or saved user IDs.  For type `gid', its effect depends on
     whether flags are present.  If none is present, it designates any of the
     process' real, effective or saved group IDs.  If one is present, it
     designates any of the process' supplementary groups.

Defaults for the Part

     If the <to> part does not list a target clause with type `uid', any of
     the current user IDs of the calling process is accepted.  In other words,
     in this case, mac_do behaves as if a target clause of:
           uid=.
     had been listed.

     Similarly, if the <to> part does not list a target clause with type
     `gid', all the groups of the calling process are assumed to be required.
     More precisely, each of the desired real, effective and saved group IDs
     must be one of the current real, effective or saved group ID, and all
     supplementary groups must be the same as those that are current.  It is
     as if the <to> part had contained the following two clauses:
           gid=.,!gid=.

Non-Redundancy and Non-Contradiction in a Part

     No two target clauses of a single rule may express the exact same logical
     intent nor contradictory ones.

     In practice, no two clauses may display the same ID except for group IDs
     but only if, each time the same ID appears, it does so with a different
     flag, or no flags only once.  Additionally, the specified flags in
     multiple occurrences must not be contradictory.  For example, the same
     group ID appearing with both `+' and `-' will cause rejection of the
     rule.

Parsing Specifics

     Any amount of whitespace is allowed around tokens of the above grammar,
     except that there may be no spaces between <flags> and <id> in target
     clauses.

     For convenience, numerical IDs may be specified as negative integers,
     which are then converted to unsigned ones as specified in the C standard
     for the uid_t and gid_t types, which are both 32-bit unsigned integers.

CONFIGURATION

     Each parameter of mac_do has a corresponding sysctl(8) knob.  mac_do
     supports per-jail values for most parameters.

Sysctl Knobs

     The sysctl(8) knobs presented by mac_do are of two types: Global ones,
     which influence all uses of the module, and per-jail ones, which allow to
     retrieve or change the setting applicable to a jail (or the host) from
     within.  They are tagged accordingly in the list below.

     The indicated default values for per-jail parameters applies to the host.
     Those applying to jails are described in the Jail Parameters subsection
     below.

     The following knobs are available:

     security.mac.do.enabled
             Enable the mac_do policy.  (Global. Default: 1.)

     security.mac.do.print_parse_error
             Logs a message on trying to set incorrect rules via the
             security.mac.do.rules sysctl(8) knob.  (Global. Default: 1.)

     security.mac.do.rules
             The list of credentials transition rules, whose syntax is
             described in the CREDENTIALS RULES section above.  An empty
             string effectively disables the policy.  (Per-jail. Default:
             Empty.)

     security.mac.do.exec_paths
             The list of absolute paths (relative to the current jail's root)
             to authorized executables, separated by colons (`:').  Only
             processes launched from these executables are considered by the
             mac_do policy.  An empty string effectively disables the policy.
             (Per-jail. Default: `/usr/bin/mdo'.)

Jail Parameters

     Most parameters of mac_do are per-jail (see the Sysctl Knobs subsection
     above).  Those that are per-jail have corresponding jail parameters that
     can be used to set their initial values on jail creation or modify their
     values in a running jail from outside the jail.

     By default, as it is for the host, a new jail has mac_do disabled for its
     processes, as if the mac.do parameter below was explicitly set to
     `disable'.

     Each unspecified parameter other than mac.do defaults to a copy of its
     value in the currently applicable configuration for a running jail, or
     from the parent jail on jail creation.

     The following jail parameters are available:

     mac.do  Possible values are:

             `new'      mac_do will use a specific configuration for the jail.
                        This case degrades to `disable' if one of the other
                        jail parameters end up empty after applying the
                        default values rule (see the preamble).
             `disable'  Disables mac_do in the jail.  This is achieved by
                        ensuring that at least one of the mac.do.rules and
                        mac.do.exec_paths jail parameters are empty.  If none
                        of them ends up empty after applying the default
                        values rule (see the preamble), mac.do.rules is forced
                        to an empty value.  An explicit `disable' is
                        incompatible with explicit non-empty values for all
                        other jail parameters and will trigger an error.
             `inherit'  The other per-jail parameters are inherited from those
                        applicable to the jail's parent (which may themselves
                        have been inherited).  Configuration modifications
                        immediately propagate to all descendant jails
                        configured for inheritance (as long as there is no
                        intervening jail having its own specific
                        configuration).

             The default value depends on the absence or presence and value of
             the other jail parameters.  As soon as one of the latter is
             present and not empty, the default value is `new', else it is
             `disable'.  Inheritance is never established implicitly, it must
             be explicitly requested.

     mac.do.rules
             The credentials rules for the jail.  See the description of the
             corresponding sysctl(8) knob security.mac.do.rules in subsection
             Sysctl Knobs above.  See also the preamble for the default values
             rule.

     mac.do.exec_paths
             The authorized executables.  See the description of the
             corresponding sysctl(8) knob security.mac.do.exec_paths in
             subsection Sysctl Knobs above.  See also the preamble for the
             default values rule.

Consistency

     Values read or set from jail parameters are always consistent with their
     corresponding sysctl(8) knobs, effectively operating on the same internal
     "variable".

     All accesses to some parameter or some jail configuration as a whole,
     whether a read or a modification, are sequentially consistent.  In other
     words, they appear to be atomic, and all threads see them happening in
     the same order.

EXAMPLES

     Here are several examples of single rules matching processes having a
     real user ID of 10001:

     uid=10001>uid=10002
             Allows the process to switch all of its real, effective or saved
             user ID to 10002, but keeping the groups it is already in, and
             with the same primary/supplementary groups split.

     uid=10001>uid=10002,uid=10003
             Same as the first example, but also allows to switch to UID 10003
             instead of 10002, or possibly having both in different user IDs.

     uid=10001>uid=10002,gid=10002
             Same as the first example, but the new primary groups must be set
             to 10002 and no supplementary groups should be set.

     uid=10001>uid=10002,gid=10002,+gid=.
             Same as the previous example, but in addition allowing to retain
             any current supplementary groups.

     uid=10001>uid=10002,gid=10002,!gid=.
             Same as the previous example, but with the additional constraint
             that all current supplementary groups must be kept.

     uid=10001>uid=10002,gid=10002,+gid=.,-gid=10001
             Same as `uid=10001>uid=10002,gid=10002,+gid=.' above, but 10001
             cannot be retained as a supplementary group.

     uid=10001>uid=10002,gid=10002,+gid=.,!gid=10003
             Same as `uid=10001>uid=10002,gid=10002,+gid=.' above, with the
             additional constraint that 10003 must appear in the supplementary
             groups.

     uid=10001>uid=10002,gid=*,+gid=*
             Same as the first example, but lifting any constraints on groups,
             allowing the process to become part of any groups it sees fit.

     Here are several examples of single rules matching processes having 10001
     as their real group IDs or in their supplementary groups:

     gid=10001>uid=0
             Makes 10001 a more powerful `wheel' group, allowing its members
             to switch to root without password.

     gid=10001>gid=10002
             Allows the process to enter GID 10002 as a primary group, but
             only if giving up all its supplementary groups.

     gid=10001>gid=10002,+gid=.
             Same as the previous example, but allows to retain any current
             supplementary groups.

     gid=10001>gid=10002,!gid=.
             Same as the previous example, but with the additional constraint
             that all current supplementary groups must be kept.

SEE ALSO

     mdo(1), setcred(2), mac(4), jail(8), sysctl(8)

AUTHORS

     Olivier Certner <olce@FreeBSD.org>
     Baptiste Daroussin <bapt@FreeBSD.org>
     Kushagra Srivastava <kushagra1403@gmail.com>

BUGS

     Currently, mac_do considers only credentials transitions requested
     through the setcred(2) system call.  This system call was in large part
     created so that mac_do can see whole credentials transitions to decide
     whether to authorize them, which the traditional UNIX's piecewise
     approach of successively changing different parts of them cannot allow.

     However, calls to traditional or standard credentials-changing functions
     can be considered as full transitions on their own, however limited, and
     as such should be equally monitored by mac_do.  Future work will lift
     this restriction.

SECURITY CONSIDERATIONS

     The threat model for mac_do is to consider userland programs as generally
     untrustable to decide upon which credentials changes are acceptable.  It
     is in contrast with the traditional UNIX way to change credentials, in
     which specialized programs are installed with the setuid bit, giving them
     full administrator privileges so that they are effectively able to
     establish new ones.  Vulnerabilities in such credentials-changing
     programs can have catastrophic consequences on the integrity of the
     system.

     Consequently, mac_do does not rely on companion userland programs to
     decide whether some credentials transition is acceptable.  Instead, it
     maintains its own configuration independently from the userland password
     and group databases.  Establishing this configuration currently itself
     relies on userland programs issuing calls to sysctl(3) or jail(2).  It
     should thus be established near system boot or jail start, before any
     possible attacks could happen on the system, and further measures should
     be taken to ensure that potential corruptions does not affect the
     configuration in subsequent restarts, such as re-establishing pristine
     state or ensuring that the boot procedure up to the configuration of
     mac_do can be trusted.

FreeBSD 15.1-STABLE-HBSD         May 21, 2026                        MAC_DO(4)