# pam_vars documentation # # $Log$ # SUMMARY: pam_vars: Parses a configuration file (which it makes sure is not world-readable) with fairly powerful conditional operators, doing little more than passing on lots of environment variables, with the intention of becoming a way for other PAM modules and PAMified applications to avoid having to read lots and lots of differently structured configuration files, and thus the systems administrators from having to learn how to set them up. pam_vars doesn't seek to prevent the application (or other modules) setting environment variables, just to remove the need to do so, in the same way that pam_unix* remove the need to parse /etc/passwd and check the password. In the interests of flexibility, though, it would probably be best if applications generally set environment variables as early as sensibly possible (so they can be used in making decisions for access denial etc), and never after calling open_session() unless unavoidable because this will deny the administrator the opportunity to fiddle. As a special exception to its normally non-interventionist stance, it will fail access attempts with deny=true at the end of parsing. (Mainly so I don't have to write a tin-pot module to detect the deny variable and fail if it's true.) Although pam_vars isn't intended to be `in competition' with any other pam modules, its flexibility may well lead to some casualties, most notably things which are just conditional denial modules. Some other modules, such as pam_tally, may never need to take advantage of it. Options: Options can appear in pam.conf as normal, but two additional mechanisms are under consideration: passed in as env-vars from the application, and as special lines in the file. These options are available for all filetypes: * file=/etc/pam.service File for config info (default=/etc/pam/service.conf or as above) * comment=# Comment character. Only unquoted `here to end of line' comments. Comment may not be space or alphanumeric, but comment= or comment=none indicates that there is no commenting in the file. * regexp=(none|simple|perl) Sets regexp and quoting type (default is simple): none None. (duh) simple * is 0 or more chars, ? is one char. perl Subset of perl REs. Provisional. * quoting=\ Character used to escape funny other characters, including itself. The quote character may not be alphanumeric or a space, but quoting= or quoting=none means no quoting. Can be used to quote the IFS, but this really isn't recommended and may well become illegal. These next options are specific to /etc/passwd type files. If any of the group is specified, fields must be defined. Unless comment has been configured explicitly, this type of file undergoes comment-sniffing (first character of both of the first two lines, if identical, becomes the comment char). A no-comment file uses comment=none regexp=none quoting=none as its defaults, a commented file uses comment=whatever regexp=simple quoting=\, but these can be overridden. The first line whose keyfield matches class has its fields split-out and assigned to the environment variables mentioned in fields. * fields=FIELD1:FIELD2:FIELD3::FIELD5 Environment variable targets for splitting /etc/passwd type files NB, : is ifs. * ifs=: Single-char IFS to use for /etc/passwd type files. Default is : * class=(user|tty|time|...) Data to use for scanning config file. Default is user * keyfield=FIELD1 Field to match class with in /etc/passwd files. Default is first named fields field. Config file: Comment char is always written in docs as #, quote char as \. Lines matching /\\$/ are continue-lines. Lines matching /^\s*#/ are comments; whitespace-only lines are ignored. If a file isn't an /etc/passwd style file (determined from the lack of a `fields' option in pam.conf), it may be a pam_env style file consisting of multiple lines like these: ENVVAR DEFAULT=EXPRESSION [OVERRIDE=EXPRESSION] The ENVVAR is set to the value of DEFAULT, unless OVERRIDE is non-null, when it's set to OVERRIDE instead. If a file is neither of the two above (pam_env files don't have a first non-comment line starting with `:' which would be sufficient by itself, but..) it starts with a file-structure revision line on its own: PVF Rev 1.00 Followed by multiple GROUPs, where a GROUP is defined thusly: GROUP is: GUARD:QUALIFIEDGROUPNAME:KEY (GROUP|GUARD SETTING)* ::GROUPNAME (The KEY must start on the same line as its introductory :, and may not start with an unescaped :. If a : or whitespace-newline is seen, the default KEY is used.) QUALIFIEDGROUPNAME is: GROUPNAME(QUALIFIER)* (Trailing whitespace is ignored) QUALIFIER is: /IDENTIFIER IDENTIFIER is: [a-zA-Z][a-zA-Z0-9_]+ GROUPNAME is: IDENTIFIER (note that GROUPNAMEs starting with _ are all reserved) KEY is: an EXPRESSION GUARD is: a comma-separated list of REXPRESSIONS SETTING is: a whitespace-separated list of ASSIGNMENTS A GROUP is a way to make ASSIGNMENTS to environment and PAM variables, based on some particular piece of information (the KEY) for various values of that information (GUARDS). At the start of the GROUP, the KEY (EXPRESSION) ending the GROUP declaration is evaluated, and its value is stored. Each GUARD is then checked against the KEY in turn, and if the KEY matches the GUARD, then the SETTING (ASSIGNMENTS) which follow that GUARD are performed. If KEY is blank, it defaults to the value of the KEY of the parent GROUP. The GUARD of a base GROUP (one whose parent is the file itself) must be blank. Its KEY, if blank, will default to @{USER}. The first GUARD in any GROUP may be blank (whitespace followed by a colon), making that SETTING the default SETTING for that GROUP: the first time any one REXPRESSION of a GROUP's SETTING's GUARD matches, the default SETTING for that GROUP (and those of the GROUP's ancestors, outermost GROUP first) are executed before that of the GUARD. The GUARD of a subgroup (a GROUP whose parent is a GROUP too) may be blank if the subgroup is acting as the default for the parent or as a grouper. The values a subgroup acting as default provides will depend on one of its own GUARDS matching: if none of its SETTINGS' GUARDS match, it will set no defaults. In this case, you will probably want to make a fake default GUARD of *, which matches anything. If a GROUP's KEY evaluates to the null string, it will still be sent through the GROUP and compared against GUARDS, but will only match an explicit "" null-string GUARD or the * anything GUARD. QUALIFIERS are modifiers for GROUP: /nomatch is tentatively mentioned as a way to set complex defaults without actually causing a GUARD match (although complex defaults may indicate you're doing it wrong). /define is a way to declare a group without actually mentioning it. A /defined group's expression sets the default expression. A SETTING is separated from another SETTING by a GUARD. GUARDS contain no unquoted equals signs or colons. If any one of the REXPRESSIONS in a GUARD matches, the SETTING is executed, but the remaining REXPRESSIONS (if any) are not examined further. Although the definition doesn't enforce it, it's a good idea to start each list of GUARDS on a new line, and to put each ASSIGNMENT on a new line too (indented from its GUARD), unless several ASSIGNMENTS are closely related. ASSIGNMENTS look like this: VAR = EXPRESSION is a simple assignment VAR .= EXPRESSION is a simple concatenation assignment VAR |= EXPRESSION is a set-addition assignment (appends to end) VAR &= REXPRESSION is a set-removal assignment VAR ~= REXPRESSION is a complement-set-removal assignment `set' operations work on IFS-separated lists. VAR is just an IDENTIFIER. An all-uppercase IDENTIFIER is assumed to be an environment variable; lower-case makes it a PAM variable. For the moment, I'm putting pam-items in as read-only values, with the same identifiers as they're addressed in pam.h. This makes their names PAM_USER PAM_TTY PAM_RHOST PAM_CONV PAM_RUSER, PAM_USER_PROMPT, PAM_AUTHTOK and PAM_OLDAUTHTOK. All PAM_ names are reserved, unless I put them into Pam_ instead, later. :) Notice that using PAM_USER or PAM_AUTHTOK may cause interrogation of the application. There is no crypt() support in the module (yet). EXPRESSION is currently just a string-replacement operation with operators akin to the Bash variable operators, but works with both ${} environment variables and @{} PAM variables: ${VAR} is the plain expansion. ${VAR:-default string if VAR is null} ${VAR:+replace non-null string} ${#VAR} is the length of VAR in characters ${##VAR} is the length of VAR in fields (using IFS) ${VAR#word} is the value of VAR, with the shortest ${VAR##word} longest matching value of word stripped from the start ${VAR%word} is the value of VAR, with the shortest ${VAR%%word} longest matching value of word stripped from the end ${?message to syslog} ${??message to user if not quiet, always to syslog} ${!as ? above, but terminate immediately, returning @{deny}} ${!!as ?? above, but terminate immediately, returning @{deny}} $[] is a special, to do arithmetic. Once EXPRESSION has been expanded, and if the context is appropriate, the wildcard matching is invoked. This occurs in GUARDS and in the set operations above. If an expression is to contain spaces, colons, comment characters, or equals signs, it must be surrounded by unescaped "" double quotes. Literal double quotes, \escapes, $ signs, and @ signs must be \escaped, whether in double quotes, or not. REXPRESSIONS are regular expressions, currently only basic REs are available: * matches any number of characters ? matches only one character ! negates the expressions, but binds more strongly than , All GROUP names starting with the _ underbar character are reserved, most notably, these: GUARD:_INCLUDE:INCLUDEFILE Includes the file if GUARD succeeds, and the file is not world- writeable. If INCLUDEFILE is a string containing spaces, the first block of non-spaces is the filename, but the rest of the string may specify options almost exactly like pam.conf, allowing easy inclusion of /etc/passwd type files: :_INCLUDE:"/etc/passwd fields=USER::UID:GID:GECOS:HOME:SHELL" ::_INCLUDE _INCLUDE's body can contain a DEFAULT setting for the file. _INCLUDE's body can contain an ERROR setting for the file. The `almosts' are these extra options: - class may be made equal to any expression (in which spaces must be escaped) in addition to the constants mentioned in the options section These + extras will either be options as above, or /qualifiers. + fromid is a `fail-weak', and so it must be specified if the UID and GID are NOT to be made effective before trying to parse the file (ie if the authenticating user's permissions are to be retained). Lack of `fromid' on an _INCLUDE suppresses use of `authid' in that file and any sub-_INCLUDE files, and requires userset to be specified. Pam-vars and pam-items may only be used in `fromid' files. + nest checks the depth of the file, and raises an error if the include depth would exceed the given value. The value defaults to 8 (or the inherited value of nest), and is checked, even if nest is not given. + modeokay sidesteps the `not world readable' check. + userset takes a list of env-vars which may be set by that and sub-_INCLUDE files. It is mandatory if `fromid' is missing, or if `modeokay' is specified, since either is dangerous. Variables PATH, UID, GID, SHELL, IFS, and HOME are tentatively illegal in this list. GUARD:_LOOP/maxit=50:WHILE Enters the loop (ended with a trailing ::_LOOP) if GUARD succeeds, iterates once, and repeats the _LOOP if WHILE evaluates true. /maxit defaults to 50 as shown, and is the maximum number of iterations, to prevent infinite loops. (If reached, it complains to syslog; maxit may turn out to be reduced by outer loops' iteration counts) GUARD?:_NEW:CONFIGURATIONCLASS GUARD?:_NEW/configurationclass:otherstuff This group is under consideration. It is intended to provide a way to integrate strong support for commands like mkuser, rmuser and mkwebhost into the file. Quite how it behaves has yet to be decided, but it's likely that only entries within this group will be handled by mkuser etc, and that mkuser etc will only be links to a configurable configuration program. It is very likely that any effects is has will track into _INCLUDE files (allowing it to process /etc/passwd type files, probably through a configuration wrapper), but how it reacts to _LOOP is rather less clear. _INCLUDE and _LOOP may be qualified, just like any other GROUP. Normal qualifiers are unlikely to make sense on _NEW. - Tim Baverstock v0.1 5 March 1997 BUGS: No code, yet. :) --------------------------------------------------------------------------- Example configuration file. For showing structure only. --------------------------------------------------------------------------- # /etc/ftpusers as you've never seen it before! # (This would actually be /etc/pam/ftp.conf or /etc/pam.ftp, by default) # A typical (but complex) config file for use with pam_vars. # The SHELL settings aren't used here, but the idea is that several # different programs can usefully share one config file. # # $Log$ # PVF Rev 1.00 # This star can go here or after the defaults. It doesn't really matter. # Default key is PAM_USER :USER: * deny=true SHELL=/bin/false # Remnant of earlier policy # FTP_READONLY=true # FTP_LC_FILENAMES=true # FTP_TRANS=".txt .htm" :ANON: deny=true SHELL=/bin/true USER=anonymous # Checking the password is somebody else's problem, so # uid, gid, chroot will presumably be handled by ftpd # (or possibly pam_vars could do it during setcred) UID=anonymous GID=anonymous FTP_CHROOT=/home/ftp FTP_READONLY=false # Vanilla anon users (could put ,s after first two, but no need) anonymous anon ftp # But we're letting guest in as read-only anon ftp at any time-of-day guest deny=false FTP_READONLY=true pam_time=*;*;*;Al0000-2400 ::ANON # Most folk get to use it between 0600-1800 (enforced by a modified pam_time # which presumably notices the pam_time variable, although it should # really accept it in a split-up form, because that would be much more # readable) # But ftp presumably handles umask, map, and dostrans. :MOSTFOLK: deny=false pam_time=*;*;*;!Al0600-1800 UMASK=022 FTP_MAP=".htm.html" FTP_DOSTRANS=".txt .htm" # This guy is instead barred between 0800h and 2000h, and gets extra stuff # but can only look around in his $HOME or in .../docs/hacker hacker deny=false pam_time=*;*;*;!Al0800-2000 UMASK=002 FTP_LC_FILENAMES=true FTP_MAP+=".tif.tiff" FTP_RESTRICTION="/home/http/uk-/co-/some-/www-/docs/hacker $HOME" # This guy is just MOSTFOLK joeuser ::MOSTFOLK :SAMBAUSERS: deny=false SHELL=/etc/passwd dave john russell ::SAMBAUSERS # Give root ftp access to these users and never keep them out. :) # Because deny isn't true or false, it's passed on as a pam variable for # something else to mess about with. Again, ftp handles uid, as with anon. :NICEFOLK: UID=0 GID=0 deny=retinalscan|phonetap mi5 mi6 nsa fbi cia ::NICEFOLK ::USER