as possible. Then, individual user declarations can be used to
override the group settings for selected users as needed.
+MEMBERSHIP IN MULTIPLE GROUPS
+-----------------------------
+
+Besides the descibed single-membership of user to some group, you may also find
+useful if user (or host) belongs to multiple groups at once. You can naturally
+specify multiple "member = group_X" commands for such user:
+
+user = fred {
+ # fred is a member of both groups: admins_company_A, admins_company_B
+ member = admins_company_A
+ member = admins_company_B
+}
+
+group = admins_company_A {
+ # group admins_company_A is not a member of any group
+ member = admins_company_A_privilege_X
+ member = admins_company_A_privilege_Y
+}
+group = admins_company_A_privilege_X {
+}
+group = admins_company_A_privilege_Y {
+}
+
+group = admins_company_B {
+ # group admins_company_B is not a member of any group
+}
+
+Here it is important to respect the ordering of "member" commands: Any
+searching for attributes/values is done by Depth-First Search - so Daemon would
+first try to look all members of admins_company_A and THEN (after it would
+failed to find any) it would start to searching through admins_company_B. The
+searching through the proposed example would be done in the following order:
+
+ fred
+ admins_company_A
+ admins_company_A_privilege_X
+ admins_company_A_privilege_Y
+ admins_company_B
+
+Sometimes you would want to only list some members (users, hosts or groups) but
+you don't want to specify any attributes for them. You would be able to do it:
+
+group = city_X_NASes {
+}
+host first.NAS.X.city {
+ member = city_X_NASes
+}
+host second.NAS.X.city {
+ member = city_X_NASes
+}
+
+But you will probably find more comfortable to use "enlist" keyword. It has
+the same functionality but it goes from the other way:
+
+ member: current entity is connected as CHILD to the specified PARENT entity
+ enlist: specified entity is connected as CHILD to the current one as PARENT
+
+The example would be re-written using "enlist" keyword as:
+
+group = city_X_NASes {
+ enlist = host first.NAS.X.city
+ enlist = host second.NAS.X.city
+}
+
+As you can see, "enlist" doesn't require the existence of the given entity as
+it would loose its primary purpose. If the entity doesn't exist it will be
+automatically created (as empty one) - this doesn't apply to "member"! Any
+argument to "member" MUST already exist. "enlist" is provided to save you from
+writing a lot of empty definition lines like:
+
+host = first.NAS.X.city {
+}
+
+All forward references are not a problem, you can still make membership or
+enlistment on the top of the file with the entity which will be defined on the
+end of the configuration file.
+
CONFIGURING USER AUTHENTICATION
-------------------------------
}
+CONFIGURATION RESPECTING NAS HOST OF THE USER
+---------------------------------------------
+
+Sometimes you would want to modify the configuration file according to the
+source NAS where the user is being authenticated/authorized. For example if you
+are big ISP you want to permit administrator of company X to be able to monitor
+status of the links on NAS in company X but, of course, she should be able to
+monitor any other links on any other NAS of the same ISP. As all the NASes are
+authorized from the same Daemon, it seems as a problem. (You can workaround it
+by using custom authorization program - see "USING PROGRAMS TO DO
+AUTHORIZATION" section below - but it is not nice solution.)
+
+For this purposes there exists another entity 'host':
+
+ user wilma
+ group admin
+ host 198.133.219.25
+ host nas.cisco.com
+
+As you can see you may use either IP address or DNS name. Anyway, we strongly
+recommend to always use only IP addresses - DNS subsystem may fail or it may be
+forged by the enemy.
+
+You have two methods of utilizing the differences between NASes:
+
+1) Current user is always automatically enlisted (=given membership to) to its
+ current NAS host. This looks weird as only groups can have members but this
+ is the only exception to this rule, current NAS host can really have a
+ member:
+
+ ( user "current_user" | user DEFAULT )
+ |
+ +-- host "current_NAS_IP_address"
+ | |
+ | +- group DEFAULT
+ |
+ +-- host "current_NAS_hostname"
+ | |
+ | +-- group DEFAULT
+ |
+ +-- group DEFAULT
+
+ Each link only exists in the case it there exist both its peers, of course.
+ user/group DEFAULT is written here only for the completeness of the chart.
+ DEFAULT is discussed elsewhere in this documentation.
+
+ According to the shown ordering, attributes/values in the host of current
+ NAS identified by its IP address has _higher_ precence over the attributes
+ in the current NAS identified by its (reverse-resolved) hostname.
+
+ According to this auto-connections, you can for example permit some command
+ to ALL the users on such NAS:
+
+user = fred {
+ login = cleartext LLLL
+}
+host = machine.A.company {
+ cmd = write {
+ permit terminal
+ }
+}
+
+ In this configuration file ALL the valid users can do "write terminal" when
+ logged in on NAS "machine.A.company".
+
+2) Sometimes you need to do the authorization specific only to some users on
+ some NASes. For example to permit "write terminal" ONLY to user fred
+ connected to NAS "machine.A.company". That means that you want to forbid it
+ to user fred on any other NAS and yuo also want to forbid it to all the
+ other users on NAS "machine.A.company". (Line "authorization = recursive" is
+ required but read the following section "FULL RECURSIVITY" to know all its
+ consequences.)
+
+authorization = recursive
+user = fred {
+ login = cleartext LLLL
+}
+host = machine.A.company {
+ when = user fred {
+ cmd = write {
+ permit terminal
+ }
+ }
+}
+
+ This file has the same effect as:
+
+authorization = recursive
+user = fred {
+ login = cleartext LLLL
+ when = host machine.A.company {
+ cmd = write {
+ permit terminal
+ }
+ }
+}
+
+ You can see the (nested) command "when" can limit the scope of existence of
+ its contents. Definition of "host machine.A.company" with empty block (no
+ attributes) isn't needed as "when" line will automatically create hosts not
+ defined elsewhere, in the same style as "enlist" keyword creates them. Any
+ "user"s or "group"s referenced by "when" MUST be defined, such entities are
+ never created automatically!
+
+ Unfortunately you cannot use "when" to limit any items, just a few of them
+ are possible:
+
+member
+enlist
+cmd
+cmd arguments (to limit specific "permit"/"deny" lines)
+service (or incl. "protocol" specification)
+service AV pairs (to limit specific "attr=value" lines)
+when (enabling pure nesting of "when" blocks)
+
+ Full flexibility to limit any contents may be done in future (needs complete
+ cfgfile.c rewrite) but currently it is not supported. Fortunately you can
+ get the same behaviour by appropriate usage of "member" keyword - all the
+ attributes to be conditioned are put into separate group and you limit only
+ the "member" keyword to such group.
+
+ "when" command has the form "when = CONDITION { BLOCK }", CONDITION can be:
+
+user USR i.e. that current user is "USR")
+host HOSTNAME i.e. that current user is on NAS "HOSTNAME")
+group GRP i.e. current user belongs to the group "GRP",
+ All possible "when" conditions to reach such belonging
+ have to be successfuly met to make this condition successful.
+ Realize that "member" can be limited by "when" keyword.
+CONDITION and CONDITION and CONDITION ...
+CONDITION or CONDITION or CONDITION ...
+not CONDITION
+( CONDITION )
+
+ You can see that you CANNOT use for example "user A and user B or user C",
+ such condition would have ambiguous precedence of operators, you must
+ explicitly write: ( user A and user B ) or user C
+ or: user A and ( user B or user C )
+
+ Both parentheses have to be written as separate words, NOT "(user A)"
+ but "( user A )" instead.
+ (Proper solution would also need the complete cfgfile.c rewrite.)
+
+ "not" operator has the highest precendence, so: not user A or user B
+ has the same meaning as: ( not user A ) or user B
+
+ Sometimes the "when" condition is so-called unresolvable. Example:
+
+group = GRP { }
+user = USR {
+ when = group GRP {
+ member = GRP
+ }
+}
+
+ It is looping, when we would be the member of GRP, we would be really the
+ member of GRP but otherwise sould wouldn't be the member of GRP. Weird?
+ Yes, such case is considered as configuration bug, it is reported as:
+
+ Unable to resolve expression from line 243, some looping occured
+
+ Generally such unresolvable conditional expression is considered as UNKNOWN
+ and the "when" keyword is then evaluated as "false" (=skip it's block).
+ This MAY produce unwanted privilege access (if you were conditionally
+ forbidding some privileges) so always watch out all the error messages in
+ logs! (You should generally do default deny and specifically only "permit"
+ all the privileges so the unintentional unresolvable expressions shouldn't
+ hurt you in real.)
+
+FULL RECURSIVITY
+----------------
+
+We have written in the previous examples line:
+
+authorization = recursive
+
+This changes some behaviour of Daemon:
+
+1) Looping of memberships: By default any looped memberships are reported as
+ error with message: recursively defined groups: ...
+
+ After "authorization = recursive" any looping is silently accepted as it
+ would be sometimes hard to prevent it in some complex configurations.
+ Searching is done in normal Depth-First Search, but traversion is
+ backtracked one step when the entity was already visited. Simply it will
+ work 'magically', just safely ignore the fact.
+
+2) "default service = default" is supported only with:
+ authorization = recursive
+ This isn't any real change, just some formality. In fact each entity block
+ has "default service = default" in effect as default (in the case you don't
+ write any "default service =" assign).
+
+ "authorization = recursive" is enforced here due to the change of Daemon
+ behaviour: In non-recursive (old) case the Daemon assumes "deny .*" as the
+ last line of "cmd" block. This makes proper NAS-host based permissions
+ impossible. In "authorization = recursive" mode it requires appropriate
+ "permit" or "deny" line to apply - otherwise the Depth-First Search through
+ the entities will continue to find another applicable "cmd" block (toplevel
+ "default authorization" may get into effect as the last resort).
+
+ "default service" keyword takes then another meaning: In non-recursive mode
+ it will mean "if the command wasn't found". In fully-recursive mode it will
+ mean "if any cmd block didn't decide".
+
+ Please keep in mind that "service =" (with its possible "protocol =" mate)
+ has always the same behaviour, nothing is changed with "authorization =
+ recursive" enabled. Its functionality is already a bit complex (with AV
+ pairs get erased, which added, which copied, which are optional from NAS,
+ which optional from Daemon, whether to do default permit or deny etc.).
+ Fortunately we didn't found that there would be needed some extended
+ 'recursiveness' functionality of "service" for application of NAS-host based
+ authorization.
+
+3) "host" entities are not recursive at all (=its attributes aren't looked up
+ in its parent groups) without "authorization = recursive". This isn't any
+ much change as in the previous versions of Daemon "host" entity was either
+ completely unsupported or it was supported only with the only one attribute:
+
+ "key": Attribute defines different protocol key (instead of the specified
+ toplevel one) for the specified host. Its use is recommended to
+ improve overall network security (by using unique key for each NAS).
+
USING PROGRAMS TO DO AUTHORIZATION
----------------------------------